From fcbdc4d3a54f2c110f5e7433d620f331ef0e356a Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 11 Feb 2016 11:41:25 -0500 Subject: [PATCH 1/2] Remove the dev-py3k docs We only have one set of dev docs (preferably built with Python 3, but it shouldn't even matter, since we don't use 2to3 anymore). The dev-py3k docs here were not at all up-to-date. --- 0.6.7/index.html | 1 - 0.7.0/index.html | 1 - 0.7.1/index.html | 1 - 0.7.2-py3k/index.html | 1 - 0.7.2/index.html | 1 - 0.7.3/index.html | 1 - 0.7.4.1/index.html | 1 - 0.7.4/index.html | 1 - 0.7.5/index.html | 1 - 0.7.6/index.html | 1 - dev-py3k/.buildinfo | 4 - dev-py3k/_images/ai.png | Bin 26596 -> 0 bytes dev-py3k/_images/ai_c.png | Bin 61953 -> 0 bytes dev-py3k/_images/ber.png | Bin 19451 -> 0 bytes dev-py3k/_images/besseli.png | Bin 19101 -> 0 bytes dev-py3k/_images/besseli_c.png | Bin 35095 -> 0 bytes dev-py3k/_images/besselj.png | Bin 23549 -> 0 bytes dev-py3k/_images/besselj_c.png | Bin 37422 -> 0 bytes dev-py3k/_images/besselk.png | Bin 15252 -> 0 bytes dev-py3k/_images/besselk_c.png | Bin 30483 -> 0 bytes dev-py3k/_images/bessely.png | Bin 19658 -> 0 bytes dev-py3k/_images/bessely_c.png | Bin 40491 -> 0 bytes dev-py3k/_images/bi.png | Bin 25211 -> 0 bytes dev-py3k/_images/bi_c.png | Bin 67342 -> 0 bytes dev-py3k/_images/chebyt.png | Bin 33025 -> 0 bytes dev-py3k/_images/chebyu.png | Bin 22495 -> 0 bytes dev-py3k/_images/coulombf.png | Bin 34810 -> 0 bytes dev-py3k/_images/coulombf_c.png | Bin 39897 -> 0 bytes dev-py3k/_images/coulombg.png | Bin 34197 -> 0 bytes dev-py3k/_images/coulombg_c.png | Bin 46092 -> 0 bytes dev-py3k/_images/cplot.png | Bin 42543 -> 0 bytes dev-py3k/_images/ellipe.png | Bin 24597 -> 0 bytes dev-py3k/_images/ellipf.png | Bin 22313 -> 0 bytes dev-py3k/_images/ellipk.png | Bin 12183 -> 0 bytes dev-py3k/_images/ellippi.png | Bin 21917 -> 0 bytes dev-py3k/_images/ex_rd.svg | 237 - dev-py3k/_images/featured-downloads.png | Bin 4470 -> 0 bytes dev-py3k/_images/gi.png | Bin 18186 -> 0 bytes dev-py3k/_images/gi_c.png | Bin 57729 -> 0 bytes dev-py3k/_images/hankel1.png | Bin 26786 -> 0 bytes dev-py3k/_images/hankel1_c.png | Bin 36130 -> 0 bytes dev-py3k/_images/hankel2.png | Bin 27100 -> 0 bytes dev-py3k/_images/hankel2_c.png | Bin 35840 -> 0 bytes dev-py3k/_images/hermite.png | Bin 25846 -> 0 bytes dev-py3k/_images/hi.png | Bin 10748 -> 0 bytes dev-py3k/_images/hi_c.png | Bin 47358 -> 0 bytes dev-py3k/_images/ker.png | Bin 13848 -> 0 bytes dev-py3k/_images/kin_1.svg | 211 - dev-py3k/_images/kin_1pt.svg | 216 - dev-py3k/_images/kin_2.svg | 317 - dev-py3k/_images/kin_2pt.svg | 198 - dev-py3k/_images/kin_3.svg | 291 - dev-py3k/_images/kin_4.svg | 413 - dev-py3k/_images/kin_angvel1.svg | 238 - dev-py3k/_images/kin_angvel2.svg | 541 - dev-py3k/_images/kin_angvel3.svg | 319 - dev-py3k/_images/kin_rolling.svg | 261 - dev-py3k/_images/kleinj.png | Bin 77369 -> 0 bytes dev-py3k/_images/kleinj2.png | Bin 61723 -> 0 bytes dev-py3k/_images/laguerre.png | Bin 19345 -> 0 bytes dev-py3k/_images/lambertw.png | Bin 15860 -> 0 bytes dev-py3k/_images/lambertw_c.png | Bin 28683 -> 0 bytes dev-py3k/_images/legendre.png | Bin 28034 -> 0 bytes dev-py3k/_images/lommels1.png | Bin 26654 -> 0 bytes dev-py3k/_images/lommels2.png | Bin 17657 -> 0 bytes dev-py3k/_images/pcfd.png | Bin 28296 -> 0 bytes dev-py3k/_images/plot.png | Bin 20614 -> 0 bytes dev-py3k/_images/pngview1.png | Bin 3251 -> 0 bytes dev-py3k/_images/simp_rot.svg | 255 - dev-py3k/_images/spherharm40.png | Bin 23638 -> 0 bytes dev-py3k/_images/spherharm41.png | Bin 37882 -> 0 bytes dev-py3k/_images/spherharm42.png | Bin 51314 -> 0 bytes dev-py3k/_images/spherharm43.png | Bin 48141 -> 0 bytes dev-py3k/_images/spherharm44.png | Bin 41468 -> 0 bytes dev-py3k/_images/splot.png | Bin 23750 -> 0 bytes dev-py3k/_images/vec_add.svg | 195 - dev-py3k/_images/vec_cross.svg | 240 - dev-py3k/_images/vec_dot.svg | 258 - dev-py3k/_images/vec_fix_notfix.svg | 385 - dev-py3k/_images/vec_mul.svg | 132 - dev-py3k/_images/vec_rep.svg | 140 - dev-py3k/_images/vec_simp_der.svg | 353 - dev-py3k/_images/winpdb1.png | Bin 66153 -> 0 bytes dev-py3k/_images/winpdb2.png | Bin 93795 -> 0 bytes dev-py3k/_modules/index.html | 331 - dev-py3k/_modules/mpmath.html | 560 - .../mpmath/calculus/optimization.html | 1206 -- .../_modules/mpmath/calculus/quadrature.html | 1126 -- dev-py3k/_modules/sympy.html | 196 - dev-py3k/_modules/sympy/assumptions/ask.html | 420 - .../_modules/sympy/assumptions/assume.html | 294 - .../sympy/assumptions/handlers/calculus.html | 420 - .../sympy/assumptions/handlers/ntheory.html | 355 - .../sympy/assumptions/handlers/order.html | 316 - .../sympy/assumptions/handlers/sets.html | 755 - .../_modules/sympy/assumptions/refine.html | 283 - dev-py3k/_modules/sympy/categories.html | 142 - .../sympy/categories/diagram_drawing.html | 2699 --- dev-py3k/_modules/sympy/combinatorics.html | 130 - .../sympy/combinatorics/graycode.html | 531 - .../sympy/combinatorics/group_constructs.html | 176 - .../sympy/combinatorics/named_groups.html | 398 - .../sympy/combinatorics/partitions.html | 814 - .../sympy/combinatorics/perm_groups.html | 3535 ---- .../sympy/combinatorics/permutations.html | 2885 --- .../sympy/combinatorics/polyhedron.html | 936 - .../_modules/sympy/combinatorics/prufer.html | 551 - .../_modules/sympy/combinatorics/subsets.html | 637 - .../sympy/combinatorics/tensor_can.html | 1258 -- .../sympy/combinatorics/testutil.html | 449 - .../_modules/sympy/combinatorics/util.html | 639 - dev-py3k/_modules/sympy/concrete/gosper.html | 329 - .../_modules/sympy/concrete/products.html | 376 - .../_modules/sympy/concrete/summations.html | 705 - dev-py3k/_modules/sympy/core/add.html | 964 - dev-py3k/_modules/sympy/core/basic.html | 1807 -- dev-py3k/_modules/sympy/core/cache.html | 257 - .../_modules/sympy/core/compatibility.html | 863 - dev-py3k/_modules/sympy/core/containers.html | 332 - dev-py3k/_modules/sympy/core/evalf.html | 1456 -- dev-py3k/_modules/sympy/core/expr.html | 3079 ---- dev-py3k/_modules/sympy/core/exprtools.html | 1002 -- dev-py3k/_modules/sympy/core/function.html | 2328 --- dev-py3k/_modules/sympy/core/mod.html | 157 - dev-py3k/_modules/sympy/core/mul.html | 1622 -- .../_modules/sympy/core/multidimensional.html | 247 - dev-py3k/_modules/sympy/core/numbers.html | 2820 --- dev-py3k/_modules/sympy/core/power.html | 1204 -- dev-py3k/_modules/sympy/core/relational.html | 708 - dev-py3k/_modules/sympy/core/sets.html | 1400 -- dev-py3k/_modules/sympy/core/singleton.html | 195 - dev-py3k/_modules/sympy/core/symbol.html | 594 - dev-py3k/_modules/sympy/core/sympify.html | 395 - dev-py3k/_modules/sympy/diffgeom.html | 126 - .../functions/combinatorial/factorials.html | 667 - .../functions/combinatorial/numbers.html | 786 - .../sympy/functions/elementary/complexes.html | 957 - .../functions/elementary/exponential.html | 866 - .../functions/elementary/hyperbolic.html | 992 -- .../sympy/functions/elementary/integers.html | 298 - .../functions/elementary/miscellaneous.html | 624 - .../sympy/functions/elementary/piecewise.html | 645 - .../functions/elementary/trigonometric.html | 1788 -- .../sympy/functions/special/bessel.html | 696 - .../sympy/functions/special/bsplines.html | 267 - .../functions/special/delta_functions.html | 347 - .../functions/special/error_functions.html | 1297 -- .../functions/special/gamma_functions.html | 710 - .../sympy/functions/special/hyper.html | 1132 -- .../sympy/functions/special/polynomials.html | 1277 -- .../special/spherical_harmonics.html | 188 - .../functions/special/tensor_functions.html | 547 - .../functions/special/zeta_functions.html | 635 - dev-py3k/_modules/sympy/galgebra/GA.html | 2695 --- .../_modules/sympy/galgebra/latex_ex.html | 1410 -- dev-py3k/_modules/sympy/geometry/curve.html | 450 - dev-py3k/_modules/sympy/geometry/ellipse.html | 1441 -- dev-py3k/_modules/sympy/geometry/entity.html | 457 - dev-py3k/_modules/sympy/geometry/line.html | 1861 -- dev-py3k/_modules/sympy/geometry/point.html | 718 - dev-py3k/_modules/sympy/geometry/polygon.html | 2342 --- dev-py3k/_modules/sympy/geometry/util.html | 509 - dev-py3k/_modules/sympy/integrals.html | 141 - .../_modules/sympy/integrals/transforms.html | 1938 -- dev-py3k/_modules/sympy/logic/boolalg.html | 1073 -- dev-py3k/_modules/sympy/logic/inference.html | 335 - dev-py3k/_modules/sympy/matrices/dense.html | 1712 -- .../_modules/sympy/matrices/expressions.html | 131 - .../matrices/expressions/blockmatrix.html | 481 - .../_modules/sympy/matrices/immutable.html | 252 - .../_modules/sympy/matrices/matrices.html | 3659 ---- dev-py3k/_modules/sympy/matrices/sparse.html | 1559 -- dev-py3k/_modules/sympy/ntheory/factor_.html | 1409 -- dev-py3k/_modules/sympy/ntheory/generate.html | 740 - dev-py3k/_modules/sympy/ntheory/modular.html | 370 - .../_modules/sympy/ntheory/multinomial.html | 355 - .../_modules/sympy/ntheory/partitions_.html | 212 - .../_modules/sympy/ntheory/primetest.html | 387 - .../sympy/ntheory/residue_ntheory.html | 334 - .../_modules/sympy/parsing/mathematica.html | 172 - dev-py3k/_modules/sympy/parsing/maxima.html | 185 - .../_modules/sympy/parsing/sympy_parser.html | 675 - .../sympy/parsing/sympy_tokenize.html | 561 - dev-py3k/_modules/sympy/physics/gaussopt.html | 958 - dev-py3k/_modules/sympy/physics/hydrogen.html | 289 - dev-py3k/_modules/sympy/physics/matrices.html | 267 - .../sympy/physics/mechanics/essential.html | 2050 --- .../sympy/physics/mechanics/functions.html | 872 - .../sympy/physics/mechanics/kane.html | 964 - .../sympy/physics/mechanics/lagrange.html | 450 - .../sympy/physics/mechanics/particle.html | 339 - .../sympy/physics/mechanics/point.html | 560 - .../sympy/physics/mechanics/rigidbody.html | 398 - .../_modules/sympy/physics/paulialgebra.html | 279 - dev-py3k/_modules/sympy/physics/qho_1d.html | 183 - .../sympy/physics/quantum/anticommutator.html | 260 - .../sympy/physics/quantum/cartesian.html | 454 - .../_modules/sympy/physics/quantum/cg.html | 836 - .../sympy/physics/quantum/commutator.html | 327 - .../sympy/physics/quantum/dagger.html | 202 - .../_modules/sympy/physics/quantum/gate.html | 1365 -- .../sympy/physics/quantum/grover.html | 434 - .../sympy/physics/quantum/hilbert.html | 769 - .../sympy/physics/quantum/innerproduct.html | 254 - .../sympy/physics/quantum/operator.html | 637 - .../sympy/physics/quantum/operatorset.html | 396 - .../_modules/sympy/physics/quantum/piab.html | 184 - .../sympy/physics/quantum/qapply.html | 298 - .../_modules/sympy/physics/quantum/qft.html | 326 - .../_modules/sympy/physics/quantum/qubit.html | 881 - .../sympy/physics/quantum/represent.html | 669 - .../_modules/sympy/physics/quantum/shor.html | 320 - .../_modules/sympy/physics/quantum/spin.html | 2192 --- .../_modules/sympy/physics/quantum/state.html | 1067 -- .../sympy/physics/quantum/tensorproduct.html | 444 - .../_modules/sympy/physics/secondquant.html | 3179 ---- dev-py3k/_modules/sympy/physics/sho.html | 205 - dev-py3k/_modules/sympy/physics/units.html | 424 - dev-py3k/_modules/sympy/physics/wigner.html | 808 - dev-py3k/_modules/sympy/plotting/plot.html | 1781 -- .../sympy/plotting/plot_implicit.html | 470 - .../sympy/polys/agca/homomorphisms.html | 735 - .../_modules/sympy/polys/agca/ideals.html | 479 - .../_modules/sympy/polys/agca/modules.html | 1493 -- .../_modules/sympy/polys/constructor.html | 358 - dev-py3k/_modules/sympy/polys/densearith.html | 2139 --- dev-py3k/_modules/sympy/polys/densebasic.html | 2050 --- dev-py3k/_modules/sympy/polys/densetools.html | 1470 -- .../sympy/polys/distributedmodules.html | 825 - .../sympy/polys/distributedpolys.html | 682 - dev-py3k/_modules/sympy/polys/domains.html | 207 - .../_modules/sympy/polys/euclidtools.html | 2087 --- .../_modules/sympy/polys/factortools.html | 1470 -- .../_modules/sympy/polys/galoistools.html | 2406 --- .../_modules/sympy/polys/groebnertools.html | 1092 -- .../_modules/sympy/polys/monomialtools.html | 753 - .../_modules/sympy/polys/numberfields.html | 672 - dev-py3k/_modules/sympy/polys/orthopolys.html | 441 - dev-py3k/_modules/sympy/polys/partfrac.html | 290 - .../_modules/sympy/polys/polyclasses.html | 1822 -- dev-py3k/_modules/sympy/polys/polyerrors.html | 261 - dev-py3k/_modules/sympy/polys/polyfuncs.html | 420 - dev-py3k/_modules/sympy/polys/polyroots.html | 832 - dev-py3k/_modules/sympy/polys/polytools.html | 6296 ------- .../_modules/sympy/polys/rationaltools.html | 198 - .../_modules/sympy/polys/rootoftools.html | 742 - .../_modules/sympy/polys/specialpolys.html | 479 - dev-py3k/_modules/sympy/printing/ccode.html | 386 - .../_modules/sympy/printing/codeprinter.html | 290 - .../_modules/sympy/printing/conventions.html | 177 - dev-py3k/_modules/sympy/printing/fcode.html | 554 - dev-py3k/_modules/sympy/printing/gtk.html | 134 - .../_modules/sympy/printing/lambdarepr.html | 196 - dev-py3k/_modules/sympy/printing/latex.html | 1675 -- dev-py3k/_modules/sympy/printing/mathml.html | 608 - .../_modules/sympy/printing/precedence.html | 209 - .../sympy/printing/pretty/pretty.html | 1802 -- .../printing/pretty/pretty_symbology.html | 628 - .../sympy/printing/pretty/stringpict.html | 612 - dev-py3k/_modules/sympy/printing/preview.html | 331 - dev-py3k/_modules/sympy/printing/printer.html | 386 - dev-py3k/_modules/sympy/printing/repr.html | 266 - dev-py3k/_modules/sympy/printing/str.html | 795 - dev-py3k/_modules/sympy/printing/tree.html | 214 - .../_modules/sympy/series/acceleration.html | 213 - dev-py3k/_modules/sympy/series/gruntz.html | 832 - dev-py3k/_modules/sympy/series/limits.html | 450 - dev-py3k/_modules/sympy/series/order.html | 383 - dev-py3k/_modules/sympy/series/residues.html | 195 - dev-py3k/_modules/sympy/series/series.html | 127 - dev-py3k/_modules/sympy/sets/fancysets.html | 405 - .../_modules/sympy/simplify/cse_main.html | 525 - .../_modules/sympy/simplify/epathtools.html | 470 - .../_modules/sympy/simplify/hyperexpand.html | 2663 --- .../_modules/sympy/simplify/simplify.html | 3561 ---- .../_modules/sympy/simplify/sqrtdenest.html | 725 - .../sympy/simplify/traversaltools.html | 156 - dev-py3k/_modules/sympy/solvers/ode.html | 3409 ---- dev-py3k/_modules/sympy/solvers/pde.html | 327 - dev-py3k/_modules/sympy/solvers/polysys.html | 434 - dev-py3k/_modules/sympy/solvers/recurr.html | 904 - dev-py3k/_modules/sympy/solvers/solvers.html | 2572 --- .../sympy/statistics/distributions.html | 629 - dev-py3k/_modules/sympy/stats.html | 181 - dev-py3k/_modules/sympy/stats/crv.html | 437 - dev-py3k/_modules/sympy/stats/crv_types.html | 2601 --- dev-py3k/_modules/sympy/stats/frv.html | 408 - dev-py3k/_modules/sympy/stats/frv_types.html | 441 - dev-py3k/_modules/sympy/stats/rv.html | 1012 -- .../_modules/sympy/tensor/index_methods.html | 558 - dev-py3k/_modules/sympy/tensor/indexed.html | 696 - .../_modules/sympy/utilities/autowrap.html | 598 - .../_modules/sympy/utilities/codegen.html | 1107 -- .../_modules/sympy/utilities/decorator.html | 201 - .../_modules/sympy/utilities/iterables.html | 2149 --- .../_modules/sympy/utilities/lambdify.html | 555 - .../_modules/sympy/utilities/memoization.html | 171 - dev-py3k/_modules/sympy/utilities/misc.html | 232 - .../_modules/sympy/utilities/pkgdata.html | 173 - dev-py3k/_modules/sympy/utilities/pytest.html | 284 - .../_modules/sympy/utilities/randtest.html | 308 - .../_modules/sympy/utilities/runtests.html | 1847 -- dev-py3k/_modules/sympy/utilities/source.html | 167 - .../_modules/sympy/utilities/timeutils.html | 191 - dev-py3k/_sources/aboutus.txt | 292 - dev-py3k/_sources/gotchas.txt | 783 - dev-py3k/_sources/guide.txt | 538 - dev-py3k/_sources/index.txt | 34 - dev-py3k/_sources/install.txt | 113 - dev-py3k/_sources/modules/assumptions/ask.txt | 6 - .../_sources/modules/assumptions/assume.txt | 6 - .../modules/assumptions/handlers/calculus.txt | 6 - .../modules/assumptions/handlers/index.txt | 16 - .../modules/assumptions/handlers/ntheory.txt | 6 - .../modules/assumptions/handlers/order.txt | 6 - .../modules/assumptions/handlers/sets.txt | 6 - .../_sources/modules/assumptions/index.txt | 402 - .../_sources/modules/assumptions/refine.txt | 6 - dev-py3k/_sources/modules/categories.txt | 71 - .../modules/combinatorics/graycode.txt | 19 - .../combinatorics/group_constructs.txt | 8 - .../_sources/modules/combinatorics/index.txt | 24 - .../modules/combinatorics/named_groups.txt | 16 - .../modules/combinatorics/partitions.txt | 22 - .../modules/combinatorics/perm_groups.txt | 9 - .../modules/combinatorics/permutations.txt | 27 - .../modules/combinatorics/polyhedron.txt | 9 - .../_sources/modules/combinatorics/prufer.txt | 9 - .../modules/combinatorics/subsets.txt | 11 - .../modules/combinatorics/tensor_can.txt | 14 - .../modules/combinatorics/testutil.txt | 16 - .../_sources/modules/combinatorics/util.txt | 22 - dev-py3k/_sources/modules/concrete.txt | 100 - dev-py3k/_sources/modules/core.txt | 463 - dev-py3k/_sources/modules/diffgeom.txt | 33 - dev-py3k/_sources/modules/evalf.txt | 424 - .../modules/functions/combinatorial.txt | 88 - .../_sources/modules/functions/elementary.txt | 326 - dev-py3k/_sources/modules/functions/index.txt | 21 - .../_sources/modules/functions/special.txt | 176 - dev-py3k/_sources/modules/galgebra/GA.txt | 6 - .../_sources/modules/galgebra/GA/GAsympy.txt | 1729 -- dev-py3k/_sources/modules/galgebra/index.txt | 15 - .../_sources/modules/galgebra/latex_ex.txt | 6 - .../modules/galgebra/latex_ex/latex_ex.txt | 417 - dev-py3k/_sources/modules/geometry.txt | 268 - dev-py3k/_sources/modules/index.txt | 59 - .../modules/integrals/g-functions.txt | 508 - .../_sources/modules/integrals/integrals.txt | 117 - dev-py3k/_sources/modules/logic.txt | 92 - dev-py3k/_sources/modules/matrices/dense.txt | 15 - .../_sources/modules/matrices/expressions.txt | 62 - .../modules/matrices/immutablematrices.txt | 41 - dev-py3k/_sources/modules/matrices/index.txt | 18 - .../_sources/modules/matrices/matrices.txt | 570 - dev-py3k/_sources/modules/matrices/sparse.txt | 16 - dev-py3k/_sources/modules/mpmath/basics.txt | 227 - .../modules/mpmath/calculus/approximation.txt | 23 - .../mpmath/calculus/differentiation.txt | 19 - .../modules/mpmath/calculus/index.txt | 13 - .../modules/mpmath/calculus/integration.txt | 31 - .../_sources/modules/mpmath/calculus/odes.txt | 7 - .../modules/mpmath/calculus/optimization.txt | 29 - .../modules/mpmath/calculus/polynomials.txt | 15 - .../modules/mpmath/calculus/sums_limits.txt | 58 - dev-py3k/_sources/modules/mpmath/contexts.txt | 306 - .../modules/mpmath/functions/bessel.txt | 185 - .../modules/mpmath/functions/constants.txt | 85 - .../modules/mpmath/functions/elliptic.txt | 96 - .../modules/mpmath/functions/expintegrals.txt | 104 - .../modules/mpmath/functions/gamma.txt | 112 - .../modules/mpmath/functions/hyperbolic.txt | 56 - .../mpmath/functions/hypergeometric.txt | 115 - .../modules/mpmath/functions/index.txt | 21 - .../modules/mpmath/functions/numtheory.txt | 80 - .../modules/mpmath/functions/orthogonal.txt | 79 - .../modules/mpmath/functions/powers.txt | 85 - .../modules/mpmath/functions/qfunctions.txt | 29 - .../mpmath/functions/trigonometric.txt | 117 - .../modules/mpmath/functions/zeta.txt | 104 - dev-py3k/_sources/modules/mpmath/general.txt | 228 - .../modules/mpmath/identification.txt | 31 - dev-py3k/_sources/modules/mpmath/index.txt | 53 - dev-py3k/_sources/modules/mpmath/matrices.txt | 375 - dev-py3k/_sources/modules/mpmath/plotting.txt | 32 - .../_sources/modules/mpmath/references.txt | 50 - dev-py3k/_sources/modules/mpmath/setup.txt | 178 - .../_sources/modules/mpmath/technical.txt | 159 - dev-py3k/_sources/modules/ntheory.txt | 98 - dev-py3k/_sources/modules/parsing.txt | 32 - .../_sources/modules/physics/gaussopt.txt | 6 - .../_sources/modules/physics/hydrogen.txt | 6 - dev-py3k/_sources/modules/physics/index.txt | 25 - .../_sources/modules/physics/matrices.txt | 6 - .../modules/physics/mechanics/advanced.txt | 206 - .../physics/mechanics/api/essential.txt | 23 - .../physics/mechanics/api/functions.txt | 32 - .../modules/physics/mechanics/api/kane.txt | 22 - .../physics/mechanics/api/kinematics.txt | 16 - .../physics/mechanics/api/part_bod.txt | 55 - .../physics/mechanics/api/printing.txt | 26 - .../modules/physics/mechanics/examples.txt | 577 - .../modules/physics/mechanics/index.txt | 50 - .../modules/physics/mechanics/kane.txt | 302 - .../modules/physics/mechanics/kinematics.txt | 572 - .../modules/physics/mechanics/masses.txt | 395 - .../modules/physics/mechanics/reference.txt | 18 - .../modules/physics/mechanics/vectors.txt | 767 - .../_sources/modules/physics/paulialgebra.txt | 6 - dev-py3k/_sources/modules/physics/qho_1d.txt | 6 - .../physics/quantum/anticommutator.txt | 6 - .../modules/physics/quantum/cartesian.txt | 6 - .../_sources/modules/physics/quantum/cg.txt | 6 - .../modules/physics/quantum/circuitplot.txt | 6 - .../modules/physics/quantum/commutator.txt | 6 - .../modules/physics/quantum/constants.txt | 6 - .../modules/physics/quantum/dagger.txt | 6 - .../_sources/modules/physics/quantum/gate.txt | 6 - .../modules/physics/quantum/grover.txt | 6 - .../modules/physics/quantum/hilbert.txt | 6 - .../modules/physics/quantum/index.txt | 58 - .../modules/physics/quantum/innerproduct.txt | 6 - .../modules/physics/quantum/operator.txt | 6 - .../modules/physics/quantum/operatorset.txt | 6 - .../_sources/modules/physics/quantum/piab.txt | 6 - .../modules/physics/quantum/qapply.txt | 6 - .../_sources/modules/physics/quantum/qft.txt | 6 - .../modules/physics/quantum/qubit.txt | 6 - .../modules/physics/quantum/represent.txt | 6 - .../_sources/modules/physics/quantum/shor.txt | 6 - .../_sources/modules/physics/quantum/spin.txt | 6 - .../modules/physics/quantum/state.txt | 6 - .../modules/physics/quantum/tensorproduct.txt | 6 - .../_sources/modules/physics/secondquant.txt | 6 - dev-py3k/_sources/modules/physics/sho.txt | 6 - dev-py3k/_sources/modules/physics/units.txt | 69 - dev-py3k/_sources/modules/physics/wigner.txt | 6 - dev-py3k/_sources/modules/plotting.txt | 287 - dev-py3k/_sources/modules/polys/agca.txt | 308 - dev-py3k/_sources/modules/polys/basics.txt | 226 - dev-py3k/_sources/modules/polys/index.txt | 28 - dev-py3k/_sources/modules/polys/internals.txt | 551 - .../_sources/modules/polys/literature.txt | 79 - dev-py3k/_sources/modules/polys/reference.txt | 171 - dev-py3k/_sources/modules/polys/wester.txt | 451 - dev-py3k/_sources/modules/printing.txt | 444 - dev-py3k/_sources/modules/rewriting.txt | 92 - dev-py3k/_sources/modules/series.txt | 188 - dev-py3k/_sources/modules/sets.txt | 71 - .../_sources/modules/simplify/hyperexpand.txt | 634 - .../_sources/modules/simplify/simplify.txt | 129 - dev-py3k/_sources/modules/solvers/ode.txt | 107 - dev-py3k/_sources/modules/solvers/solvers.txt | 71 - dev-py3k/_sources/modules/statistics.txt | 136 - dev-py3k/_sources/modules/stats.txt | 170 - dev-py3k/_sources/modules/tensor/index.txt | 16 - .../_sources/modules/tensor/index_methods.txt | 6 - dev-py3k/_sources/modules/tensor/indexed.txt | 6 - .../_sources/modules/utilities/autowrap.txt | 53 - .../_sources/modules/utilities/codegen.txt | 50 - .../modules/utilities/cythonutils.txt | 6 - .../_sources/modules/utilities/decorator.txt | 6 - dev-py3k/_sources/modules/utilities/index.txt | 29 - .../_sources/modules/utilities/iterables.txt | 91 - .../_sources/modules/utilities/lambdify.txt | 6 - .../modules/utilities/memoization.txt | 6 - dev-py3k/_sources/modules/utilities/misc.txt | 6 - .../_sources/modules/utilities/pkgdata.txt | 6 - .../_sources/modules/utilities/pytest.txt | 6 - .../_sources/modules/utilities/randtest.txt | 6 - .../_sources/modules/utilities/runtests.txt | 6 - .../_sources/modules/utilities/source.txt | 6 - .../_sources/modules/utilities/timeutils.txt | 6 - dev-py3k/_sources/outreach.txt | 39 - dev-py3k/_sources/python-comparisons.txt | 388 - dev-py3k/_sources/tutorial/tutorial.en.txt | 1063 -- dev-py3k/_sources/wiki.txt | 12 - dev-py3k/_static/SymPy-Favicon.ico | Bin 6230 -> 0 bytes dev-py3k/_static/ajax-loader.gif | Bin 673 -> 0 bytes dev-py3k/_static/basic.css | 540 - dev-py3k/_static/comment-bright.png | Bin 3500 -> 0 bytes dev-py3k/_static/comment-close.png | Bin 3578 -> 0 bytes dev-py3k/_static/comment.png | Bin 3445 -> 0 bytes dev-py3k/_static/default.css | 261 - dev-py3k/_static/doctools.js | 247 - dev-py3k/_static/down-pressed.png | Bin 368 -> 0 bytes dev-py3k/_static/down.png | Bin 363 -> 0 bytes dev-py3k/_static/file.png | Bin 392 -> 0 bytes dev-py3k/_static/jquery.js | 154 - dev-py3k/_static/minus.png | Bin 199 -> 0 bytes dev-py3k/_static/plus.png | Bin 199 -> 0 bytes dev-py3k/_static/pygments.css | 62 - dev-py3k/_static/searchtools.js | 560 - dev-py3k/_static/sidebar.js | 151 - dev-py3k/_static/sympylogo.png | Bin 19711 -> 0 bytes dev-py3k/_static/underscore.js | 23 - dev-py3k/_static/up-pressed.png | Bin 372 -> 0 bytes dev-py3k/_static/up.png | Bin 363 -> 0 bytes dev-py3k/_static/websupport.js | 808 - dev-py3k/aboutus.html | 449 - dev-py3k/figures/admon-note.png | Bin 1733 -> 0 bytes dev-py3k/figures/featured-downloads.png | Bin 4470 -> 0 bytes dev-py3k/genindex.html | 14734 ---------------- dev-py3k/gotchas.html | 913 - dev-py3k/guide.html | 647 - dev-py3k/index.html | 250 - dev-py3k/install.html | 241 - dev-py3k/modules/assumptions/ask.html | 232 - dev-py3k/modules/assumptions/assume.html | 234 - .../assumptions/handlers/calculus.html | 371 - .../modules/assumptions/handlers/index.html | 165 - .../modules/assumptions/handlers/ntheory.html | 169 - .../modules/assumptions/handlers/order.html | 191 - .../modules/assumptions/handlers/sets.html | 365 - dev-py3k/modules/assumptions/index.html | 607 - dev-py3k/modules/assumptions/refine.html | 220 - dev-py3k/modules/categories.html | 1215 -- dev-py3k/modules/combinatorics/graycode.html | 417 - .../combinatorics/group_constructs.html | 168 - dev-py3k/modules/combinatorics/index.html | 174 - .../modules/combinatorics/named_groups.html | 287 - .../modules/combinatorics/partitions.html | 457 - .../modules/combinatorics/perm_groups.html | 1906 -- .../modules/combinatorics/permutations.html | 1710 -- .../modules/combinatorics/polyhedron.html | 337 - dev-py3k/modules/combinatorics/prufer.html | 395 - dev-py3k/modules/combinatorics/subsets.html | 541 - .../modules/combinatorics/tensor_can.html | 459 - dev-py3k/modules/combinatorics/testutil.html | 223 - dev-py3k/modules/combinatorics/util.html | 483 - dev-py3k/modules/concrete.html | 568 - dev-py3k/modules/core.html | 6367 ------- dev-py3k/modules/diffgeom.html | 572 - dev-py3k/modules/evalf.html | 541 - dev-py3k/modules/functions/combinatorial.html | 826 - dev-py3k/modules/functions/elementary.html | 1484 -- dev-py3k/modules/functions/index.html | 333 - dev-py3k/modules/functions/special.html | 3313 ---- dev-py3k/modules/galgebra/GA.html | 316 - dev-py3k/modules/galgebra/GA/GAsympy.html | 2002 --- dev-py3k/modules/galgebra/index.html | 150 - dev-py3k/modules/galgebra/latex_ex.html | 324 - .../modules/galgebra/latex_ex/latex_ex.html | 561 - dev-py3k/modules/geometry.html | 5271 ------ dev-py3k/modules/index.html | 460 - dev-py3k/modules/integrals/g-functions.html | 857 - dev-py3k/modules/integrals/integrals.html | 1296 -- dev-py3k/modules/logic.html | 421 - dev-py3k/modules/matrices/dense.html | 469 - dev-py3k/modules/matrices/expressions.html | 658 - .../modules/matrices/immutablematrices.html | 259 - dev-py3k/modules/matrices/index.html | 180 - dev-py3k/modules/matrices/matrices.html | 3216 ---- dev-py3k/modules/matrices/sparse.html | 829 - dev-py3k/modules/mpmath/basics.html | 387 - .../mpmath/calculus/approximation.html | 371 - .../mpmath/calculus/differentiation.html | 436 - dev-py3k/modules/mpmath/calculus/index.html | 187 - .../modules/mpmath/calculus/integration.html | 800 - dev-py3k/modules/mpmath/calculus/odes.html | 313 - .../modules/mpmath/calculus/optimization.html | 553 - .../modules/mpmath/calculus/polynomials.html | 271 - .../modules/mpmath/calculus/sums_limits.html | 1027 -- dev-py3k/modules/mpmath/contexts.html | 453 - dev-py3k/modules/mpmath/functions/bessel.html | 2607 --- .../modules/mpmath/functions/constants.html | 279 - .../modules/mpmath/functions/elliptic.html | 1368 -- .../mpmath/functions/expintegrals.html | 1152 -- dev-py3k/modules/mpmath/functions/gamma.html | 1230 -- .../modules/mpmath/functions/hyperbolic.html | 374 - .../mpmath/functions/hypergeometric.html | 1513 -- dev-py3k/modules/mpmath/functions/index.html | 273 - .../modules/mpmath/functions/numtheory.html | 950 - .../modules/mpmath/functions/orthogonal.html | 969 - dev-py3k/modules/mpmath/functions/powers.html | 1067 -- .../modules/mpmath/functions/qfunctions.html | 346 - .../mpmath/functions/trigonometric.html | 711 - dev-py3k/modules/mpmath/functions/zeta.html | 1567 -- dev-py3k/modules/mpmath/general.html | 1562 -- dev-py3k/modules/mpmath/identification.html | 591 - dev-py3k/modules/mpmath/index.html | 285 - dev-py3k/modules/mpmath/matrices.html | 981 - dev-py3k/modules/mpmath/plotting.html | 254 - dev-py3k/modules/mpmath/references.html | 275 - dev-py3k/modules/mpmath/setup.html | 307 - dev-py3k/modules/mpmath/technical.html | 313 - dev-py3k/modules/ntheory.html | 1624 -- dev-py3k/modules/parsing.html | 267 - dev-py3k/modules/physics/gaussopt.html | 830 - dev-py3k/modules/physics/hydrogen.html | 279 - dev-py3k/modules/physics/index.html | 240 - dev-py3k/modules/physics/matrices.html | 212 - .../modules/physics/mechanics/advanced.html | 366 - .../physics/mechanics/api/essential.html | 925 - .../physics/mechanics/api/functions.html | 336 - .../modules/physics/mechanics/api/kane.html | 553 - .../physics/mechanics/api/kinematics.html | 601 - .../physics/mechanics/api/part_bod.html | 973 - .../physics/mechanics/api/printing.html | 303 - .../modules/physics/mechanics/examples.html | 720 - dev-py3k/modules/physics/mechanics/index.html | 291 - dev-py3k/modules/physics/mechanics/kane.html | 439 - .../modules/physics/mechanics/kinematics.html | 588 - .../modules/physics/mechanics/masses.html | 492 - .../modules/physics/mechanics/reference.html | 190 - .../modules/physics/mechanics/vectors.html | 804 - dev-py3k/modules/physics/paulialgebra.html | 178 - dev-py3k/modules/physics/qho_1d.html | 195 - .../physics/quantum/anticommutator.html | 220 - .../modules/physics/quantum/cartesian.html | 259 - dev-py3k/modules/physics/quantum/cg.html | 304 - .../modules/physics/quantum/circuitplot.html | 160 - .../modules/physics/quantum/commutator.html | 232 - .../modules/physics/quantum/constants.html | 147 - dev-py3k/modules/physics/quantum/dagger.html | 223 - dev-py3k/modules/physics/quantum/gate.html | 685 - dev-py3k/modules/physics/quantum/grover.html | 330 - dev-py3k/modules/physics/quantum/hilbert.html | 254 - dev-py3k/modules/physics/quantum/index.html | 217 - .../modules/physics/quantum/innerproduct.html | 210 - .../modules/physics/quantum/operator.html | 487 - .../modules/physics/quantum/operatorset.html | 272 - dev-py3k/modules/physics/quantum/piab.html | 165 - dev-py3k/modules/physics/quantum/qapply.html | 186 - dev-py3k/modules/physics/quantum/qft.html | 193 - dev-py3k/modules/physics/quantum/qubit.html | 515 - .../modules/physics/quantum/represent.html | 440 - dev-py3k/modules/physics/quantum/shor.html | 215 - dev-py3k/modules/physics/quantum/spin.html | 926 - dev-py3k/modules/physics/quantum/state.html | 690 - .../physics/quantum/tensorproduct.html | 252 - dev-py3k/modules/physics/secondquant.html | 1491 -- dev-py3k/modules/physics/sho.html | 218 - dev-py3k/modules/physics/units.html | 326 - dev-py3k/modules/physics/wigner.html | 543 - dev-py3k/modules/plotting.html | 1025 -- dev-py3k/modules/polys/agca.html | 1734 -- dev-py3k/modules/polys/basics.html | 380 - dev-py3k/modules/polys/index.html | 250 - dev-py3k/modules/polys/internals.html | 7400 -------- dev-py3k/modules/polys/literature.html | 297 - dev-py3k/modules/polys/reference.html | 4028 ----- dev-py3k/modules/polys/wester.html | 625 - dev-py3k/modules/printing.html | 1564 -- dev-py3k/modules/rewriting.html | 239 - dev-py3k/modules/series.html | 688 - dev-py3k/modules/sets.html | 970 - dev-py3k/modules/simplify/hyperexpand.html | 787 - dev-py3k/modules/simplify/simplify.html | 1496 -- dev-py3k/modules/solvers/ode.html | 1710 -- dev-py3k/modules/solvers/solvers.html | 1163 -- dev-py3k/modules/statistics.html | 574 - dev-py3k/modules/stats.html | 2510 --- dev-py3k/modules/tensor/index.html | 165 - dev-py3k/modules/tensor/index_methods.html | 313 - dev-py3k/modules/tensor/indexed.html | 567 - dev-py3k/modules/utilities/autowrap.html | 422 - dev-py3k/modules/utilities/codegen.html | 670 - dev-py3k/modules/utilities/cythonutils.html | 145 - dev-py3k/modules/utilities/decorator.html | 189 - dev-py3k/modules/utilities/index.html | 177 - dev-py3k/modules/utilities/iterables.html | 1379 -- dev-py3k/modules/utilities/lambdify.html | 298 - dev-py3k/modules/utilities/memoization.html | 160 - dev-py3k/modules/utilities/misc.html | 210 - dev-py3k/modules/utilities/pkgdata.html | 176 - dev-py3k/modules/utilities/pytest.html | 208 - dev-py3k/modules/utilities/randtest.html | 197 - dev-py3k/modules/utilities/runtests.html | 572 - dev-py3k/modules/utilities/source.html | 171 - dev-py3k/modules/utilities/timeutils.html | 152 - dev-py3k/np-modindex.html | 992 -- dev-py3k/objects.inv | Bin 80112 -> 0 bytes dev-py3k/outreach.html | 176 - dev-py3k/pics/pngview1.png | Bin 3251 -> 0 bytes dev-py3k/pics/winpdb1.png | Bin 66153 -> 0 bytes dev-py3k/pics/winpdb2.png | Bin 93795 -> 0 bytes dev-py3k/py-modindex.html | 992 -- dev-py3k/python-comparisons.html | 511 - dev-py3k/search.html | 126 - dev-py3k/searchindex.js | 1 - dev-py3k/tutorial/tutorial.bg.html | 978 - dev-py3k/tutorial/tutorial.cs.html | 988 -- dev-py3k/tutorial/tutorial.de.html | 974 - dev-py3k/tutorial/tutorial.en.html | 1065 -- dev-py3k/tutorial/tutorial.fr.html | 972 - dev-py3k/tutorial/tutorial.pl.html | 992 -- dev-py3k/tutorial/tutorial.ru.html | 975 - dev-py3k/tutorial/tutorial.sr.html | 991 -- dev-py3k/tutorial/tutorial.zh.html | 971 - dev-py3k/wiki.html | 154 - dev/index.html | 1 - releases.txt | 1 - 693 files changed, 367657 deletions(-) delete mode 100644 dev-py3k/.buildinfo delete mode 100644 dev-py3k/_images/ai.png delete mode 100644 dev-py3k/_images/ai_c.png delete mode 100644 dev-py3k/_images/ber.png delete mode 100644 dev-py3k/_images/besseli.png delete mode 100644 dev-py3k/_images/besseli_c.png delete mode 100644 dev-py3k/_images/besselj.png delete mode 100644 dev-py3k/_images/besselj_c.png delete mode 100644 dev-py3k/_images/besselk.png delete mode 100644 dev-py3k/_images/besselk_c.png delete mode 100644 dev-py3k/_images/bessely.png delete mode 100644 dev-py3k/_images/bessely_c.png delete mode 100644 dev-py3k/_images/bi.png delete mode 100644 dev-py3k/_images/bi_c.png delete mode 100644 dev-py3k/_images/chebyt.png delete mode 100644 dev-py3k/_images/chebyu.png delete mode 100644 dev-py3k/_images/coulombf.png delete mode 100644 dev-py3k/_images/coulombf_c.png delete mode 100644 dev-py3k/_images/coulombg.png delete mode 100644 dev-py3k/_images/coulombg_c.png delete mode 100644 dev-py3k/_images/cplot.png delete mode 100644 dev-py3k/_images/ellipe.png delete mode 100644 dev-py3k/_images/ellipf.png delete mode 100644 dev-py3k/_images/ellipk.png delete mode 100644 dev-py3k/_images/ellippi.png delete mode 100644 dev-py3k/_images/ex_rd.svg delete mode 100644 dev-py3k/_images/featured-downloads.png delete mode 100644 dev-py3k/_images/gi.png delete mode 100644 dev-py3k/_images/gi_c.png delete mode 100644 dev-py3k/_images/hankel1.png delete mode 100644 dev-py3k/_images/hankel1_c.png delete mode 100644 dev-py3k/_images/hankel2.png delete mode 100644 dev-py3k/_images/hankel2_c.png delete mode 100644 dev-py3k/_images/hermite.png delete mode 100644 dev-py3k/_images/hi.png delete mode 100644 dev-py3k/_images/hi_c.png delete mode 100644 dev-py3k/_images/ker.png delete mode 100644 dev-py3k/_images/kin_1.svg delete mode 100644 dev-py3k/_images/kin_1pt.svg delete mode 100644 dev-py3k/_images/kin_2.svg delete mode 100644 dev-py3k/_images/kin_2pt.svg delete mode 100644 dev-py3k/_images/kin_3.svg delete mode 100644 dev-py3k/_images/kin_4.svg delete mode 100644 dev-py3k/_images/kin_angvel1.svg delete mode 100644 dev-py3k/_images/kin_angvel2.svg delete mode 100644 dev-py3k/_images/kin_angvel3.svg delete mode 100644 dev-py3k/_images/kin_rolling.svg delete mode 100644 dev-py3k/_images/kleinj.png delete mode 100644 dev-py3k/_images/kleinj2.png delete mode 100644 dev-py3k/_images/laguerre.png delete mode 100644 dev-py3k/_images/lambertw.png delete mode 100644 dev-py3k/_images/lambertw_c.png delete mode 100644 dev-py3k/_images/legendre.png delete mode 100644 dev-py3k/_images/lommels1.png delete mode 100644 dev-py3k/_images/lommels2.png delete mode 100644 dev-py3k/_images/pcfd.png delete mode 100644 dev-py3k/_images/plot.png delete mode 100644 dev-py3k/_images/pngview1.png delete mode 100644 dev-py3k/_images/simp_rot.svg delete mode 100644 dev-py3k/_images/spherharm40.png delete mode 100644 dev-py3k/_images/spherharm41.png delete mode 100644 dev-py3k/_images/spherharm42.png delete mode 100644 dev-py3k/_images/spherharm43.png delete mode 100644 dev-py3k/_images/spherharm44.png delete mode 100644 dev-py3k/_images/splot.png delete mode 100644 dev-py3k/_images/vec_add.svg delete mode 100644 dev-py3k/_images/vec_cross.svg delete mode 100644 dev-py3k/_images/vec_dot.svg delete mode 100644 dev-py3k/_images/vec_fix_notfix.svg delete mode 100644 dev-py3k/_images/vec_mul.svg delete mode 100644 dev-py3k/_images/vec_rep.svg delete mode 100644 dev-py3k/_images/vec_simp_der.svg delete mode 100644 dev-py3k/_images/winpdb1.png delete mode 100644 dev-py3k/_images/winpdb2.png delete mode 100644 dev-py3k/_modules/index.html delete mode 100644 dev-py3k/_modules/mpmath.html delete mode 100644 dev-py3k/_modules/mpmath/calculus/optimization.html delete mode 100644 dev-py3k/_modules/mpmath/calculus/quadrature.html delete mode 100644 dev-py3k/_modules/sympy.html delete mode 100644 dev-py3k/_modules/sympy/assumptions/ask.html delete mode 100644 dev-py3k/_modules/sympy/assumptions/assume.html delete mode 100644 dev-py3k/_modules/sympy/assumptions/handlers/calculus.html delete mode 100644 dev-py3k/_modules/sympy/assumptions/handlers/ntheory.html delete mode 100644 dev-py3k/_modules/sympy/assumptions/handlers/order.html delete mode 100644 dev-py3k/_modules/sympy/assumptions/handlers/sets.html delete mode 100644 dev-py3k/_modules/sympy/assumptions/refine.html delete mode 100644 dev-py3k/_modules/sympy/categories.html delete mode 100644 dev-py3k/_modules/sympy/categories/diagram_drawing.html delete mode 100644 dev-py3k/_modules/sympy/combinatorics.html delete mode 100644 dev-py3k/_modules/sympy/combinatorics/graycode.html delete mode 100644 dev-py3k/_modules/sympy/combinatorics/group_constructs.html delete mode 100644 dev-py3k/_modules/sympy/combinatorics/named_groups.html delete mode 100644 dev-py3k/_modules/sympy/combinatorics/partitions.html delete mode 100644 dev-py3k/_modules/sympy/combinatorics/perm_groups.html delete mode 100644 dev-py3k/_modules/sympy/combinatorics/permutations.html delete mode 100644 dev-py3k/_modules/sympy/combinatorics/polyhedron.html delete mode 100644 dev-py3k/_modules/sympy/combinatorics/prufer.html delete mode 100644 dev-py3k/_modules/sympy/combinatorics/subsets.html delete mode 100644 dev-py3k/_modules/sympy/combinatorics/tensor_can.html delete mode 100644 dev-py3k/_modules/sympy/combinatorics/testutil.html delete mode 100644 dev-py3k/_modules/sympy/combinatorics/util.html delete mode 100644 dev-py3k/_modules/sympy/concrete/gosper.html delete mode 100644 dev-py3k/_modules/sympy/concrete/products.html delete mode 100644 dev-py3k/_modules/sympy/concrete/summations.html delete mode 100644 dev-py3k/_modules/sympy/core/add.html delete mode 100644 dev-py3k/_modules/sympy/core/basic.html delete mode 100644 dev-py3k/_modules/sympy/core/cache.html delete mode 100644 dev-py3k/_modules/sympy/core/compatibility.html delete mode 100644 dev-py3k/_modules/sympy/core/containers.html delete mode 100644 dev-py3k/_modules/sympy/core/evalf.html delete mode 100644 dev-py3k/_modules/sympy/core/expr.html delete mode 100644 dev-py3k/_modules/sympy/core/exprtools.html delete mode 100644 dev-py3k/_modules/sympy/core/function.html delete mode 100644 dev-py3k/_modules/sympy/core/mod.html delete mode 100644 dev-py3k/_modules/sympy/core/mul.html delete mode 100644 dev-py3k/_modules/sympy/core/multidimensional.html delete mode 100644 dev-py3k/_modules/sympy/core/numbers.html delete mode 100644 dev-py3k/_modules/sympy/core/power.html delete mode 100644 dev-py3k/_modules/sympy/core/relational.html delete mode 100644 dev-py3k/_modules/sympy/core/sets.html delete mode 100644 dev-py3k/_modules/sympy/core/singleton.html delete mode 100644 dev-py3k/_modules/sympy/core/symbol.html delete mode 100644 dev-py3k/_modules/sympy/core/sympify.html delete mode 100644 dev-py3k/_modules/sympy/diffgeom.html delete mode 100644 dev-py3k/_modules/sympy/functions/combinatorial/factorials.html delete mode 100644 dev-py3k/_modules/sympy/functions/combinatorial/numbers.html delete mode 100644 dev-py3k/_modules/sympy/functions/elementary/complexes.html delete mode 100644 dev-py3k/_modules/sympy/functions/elementary/exponential.html delete mode 100644 dev-py3k/_modules/sympy/functions/elementary/hyperbolic.html delete mode 100644 dev-py3k/_modules/sympy/functions/elementary/integers.html delete mode 100644 dev-py3k/_modules/sympy/functions/elementary/miscellaneous.html delete mode 100644 dev-py3k/_modules/sympy/functions/elementary/piecewise.html delete mode 100644 dev-py3k/_modules/sympy/functions/elementary/trigonometric.html delete mode 100644 dev-py3k/_modules/sympy/functions/special/bessel.html delete mode 100644 dev-py3k/_modules/sympy/functions/special/bsplines.html delete mode 100644 dev-py3k/_modules/sympy/functions/special/delta_functions.html delete mode 100644 dev-py3k/_modules/sympy/functions/special/error_functions.html delete mode 100644 dev-py3k/_modules/sympy/functions/special/gamma_functions.html delete mode 100644 dev-py3k/_modules/sympy/functions/special/hyper.html delete mode 100644 dev-py3k/_modules/sympy/functions/special/polynomials.html delete mode 100644 dev-py3k/_modules/sympy/functions/special/spherical_harmonics.html delete mode 100644 dev-py3k/_modules/sympy/functions/special/tensor_functions.html delete mode 100644 dev-py3k/_modules/sympy/functions/special/zeta_functions.html delete mode 100644 dev-py3k/_modules/sympy/galgebra/GA.html delete mode 100644 dev-py3k/_modules/sympy/galgebra/latex_ex.html delete mode 100644 dev-py3k/_modules/sympy/geometry/curve.html delete mode 100644 dev-py3k/_modules/sympy/geometry/ellipse.html delete mode 100644 dev-py3k/_modules/sympy/geometry/entity.html delete mode 100644 dev-py3k/_modules/sympy/geometry/line.html delete mode 100644 dev-py3k/_modules/sympy/geometry/point.html delete mode 100644 dev-py3k/_modules/sympy/geometry/polygon.html delete mode 100644 dev-py3k/_modules/sympy/geometry/util.html delete mode 100644 dev-py3k/_modules/sympy/integrals.html delete mode 100644 dev-py3k/_modules/sympy/integrals/transforms.html delete mode 100644 dev-py3k/_modules/sympy/logic/boolalg.html delete mode 100644 dev-py3k/_modules/sympy/logic/inference.html delete mode 100644 dev-py3k/_modules/sympy/matrices/dense.html delete mode 100644 dev-py3k/_modules/sympy/matrices/expressions.html delete mode 100644 dev-py3k/_modules/sympy/matrices/expressions/blockmatrix.html delete mode 100644 dev-py3k/_modules/sympy/matrices/immutable.html delete mode 100644 dev-py3k/_modules/sympy/matrices/matrices.html delete mode 100644 dev-py3k/_modules/sympy/matrices/sparse.html delete mode 100644 dev-py3k/_modules/sympy/ntheory/factor_.html delete mode 100644 dev-py3k/_modules/sympy/ntheory/generate.html delete mode 100644 dev-py3k/_modules/sympy/ntheory/modular.html delete mode 100644 dev-py3k/_modules/sympy/ntheory/multinomial.html delete mode 100644 dev-py3k/_modules/sympy/ntheory/partitions_.html delete mode 100644 dev-py3k/_modules/sympy/ntheory/primetest.html delete mode 100644 dev-py3k/_modules/sympy/ntheory/residue_ntheory.html delete mode 100644 dev-py3k/_modules/sympy/parsing/mathematica.html delete mode 100644 dev-py3k/_modules/sympy/parsing/maxima.html delete mode 100644 dev-py3k/_modules/sympy/parsing/sympy_parser.html delete mode 100644 dev-py3k/_modules/sympy/parsing/sympy_tokenize.html delete mode 100644 dev-py3k/_modules/sympy/physics/gaussopt.html delete mode 100644 dev-py3k/_modules/sympy/physics/hydrogen.html delete mode 100644 dev-py3k/_modules/sympy/physics/matrices.html delete mode 100644 dev-py3k/_modules/sympy/physics/mechanics/essential.html delete mode 100644 dev-py3k/_modules/sympy/physics/mechanics/functions.html delete mode 100644 dev-py3k/_modules/sympy/physics/mechanics/kane.html delete mode 100644 dev-py3k/_modules/sympy/physics/mechanics/lagrange.html delete mode 100644 dev-py3k/_modules/sympy/physics/mechanics/particle.html delete mode 100644 dev-py3k/_modules/sympy/physics/mechanics/point.html delete mode 100644 dev-py3k/_modules/sympy/physics/mechanics/rigidbody.html delete mode 100644 dev-py3k/_modules/sympy/physics/paulialgebra.html delete mode 100644 dev-py3k/_modules/sympy/physics/qho_1d.html delete mode 100644 dev-py3k/_modules/sympy/physics/quantum/anticommutator.html delete mode 100644 dev-py3k/_modules/sympy/physics/quantum/cartesian.html delete mode 100644 dev-py3k/_modules/sympy/physics/quantum/cg.html delete mode 100644 dev-py3k/_modules/sympy/physics/quantum/commutator.html delete mode 100644 dev-py3k/_modules/sympy/physics/quantum/dagger.html delete mode 100644 dev-py3k/_modules/sympy/physics/quantum/gate.html delete mode 100644 dev-py3k/_modules/sympy/physics/quantum/grover.html delete mode 100644 dev-py3k/_modules/sympy/physics/quantum/hilbert.html delete mode 100644 dev-py3k/_modules/sympy/physics/quantum/innerproduct.html delete mode 100644 dev-py3k/_modules/sympy/physics/quantum/operator.html delete mode 100644 dev-py3k/_modules/sympy/physics/quantum/operatorset.html delete mode 100644 dev-py3k/_modules/sympy/physics/quantum/piab.html delete mode 100644 dev-py3k/_modules/sympy/physics/quantum/qapply.html delete mode 100644 dev-py3k/_modules/sympy/physics/quantum/qft.html delete mode 100644 dev-py3k/_modules/sympy/physics/quantum/qubit.html delete mode 100644 dev-py3k/_modules/sympy/physics/quantum/represent.html delete mode 100644 dev-py3k/_modules/sympy/physics/quantum/shor.html delete mode 100644 dev-py3k/_modules/sympy/physics/quantum/spin.html delete mode 100644 dev-py3k/_modules/sympy/physics/quantum/state.html delete mode 100644 dev-py3k/_modules/sympy/physics/quantum/tensorproduct.html delete mode 100644 dev-py3k/_modules/sympy/physics/secondquant.html delete mode 100644 dev-py3k/_modules/sympy/physics/sho.html delete mode 100644 dev-py3k/_modules/sympy/physics/units.html delete mode 100644 dev-py3k/_modules/sympy/physics/wigner.html delete mode 100644 dev-py3k/_modules/sympy/plotting/plot.html delete mode 100644 dev-py3k/_modules/sympy/plotting/plot_implicit.html delete mode 100644 dev-py3k/_modules/sympy/polys/agca/homomorphisms.html delete mode 100644 dev-py3k/_modules/sympy/polys/agca/ideals.html delete mode 100644 dev-py3k/_modules/sympy/polys/agca/modules.html delete mode 100644 dev-py3k/_modules/sympy/polys/constructor.html delete mode 100644 dev-py3k/_modules/sympy/polys/densearith.html delete mode 100644 dev-py3k/_modules/sympy/polys/densebasic.html delete mode 100644 dev-py3k/_modules/sympy/polys/densetools.html delete mode 100644 dev-py3k/_modules/sympy/polys/distributedmodules.html delete mode 100644 dev-py3k/_modules/sympy/polys/distributedpolys.html delete mode 100644 dev-py3k/_modules/sympy/polys/domains.html delete mode 100644 dev-py3k/_modules/sympy/polys/euclidtools.html delete mode 100644 dev-py3k/_modules/sympy/polys/factortools.html delete mode 100644 dev-py3k/_modules/sympy/polys/galoistools.html delete mode 100644 dev-py3k/_modules/sympy/polys/groebnertools.html delete mode 100644 dev-py3k/_modules/sympy/polys/monomialtools.html delete mode 100644 dev-py3k/_modules/sympy/polys/numberfields.html delete mode 100644 dev-py3k/_modules/sympy/polys/orthopolys.html delete mode 100644 dev-py3k/_modules/sympy/polys/partfrac.html delete mode 100644 dev-py3k/_modules/sympy/polys/polyclasses.html delete mode 100644 dev-py3k/_modules/sympy/polys/polyerrors.html delete mode 100644 dev-py3k/_modules/sympy/polys/polyfuncs.html delete mode 100644 dev-py3k/_modules/sympy/polys/polyroots.html delete mode 100644 dev-py3k/_modules/sympy/polys/polytools.html delete mode 100644 dev-py3k/_modules/sympy/polys/rationaltools.html delete mode 100644 dev-py3k/_modules/sympy/polys/rootoftools.html delete mode 100644 dev-py3k/_modules/sympy/polys/specialpolys.html delete mode 100644 dev-py3k/_modules/sympy/printing/ccode.html delete mode 100644 dev-py3k/_modules/sympy/printing/codeprinter.html delete mode 100644 dev-py3k/_modules/sympy/printing/conventions.html delete mode 100644 dev-py3k/_modules/sympy/printing/fcode.html delete mode 100644 dev-py3k/_modules/sympy/printing/gtk.html delete mode 100644 dev-py3k/_modules/sympy/printing/lambdarepr.html delete mode 100644 dev-py3k/_modules/sympy/printing/latex.html delete mode 100644 dev-py3k/_modules/sympy/printing/mathml.html delete mode 100644 dev-py3k/_modules/sympy/printing/precedence.html delete mode 100644 dev-py3k/_modules/sympy/printing/pretty/pretty.html delete mode 100644 dev-py3k/_modules/sympy/printing/pretty/pretty_symbology.html delete mode 100644 dev-py3k/_modules/sympy/printing/pretty/stringpict.html delete mode 100644 dev-py3k/_modules/sympy/printing/preview.html delete mode 100644 dev-py3k/_modules/sympy/printing/printer.html delete mode 100644 dev-py3k/_modules/sympy/printing/repr.html delete mode 100644 dev-py3k/_modules/sympy/printing/str.html delete mode 100644 dev-py3k/_modules/sympy/printing/tree.html delete mode 100644 dev-py3k/_modules/sympy/series/acceleration.html delete mode 100644 dev-py3k/_modules/sympy/series/gruntz.html delete mode 100644 dev-py3k/_modules/sympy/series/limits.html delete mode 100644 dev-py3k/_modules/sympy/series/order.html delete mode 100644 dev-py3k/_modules/sympy/series/residues.html delete mode 100644 dev-py3k/_modules/sympy/series/series.html delete mode 100644 dev-py3k/_modules/sympy/sets/fancysets.html delete mode 100644 dev-py3k/_modules/sympy/simplify/cse_main.html delete mode 100644 dev-py3k/_modules/sympy/simplify/epathtools.html delete mode 100644 dev-py3k/_modules/sympy/simplify/hyperexpand.html delete mode 100644 dev-py3k/_modules/sympy/simplify/simplify.html delete mode 100644 dev-py3k/_modules/sympy/simplify/sqrtdenest.html delete mode 100644 dev-py3k/_modules/sympy/simplify/traversaltools.html delete mode 100644 dev-py3k/_modules/sympy/solvers/ode.html delete mode 100644 dev-py3k/_modules/sympy/solvers/pde.html delete mode 100644 dev-py3k/_modules/sympy/solvers/polysys.html delete mode 100644 dev-py3k/_modules/sympy/solvers/recurr.html delete mode 100644 dev-py3k/_modules/sympy/solvers/solvers.html delete mode 100644 dev-py3k/_modules/sympy/statistics/distributions.html delete mode 100644 dev-py3k/_modules/sympy/stats.html delete mode 100644 dev-py3k/_modules/sympy/stats/crv.html delete mode 100644 dev-py3k/_modules/sympy/stats/crv_types.html delete mode 100644 dev-py3k/_modules/sympy/stats/frv.html delete mode 100644 dev-py3k/_modules/sympy/stats/frv_types.html delete mode 100644 dev-py3k/_modules/sympy/stats/rv.html delete mode 100644 dev-py3k/_modules/sympy/tensor/index_methods.html delete mode 100644 dev-py3k/_modules/sympy/tensor/indexed.html delete mode 100644 dev-py3k/_modules/sympy/utilities/autowrap.html delete mode 100644 dev-py3k/_modules/sympy/utilities/codegen.html delete mode 100644 dev-py3k/_modules/sympy/utilities/decorator.html delete mode 100644 dev-py3k/_modules/sympy/utilities/iterables.html delete mode 100644 dev-py3k/_modules/sympy/utilities/lambdify.html delete mode 100644 dev-py3k/_modules/sympy/utilities/memoization.html delete mode 100644 dev-py3k/_modules/sympy/utilities/misc.html delete mode 100644 dev-py3k/_modules/sympy/utilities/pkgdata.html delete mode 100644 dev-py3k/_modules/sympy/utilities/pytest.html delete mode 100644 dev-py3k/_modules/sympy/utilities/randtest.html delete mode 100644 dev-py3k/_modules/sympy/utilities/runtests.html delete mode 100644 dev-py3k/_modules/sympy/utilities/source.html delete mode 100644 dev-py3k/_modules/sympy/utilities/timeutils.html delete mode 100644 dev-py3k/_sources/aboutus.txt delete mode 100644 dev-py3k/_sources/gotchas.txt delete mode 100644 dev-py3k/_sources/guide.txt delete mode 100644 dev-py3k/_sources/index.txt delete mode 100644 dev-py3k/_sources/install.txt delete mode 100644 dev-py3k/_sources/modules/assumptions/ask.txt delete mode 100644 dev-py3k/_sources/modules/assumptions/assume.txt delete mode 100644 dev-py3k/_sources/modules/assumptions/handlers/calculus.txt delete mode 100644 dev-py3k/_sources/modules/assumptions/handlers/index.txt delete mode 100644 dev-py3k/_sources/modules/assumptions/handlers/ntheory.txt delete mode 100644 dev-py3k/_sources/modules/assumptions/handlers/order.txt delete mode 100644 dev-py3k/_sources/modules/assumptions/handlers/sets.txt delete mode 100644 dev-py3k/_sources/modules/assumptions/index.txt delete mode 100644 dev-py3k/_sources/modules/assumptions/refine.txt delete mode 100644 dev-py3k/_sources/modules/categories.txt delete mode 100644 dev-py3k/_sources/modules/combinatorics/graycode.txt delete mode 100644 dev-py3k/_sources/modules/combinatorics/group_constructs.txt delete mode 100644 dev-py3k/_sources/modules/combinatorics/index.txt delete mode 100644 dev-py3k/_sources/modules/combinatorics/named_groups.txt delete mode 100644 dev-py3k/_sources/modules/combinatorics/partitions.txt delete mode 100644 dev-py3k/_sources/modules/combinatorics/perm_groups.txt delete mode 100644 dev-py3k/_sources/modules/combinatorics/permutations.txt delete mode 100644 dev-py3k/_sources/modules/combinatorics/polyhedron.txt delete mode 100644 dev-py3k/_sources/modules/combinatorics/prufer.txt delete mode 100644 dev-py3k/_sources/modules/combinatorics/subsets.txt delete mode 100644 dev-py3k/_sources/modules/combinatorics/tensor_can.txt delete mode 100644 dev-py3k/_sources/modules/combinatorics/testutil.txt delete mode 100644 dev-py3k/_sources/modules/combinatorics/util.txt delete mode 100644 dev-py3k/_sources/modules/concrete.txt delete mode 100644 dev-py3k/_sources/modules/core.txt delete mode 100644 dev-py3k/_sources/modules/diffgeom.txt delete mode 100644 dev-py3k/_sources/modules/evalf.txt delete mode 100644 dev-py3k/_sources/modules/functions/combinatorial.txt delete mode 100644 dev-py3k/_sources/modules/functions/elementary.txt delete mode 100644 dev-py3k/_sources/modules/functions/index.txt delete mode 100644 dev-py3k/_sources/modules/functions/special.txt delete mode 100644 dev-py3k/_sources/modules/galgebra/GA.txt delete mode 100644 dev-py3k/_sources/modules/galgebra/GA/GAsympy.txt delete mode 100644 dev-py3k/_sources/modules/galgebra/index.txt delete mode 100644 dev-py3k/_sources/modules/galgebra/latex_ex.txt delete mode 100644 dev-py3k/_sources/modules/galgebra/latex_ex/latex_ex.txt delete mode 100644 dev-py3k/_sources/modules/geometry.txt delete mode 100644 dev-py3k/_sources/modules/index.txt delete mode 100644 dev-py3k/_sources/modules/integrals/g-functions.txt delete mode 100644 dev-py3k/_sources/modules/integrals/integrals.txt delete mode 100644 dev-py3k/_sources/modules/logic.txt delete mode 100644 dev-py3k/_sources/modules/matrices/dense.txt delete mode 100644 dev-py3k/_sources/modules/matrices/expressions.txt delete mode 100644 dev-py3k/_sources/modules/matrices/immutablematrices.txt delete mode 100644 dev-py3k/_sources/modules/matrices/index.txt delete mode 100644 dev-py3k/_sources/modules/matrices/matrices.txt delete mode 100644 dev-py3k/_sources/modules/matrices/sparse.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/basics.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/calculus/approximation.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/calculus/differentiation.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/calculus/index.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/calculus/integration.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/calculus/odes.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/calculus/optimization.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/calculus/polynomials.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/calculus/sums_limits.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/contexts.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/functions/bessel.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/functions/constants.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/functions/elliptic.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/functions/expintegrals.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/functions/gamma.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/functions/hyperbolic.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/functions/hypergeometric.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/functions/index.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/functions/numtheory.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/functions/orthogonal.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/functions/powers.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/functions/qfunctions.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/functions/trigonometric.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/functions/zeta.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/general.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/identification.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/index.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/matrices.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/plotting.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/references.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/setup.txt delete mode 100644 dev-py3k/_sources/modules/mpmath/technical.txt delete mode 100644 dev-py3k/_sources/modules/ntheory.txt delete mode 100644 dev-py3k/_sources/modules/parsing.txt delete mode 100644 dev-py3k/_sources/modules/physics/gaussopt.txt delete mode 100644 dev-py3k/_sources/modules/physics/hydrogen.txt delete mode 100644 dev-py3k/_sources/modules/physics/index.txt delete mode 100644 dev-py3k/_sources/modules/physics/matrices.txt delete mode 100644 dev-py3k/_sources/modules/physics/mechanics/advanced.txt delete mode 100644 dev-py3k/_sources/modules/physics/mechanics/api/essential.txt delete mode 100644 dev-py3k/_sources/modules/physics/mechanics/api/functions.txt delete mode 100644 dev-py3k/_sources/modules/physics/mechanics/api/kane.txt delete mode 100644 dev-py3k/_sources/modules/physics/mechanics/api/kinematics.txt delete mode 100644 dev-py3k/_sources/modules/physics/mechanics/api/part_bod.txt delete mode 100644 dev-py3k/_sources/modules/physics/mechanics/api/printing.txt delete mode 100644 dev-py3k/_sources/modules/physics/mechanics/examples.txt delete mode 100644 dev-py3k/_sources/modules/physics/mechanics/index.txt delete mode 100644 dev-py3k/_sources/modules/physics/mechanics/kane.txt delete mode 100644 dev-py3k/_sources/modules/physics/mechanics/kinematics.txt delete mode 100644 dev-py3k/_sources/modules/physics/mechanics/masses.txt delete mode 100644 dev-py3k/_sources/modules/physics/mechanics/reference.txt delete mode 100644 dev-py3k/_sources/modules/physics/mechanics/vectors.txt delete mode 100644 dev-py3k/_sources/modules/physics/paulialgebra.txt delete mode 100644 dev-py3k/_sources/modules/physics/qho_1d.txt delete mode 100644 dev-py3k/_sources/modules/physics/quantum/anticommutator.txt delete mode 100644 dev-py3k/_sources/modules/physics/quantum/cartesian.txt delete mode 100644 dev-py3k/_sources/modules/physics/quantum/cg.txt delete mode 100644 dev-py3k/_sources/modules/physics/quantum/circuitplot.txt delete mode 100644 dev-py3k/_sources/modules/physics/quantum/commutator.txt delete mode 100644 dev-py3k/_sources/modules/physics/quantum/constants.txt delete mode 100644 dev-py3k/_sources/modules/physics/quantum/dagger.txt delete mode 100644 dev-py3k/_sources/modules/physics/quantum/gate.txt delete mode 100644 dev-py3k/_sources/modules/physics/quantum/grover.txt delete mode 100644 dev-py3k/_sources/modules/physics/quantum/hilbert.txt delete mode 100644 dev-py3k/_sources/modules/physics/quantum/index.txt delete mode 100644 dev-py3k/_sources/modules/physics/quantum/innerproduct.txt delete mode 100644 dev-py3k/_sources/modules/physics/quantum/operator.txt delete mode 100644 dev-py3k/_sources/modules/physics/quantum/operatorset.txt delete mode 100644 dev-py3k/_sources/modules/physics/quantum/piab.txt delete mode 100644 dev-py3k/_sources/modules/physics/quantum/qapply.txt delete mode 100644 dev-py3k/_sources/modules/physics/quantum/qft.txt delete mode 100644 dev-py3k/_sources/modules/physics/quantum/qubit.txt delete mode 100644 dev-py3k/_sources/modules/physics/quantum/represent.txt delete mode 100644 dev-py3k/_sources/modules/physics/quantum/shor.txt delete mode 100644 dev-py3k/_sources/modules/physics/quantum/spin.txt delete mode 100644 dev-py3k/_sources/modules/physics/quantum/state.txt delete mode 100644 dev-py3k/_sources/modules/physics/quantum/tensorproduct.txt delete mode 100644 dev-py3k/_sources/modules/physics/secondquant.txt delete mode 100644 dev-py3k/_sources/modules/physics/sho.txt delete mode 100644 dev-py3k/_sources/modules/physics/units.txt delete mode 100644 dev-py3k/_sources/modules/physics/wigner.txt delete mode 100644 dev-py3k/_sources/modules/plotting.txt delete mode 100644 dev-py3k/_sources/modules/polys/agca.txt delete mode 100644 dev-py3k/_sources/modules/polys/basics.txt delete mode 100644 dev-py3k/_sources/modules/polys/index.txt delete mode 100644 dev-py3k/_sources/modules/polys/internals.txt delete mode 100644 dev-py3k/_sources/modules/polys/literature.txt delete mode 100644 dev-py3k/_sources/modules/polys/reference.txt delete mode 100644 dev-py3k/_sources/modules/polys/wester.txt delete mode 100644 dev-py3k/_sources/modules/printing.txt delete mode 100644 dev-py3k/_sources/modules/rewriting.txt delete mode 100644 dev-py3k/_sources/modules/series.txt delete mode 100644 dev-py3k/_sources/modules/sets.txt delete mode 100644 dev-py3k/_sources/modules/simplify/hyperexpand.txt delete mode 100644 dev-py3k/_sources/modules/simplify/simplify.txt delete mode 100644 dev-py3k/_sources/modules/solvers/ode.txt delete mode 100644 dev-py3k/_sources/modules/solvers/solvers.txt delete mode 100644 dev-py3k/_sources/modules/statistics.txt delete mode 100644 dev-py3k/_sources/modules/stats.txt delete mode 100644 dev-py3k/_sources/modules/tensor/index.txt delete mode 100644 dev-py3k/_sources/modules/tensor/index_methods.txt delete mode 100644 dev-py3k/_sources/modules/tensor/indexed.txt delete mode 100644 dev-py3k/_sources/modules/utilities/autowrap.txt delete mode 100644 dev-py3k/_sources/modules/utilities/codegen.txt delete mode 100644 dev-py3k/_sources/modules/utilities/cythonutils.txt delete mode 100644 dev-py3k/_sources/modules/utilities/decorator.txt delete mode 100644 dev-py3k/_sources/modules/utilities/index.txt delete mode 100644 dev-py3k/_sources/modules/utilities/iterables.txt delete mode 100644 dev-py3k/_sources/modules/utilities/lambdify.txt delete mode 100644 dev-py3k/_sources/modules/utilities/memoization.txt delete mode 100644 dev-py3k/_sources/modules/utilities/misc.txt delete mode 100644 dev-py3k/_sources/modules/utilities/pkgdata.txt delete mode 100644 dev-py3k/_sources/modules/utilities/pytest.txt delete mode 100644 dev-py3k/_sources/modules/utilities/randtest.txt delete mode 100644 dev-py3k/_sources/modules/utilities/runtests.txt delete mode 100644 dev-py3k/_sources/modules/utilities/source.txt delete mode 100644 dev-py3k/_sources/modules/utilities/timeutils.txt delete mode 100644 dev-py3k/_sources/outreach.txt delete mode 100644 dev-py3k/_sources/python-comparisons.txt delete mode 100644 dev-py3k/_sources/tutorial/tutorial.en.txt delete mode 100644 dev-py3k/_sources/wiki.txt delete mode 100644 dev-py3k/_static/SymPy-Favicon.ico delete mode 100644 dev-py3k/_static/ajax-loader.gif delete mode 100644 dev-py3k/_static/basic.css delete mode 100644 dev-py3k/_static/comment-bright.png delete mode 100644 dev-py3k/_static/comment-close.png delete mode 100644 dev-py3k/_static/comment.png delete mode 100644 dev-py3k/_static/default.css delete mode 100644 dev-py3k/_static/doctools.js delete mode 100644 dev-py3k/_static/down-pressed.png delete mode 100644 dev-py3k/_static/down.png delete mode 100644 dev-py3k/_static/file.png delete mode 100644 dev-py3k/_static/jquery.js delete mode 100644 dev-py3k/_static/minus.png delete mode 100644 dev-py3k/_static/plus.png delete mode 100644 dev-py3k/_static/pygments.css delete mode 100644 dev-py3k/_static/searchtools.js delete mode 100644 dev-py3k/_static/sidebar.js delete mode 100644 dev-py3k/_static/sympylogo.png delete mode 100644 dev-py3k/_static/underscore.js delete mode 100644 dev-py3k/_static/up-pressed.png delete mode 100644 dev-py3k/_static/up.png delete mode 100644 dev-py3k/_static/websupport.js delete mode 100644 dev-py3k/aboutus.html delete mode 100644 dev-py3k/figures/admon-note.png delete mode 100644 dev-py3k/figures/featured-downloads.png delete mode 100644 dev-py3k/genindex.html delete mode 100644 dev-py3k/gotchas.html delete mode 100644 dev-py3k/guide.html delete mode 100644 dev-py3k/index.html delete mode 100644 dev-py3k/install.html delete mode 100644 dev-py3k/modules/assumptions/ask.html delete mode 100644 dev-py3k/modules/assumptions/assume.html delete mode 100644 dev-py3k/modules/assumptions/handlers/calculus.html delete mode 100644 dev-py3k/modules/assumptions/handlers/index.html delete mode 100644 dev-py3k/modules/assumptions/handlers/ntheory.html delete mode 100644 dev-py3k/modules/assumptions/handlers/order.html delete mode 100644 dev-py3k/modules/assumptions/handlers/sets.html delete mode 100644 dev-py3k/modules/assumptions/index.html delete mode 100644 dev-py3k/modules/assumptions/refine.html delete mode 100644 dev-py3k/modules/categories.html delete mode 100644 dev-py3k/modules/combinatorics/graycode.html delete mode 100644 dev-py3k/modules/combinatorics/group_constructs.html delete mode 100644 dev-py3k/modules/combinatorics/index.html delete mode 100644 dev-py3k/modules/combinatorics/named_groups.html delete mode 100644 dev-py3k/modules/combinatorics/partitions.html delete mode 100644 dev-py3k/modules/combinatorics/perm_groups.html delete mode 100644 dev-py3k/modules/combinatorics/permutations.html delete mode 100644 dev-py3k/modules/combinatorics/polyhedron.html delete mode 100644 dev-py3k/modules/combinatorics/prufer.html delete mode 100644 dev-py3k/modules/combinatorics/subsets.html delete mode 100644 dev-py3k/modules/combinatorics/tensor_can.html delete mode 100644 dev-py3k/modules/combinatorics/testutil.html delete mode 100644 dev-py3k/modules/combinatorics/util.html delete mode 100644 dev-py3k/modules/concrete.html delete mode 100644 dev-py3k/modules/core.html delete mode 100644 dev-py3k/modules/diffgeom.html delete mode 100644 dev-py3k/modules/evalf.html delete mode 100644 dev-py3k/modules/functions/combinatorial.html delete mode 100644 dev-py3k/modules/functions/elementary.html delete mode 100644 dev-py3k/modules/functions/index.html delete mode 100644 dev-py3k/modules/functions/special.html delete mode 100644 dev-py3k/modules/galgebra/GA.html delete mode 100644 dev-py3k/modules/galgebra/GA/GAsympy.html delete mode 100644 dev-py3k/modules/galgebra/index.html delete mode 100644 dev-py3k/modules/galgebra/latex_ex.html delete mode 100644 dev-py3k/modules/galgebra/latex_ex/latex_ex.html delete mode 100644 dev-py3k/modules/geometry.html delete mode 100644 dev-py3k/modules/index.html delete mode 100644 dev-py3k/modules/integrals/g-functions.html delete mode 100644 dev-py3k/modules/integrals/integrals.html delete mode 100644 dev-py3k/modules/logic.html delete mode 100644 dev-py3k/modules/matrices/dense.html delete mode 100644 dev-py3k/modules/matrices/expressions.html delete mode 100644 dev-py3k/modules/matrices/immutablematrices.html delete mode 100644 dev-py3k/modules/matrices/index.html delete mode 100644 dev-py3k/modules/matrices/matrices.html delete mode 100644 dev-py3k/modules/matrices/sparse.html delete mode 100644 dev-py3k/modules/mpmath/basics.html delete mode 100644 dev-py3k/modules/mpmath/calculus/approximation.html delete mode 100644 dev-py3k/modules/mpmath/calculus/differentiation.html delete mode 100644 dev-py3k/modules/mpmath/calculus/index.html delete mode 100644 dev-py3k/modules/mpmath/calculus/integration.html delete mode 100644 dev-py3k/modules/mpmath/calculus/odes.html delete mode 100644 dev-py3k/modules/mpmath/calculus/optimization.html delete mode 100644 dev-py3k/modules/mpmath/calculus/polynomials.html delete mode 100644 dev-py3k/modules/mpmath/calculus/sums_limits.html delete mode 100644 dev-py3k/modules/mpmath/contexts.html delete mode 100644 dev-py3k/modules/mpmath/functions/bessel.html delete mode 100644 dev-py3k/modules/mpmath/functions/constants.html delete mode 100644 dev-py3k/modules/mpmath/functions/elliptic.html delete mode 100644 dev-py3k/modules/mpmath/functions/expintegrals.html delete mode 100644 dev-py3k/modules/mpmath/functions/gamma.html delete mode 100644 dev-py3k/modules/mpmath/functions/hyperbolic.html delete mode 100644 dev-py3k/modules/mpmath/functions/hypergeometric.html delete mode 100644 dev-py3k/modules/mpmath/functions/index.html delete mode 100644 dev-py3k/modules/mpmath/functions/numtheory.html delete mode 100644 dev-py3k/modules/mpmath/functions/orthogonal.html delete mode 100644 dev-py3k/modules/mpmath/functions/powers.html delete mode 100644 dev-py3k/modules/mpmath/functions/qfunctions.html delete mode 100644 dev-py3k/modules/mpmath/functions/trigonometric.html delete mode 100644 dev-py3k/modules/mpmath/functions/zeta.html delete mode 100644 dev-py3k/modules/mpmath/general.html delete mode 100644 dev-py3k/modules/mpmath/identification.html delete mode 100644 dev-py3k/modules/mpmath/index.html delete mode 100644 dev-py3k/modules/mpmath/matrices.html delete mode 100644 dev-py3k/modules/mpmath/plotting.html delete mode 100644 dev-py3k/modules/mpmath/references.html delete mode 100644 dev-py3k/modules/mpmath/setup.html delete mode 100644 dev-py3k/modules/mpmath/technical.html delete mode 100644 dev-py3k/modules/ntheory.html delete mode 100644 dev-py3k/modules/parsing.html delete mode 100644 dev-py3k/modules/physics/gaussopt.html delete mode 100644 dev-py3k/modules/physics/hydrogen.html delete mode 100644 dev-py3k/modules/physics/index.html delete mode 100644 dev-py3k/modules/physics/matrices.html delete mode 100644 dev-py3k/modules/physics/mechanics/advanced.html delete mode 100644 dev-py3k/modules/physics/mechanics/api/essential.html delete mode 100644 dev-py3k/modules/physics/mechanics/api/functions.html delete mode 100644 dev-py3k/modules/physics/mechanics/api/kane.html delete mode 100644 dev-py3k/modules/physics/mechanics/api/kinematics.html delete mode 100644 dev-py3k/modules/physics/mechanics/api/part_bod.html delete mode 100644 dev-py3k/modules/physics/mechanics/api/printing.html delete mode 100644 dev-py3k/modules/physics/mechanics/examples.html delete mode 100644 dev-py3k/modules/physics/mechanics/index.html delete mode 100644 dev-py3k/modules/physics/mechanics/kane.html delete mode 100644 dev-py3k/modules/physics/mechanics/kinematics.html delete mode 100644 dev-py3k/modules/physics/mechanics/masses.html delete mode 100644 dev-py3k/modules/physics/mechanics/reference.html delete mode 100644 dev-py3k/modules/physics/mechanics/vectors.html delete mode 100644 dev-py3k/modules/physics/paulialgebra.html delete mode 100644 dev-py3k/modules/physics/qho_1d.html delete mode 100644 dev-py3k/modules/physics/quantum/anticommutator.html delete mode 100644 dev-py3k/modules/physics/quantum/cartesian.html delete mode 100644 dev-py3k/modules/physics/quantum/cg.html delete mode 100644 dev-py3k/modules/physics/quantum/circuitplot.html delete mode 100644 dev-py3k/modules/physics/quantum/commutator.html delete mode 100644 dev-py3k/modules/physics/quantum/constants.html delete mode 100644 dev-py3k/modules/physics/quantum/dagger.html delete mode 100644 dev-py3k/modules/physics/quantum/gate.html delete mode 100644 dev-py3k/modules/physics/quantum/grover.html delete mode 100644 dev-py3k/modules/physics/quantum/hilbert.html delete mode 100644 dev-py3k/modules/physics/quantum/index.html delete mode 100644 dev-py3k/modules/physics/quantum/innerproduct.html delete mode 100644 dev-py3k/modules/physics/quantum/operator.html delete mode 100644 dev-py3k/modules/physics/quantum/operatorset.html delete mode 100644 dev-py3k/modules/physics/quantum/piab.html delete mode 100644 dev-py3k/modules/physics/quantum/qapply.html delete mode 100644 dev-py3k/modules/physics/quantum/qft.html delete mode 100644 dev-py3k/modules/physics/quantum/qubit.html delete mode 100644 dev-py3k/modules/physics/quantum/represent.html delete mode 100644 dev-py3k/modules/physics/quantum/shor.html delete mode 100644 dev-py3k/modules/physics/quantum/spin.html delete mode 100644 dev-py3k/modules/physics/quantum/state.html delete mode 100644 dev-py3k/modules/physics/quantum/tensorproduct.html delete mode 100644 dev-py3k/modules/physics/secondquant.html delete mode 100644 dev-py3k/modules/physics/sho.html delete mode 100644 dev-py3k/modules/physics/units.html delete mode 100644 dev-py3k/modules/physics/wigner.html delete mode 100644 dev-py3k/modules/plotting.html delete mode 100644 dev-py3k/modules/polys/agca.html delete mode 100644 dev-py3k/modules/polys/basics.html delete mode 100644 dev-py3k/modules/polys/index.html delete mode 100644 dev-py3k/modules/polys/internals.html delete mode 100644 dev-py3k/modules/polys/literature.html delete mode 100644 dev-py3k/modules/polys/reference.html delete mode 100644 dev-py3k/modules/polys/wester.html delete mode 100644 dev-py3k/modules/printing.html delete mode 100644 dev-py3k/modules/rewriting.html delete mode 100644 dev-py3k/modules/series.html delete mode 100644 dev-py3k/modules/sets.html delete mode 100644 dev-py3k/modules/simplify/hyperexpand.html delete mode 100644 dev-py3k/modules/simplify/simplify.html delete mode 100644 dev-py3k/modules/solvers/ode.html delete mode 100644 dev-py3k/modules/solvers/solvers.html delete mode 100644 dev-py3k/modules/statistics.html delete mode 100644 dev-py3k/modules/stats.html delete mode 100644 dev-py3k/modules/tensor/index.html delete mode 100644 dev-py3k/modules/tensor/index_methods.html delete mode 100644 dev-py3k/modules/tensor/indexed.html delete mode 100644 dev-py3k/modules/utilities/autowrap.html delete mode 100644 dev-py3k/modules/utilities/codegen.html delete mode 100644 dev-py3k/modules/utilities/cythonutils.html delete mode 100644 dev-py3k/modules/utilities/decorator.html delete mode 100644 dev-py3k/modules/utilities/index.html delete mode 100644 dev-py3k/modules/utilities/iterables.html delete mode 100644 dev-py3k/modules/utilities/lambdify.html delete mode 100644 dev-py3k/modules/utilities/memoization.html delete mode 100644 dev-py3k/modules/utilities/misc.html delete mode 100644 dev-py3k/modules/utilities/pkgdata.html delete mode 100644 dev-py3k/modules/utilities/pytest.html delete mode 100644 dev-py3k/modules/utilities/randtest.html delete mode 100644 dev-py3k/modules/utilities/runtests.html delete mode 100644 dev-py3k/modules/utilities/source.html delete mode 100644 dev-py3k/modules/utilities/timeutils.html delete mode 100644 dev-py3k/np-modindex.html delete mode 100644 dev-py3k/objects.inv delete mode 100644 dev-py3k/outreach.html delete mode 100644 dev-py3k/pics/pngview1.png delete mode 100644 dev-py3k/pics/winpdb1.png delete mode 100644 dev-py3k/pics/winpdb2.png delete mode 100644 dev-py3k/py-modindex.html delete mode 100644 dev-py3k/python-comparisons.html delete mode 100644 dev-py3k/search.html delete mode 100644 dev-py3k/searchindex.js delete mode 100644 dev-py3k/tutorial/tutorial.bg.html delete mode 100644 dev-py3k/tutorial/tutorial.cs.html delete mode 100644 dev-py3k/tutorial/tutorial.de.html delete mode 100644 dev-py3k/tutorial/tutorial.en.html delete mode 100644 dev-py3k/tutorial/tutorial.fr.html delete mode 100644 dev-py3k/tutorial/tutorial.pl.html delete mode 100644 dev-py3k/tutorial/tutorial.ru.html delete mode 100644 dev-py3k/tutorial/tutorial.sr.html delete mode 100644 dev-py3k/tutorial/tutorial.zh.html delete mode 100644 dev-py3k/wiki.html diff --git a/0.6.7/index.html b/0.6.7/index.html index b581396dee1..6d9925da0ac 100644 --- a/0.6.7/index.html +++ b/0.6.7/index.html @@ -160,7 +160,6 @@

Docs for other versions

SymPy 0.7.2 (Python 3)

SymPy 0.7.3

SymPy git

-

SymPy git (Python 3)

SymPy 0.7.4

SymPy 0.7.4.1

SymPy 0.7.5

diff --git a/0.7.0/index.html b/0.7.0/index.html index c434007d352..79301ad5c44 100644 --- a/0.7.0/index.html +++ b/0.7.0/index.html @@ -158,7 +158,6 @@

Docs for other versions

SymPy 0.7.2 (Python 3)

SymPy 0.7.3

SymPy git

-

SymPy git (Python 3)

SymPy 0.7.4

SymPy 0.7.4.1

SymPy 0.7.5

diff --git a/0.7.1/index.html b/0.7.1/index.html index a2ddca585d6..ea5697a4265 100644 --- a/0.7.1/index.html +++ b/0.7.1/index.html @@ -160,7 +160,6 @@

Docs for other versions

SymPy 0.7.2 (Python 3)

SymPy 0.7.3

SymPy git

-

SymPy git (Python 3)

SymPy 0.7.4

SymPy 0.7.4.1

SymPy 0.7.5

diff --git a/0.7.2-py3k/index.html b/0.7.2-py3k/index.html index 7e6109b197e..9ecc7456865 100644 --- a/0.7.2-py3k/index.html +++ b/0.7.2-py3k/index.html @@ -195,7 +195,6 @@

Docs for other versions

SymPy 0.7.2 (Python 3)

SymPy 0.7.3

SymPy git

-

SymPy git (Python 3)

SymPy 0.7.4

SymPy 0.7.4.1

SymPy 0.7.5

diff --git a/0.7.2/index.html b/0.7.2/index.html index 780f85cd78b..fefc2ecdf03 100644 --- a/0.7.2/index.html +++ b/0.7.2/index.html @@ -195,7 +195,6 @@

Docs for other versions

SymPy 0.7.2 (Python 3)

SymPy 0.7.3

SymPy git

-

SymPy git (Python 3)

SymPy 0.7.4

SymPy 0.7.4.1

SymPy 0.7.5

diff --git a/0.7.3/index.html b/0.7.3/index.html index dad375a270a..cc1f725c3e3 100644 --- a/0.7.3/index.html +++ b/0.7.3/index.html @@ -192,7 +192,6 @@

Docs for other versions

SymPy 0.7.2 (Python 3)

SymPy 0.7.3

SymPy git

-

SymPy git (Python 3)

SymPy 0.7.4

SymPy 0.7.4.1

SymPy 0.7.5

diff --git a/0.7.4.1/index.html b/0.7.4.1/index.html index 9818b58d326..62f0af9103c 100644 --- a/0.7.4.1/index.html +++ b/0.7.4.1/index.html @@ -195,7 +195,6 @@

Docs for other versions

SymPy 0.7.2 (Python 3)

SymPy 0.7.3

SymPy git

-

SymPy git (Python 3)

SymPy 0.7.4

SymPy 0.7.4.1

SymPy 0.7.5

diff --git a/0.7.4/index.html b/0.7.4/index.html index e27c41344cb..75e1e92ea91 100644 --- a/0.7.4/index.html +++ b/0.7.4/index.html @@ -195,7 +195,6 @@

Docs for other versions

SymPy 0.7.2 (Python 3)

SymPy 0.7.3

SymPy git

-

SymPy git (Python 3)

SymPy 0.7.4

SymPy 0.7.4.1

SymPy 0.7.5

diff --git a/0.7.5/index.html b/0.7.5/index.html index 060a25c3da1..fb31dcba39e 100644 --- a/0.7.5/index.html +++ b/0.7.5/index.html @@ -197,7 +197,6 @@

Docs for other versions

SymPy 0.7.2 (Python 3)

SymPy 0.7.3

SymPy git

-

SymPy git (Python 3)

SymPy 0.7.4

SymPy 0.7.4.1

SymPy 0.7.5

diff --git a/0.7.6/index.html b/0.7.6/index.html index cb019f68032..eff40f80f69 100644 --- a/0.7.6/index.html +++ b/0.7.6/index.html @@ -198,7 +198,6 @@

Docs for other versions

SymPy 0.7.2 (Python 3)

SymPy 0.7.3

SymPy git

-

SymPy git (Python 3)

SymPy 0.7.4

SymPy 0.7.4.1

SymPy 0.7.5

diff --git a/dev-py3k/.buildinfo b/dev-py3k/.buildinfo deleted file mode 100644 index f00466f1c96..00000000000 --- a/dev-py3k/.buildinfo +++ /dev/null @@ -1,4 +0,0 @@ -# Sphinx build info version 1 -# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: f0e0503e156fa11ad2e63ac2d240d0cf -tags: fbb0d17656682115ca4d033fb2f83ba1 diff --git a/dev-py3k/_images/ai.png b/dev-py3k/_images/ai.png deleted file mode 100644 index 96762ce15a054bf8db922d37dbab1eae59069ee8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26596 zcmd43g>`cM@3md3j_jN0bjW2sK8G?D#s)O z-;kZMLX(uIi`4iCg+V9RBgDm6OdoaCG)u_Zgt>%*{Vkp+UcY;~-CD_17P;3&JLW-=HST{r~<4K0mIOXJ%$R_Ufh_ z)(0Ojv9hM0Q^qJto=s>U9v#X0`bz1R=`AUy0Q*KP(lojv&o{Z+lo`}vE-fvQ-dr5~ zjEkj~ST3z-l`}G;IzRY%z!Lh`pGp^vE%yx%EAC8|gF$<#YF99(K~WKdsUU;AQ55I2o|#dyvTBIoe<2;9jR9uxD(+L}F0*bbAHmX?^5ltEI` zXw>)h>(^%Fibqqfwd#fA9dmPrBu2G1%s!^7syMm1xjG#o^X_ZkNVCe$pVI?zCi)`GC;FePbm85* zsErq>ChZz&wU;w)wzosM4C|v$s**XM&&uF}aA{Gm&yvF4qgSRxr@nM7GcILU zK62@7$x0w0fpAE}4Kz$m58h%5YcR4xGn)m~e!(5JNX0|V)gYT&ZyDtHj0-y(?B*aJ zKI|9d$_9n{;}@uB$J)2uPR5f=}^_nedez(&fdJDmPEImMUJLKd-tv}od86~$vH4jESjTe zLc!{X5M{@TBi@V;N55u~nr~6ivz-k_25rufHF^?Fx6I8)1D6v6W51vsL+E%+^eCuD zMc?Bil|5ubHZo@H=H~5>X=^#)GXxzSFP^CDx0Yu3!lAFNXcEgdxooxsnujMnCtJ8*>k95{3A`i8pp)o-B@PP?h1ku4uC4+kWTXZLV7s<- z(li)Sl#lzrxEIJ)Ni8@Q78b1DZVS!bt?7wszFjI$jO?S0;o4^~ zs05BWIjX2HMbRhSPz|IB@@=cXj~jSXXJ;oR%4}wX$j0z!z0Fh)JlEH|eeR29^E`Uk zQvcRCG+P=vk|o%o*rA~vlM~w$K`W^C?JFZn)2C0k;W7ENKOPWg$*9lq7*hmAvZw|$ zil(jUne0oybs)?d%)m6hrAqR&2sa|dEh z5`A%y^;EN{q|C~YP?`rxxFP%9XP!&lOR82ovhs?^0R~OM`g$n=LQP1RD-Nqi=g?V} zpTkjay$Y>Ymjp*sp#1P;O-7}W-Ze3K8~mS$m6h=&qM(vo2edbD0tQlHljX2QvjKRZ zZ)f97*gCfQ_6|(QrjIpSIOaTt`iWtP1+;{JV)S352@4BDN=P`BmxXyNudYKB8Lx&a zF*&9QYSU5D1SG}0Q@#;};i61G_~1VO$Z&Gs0gYEg{$EjzpFc+n4jrrA{wjA7-{=-o zKROC33yf6DwDR6c{Q8lr<3Sdc)Z;lA66J;KCe)NwJ@NYzuF2cb|D><3PV5rn8oY(f z^I_JZbL8fNWMnR8cySi7)%9RBeCz7YAE9`xcfMgXbLOyIv7e*5E{eLY1YerLGj%=1X&#IK5R#l^e=hcVw zQabi0(+(WGtaweTl;dbo7%g6-=c_7fp2eX5?&9I0!#U+_lRSKez$FMehfGoi{W7{< zx#aRobckH=B3r-K9#7EjF~m-gW8eu2;$Qtveg8fxvQKQKL{AULgw-!~#R^`Z)%aF7 zh#>ONVyXomzFW(O{Aa10oSa*fy6}0;HfJI_6V!%V*9Sn4P^@+$w}6bHb`0UN|p<0WB0IBf>$t5PmYRisFTUY2vTP#*k)?1 zWT&|J)Y-cDI0G39T#PrXOeBEEQKiqxrbU!}xZ%PDP98B$C?sPF)~c^ubU9+MyNZRs zCbsI9%Vd5M@R7;D-LV(f>>Kf7!_R=D73<>KR1>YNB+zf0h{^aEsD$Z~=@~#mqn4GM zD9xK4kMHmWYUt49l|1<*I?~Ap-!PCh56VfnO^)5}eW1BgLhJw0dKZ-Z;FFPxe>$M5 z+neMSCVaq^JCjU$vG4%2a^<2L zA2H*LFd9gcOO+271LX2h{`&6r;{US{_+mn?hH;aW`{@_aAWRXg+6ZkmT)UZBTcz;W zf`s~LMynZl>O6wWQ|;@!@6SJHpB#3+Be4C6Yt&ry4X*N^wy=hzEZryMHrBu$9Wf0< zl|(v?`f$~ZX#-nn$le-Ct&I|`2i2NY;&CDt8lF~i&*9SfP3+}0#CaWYk?sHK|5{6x zp8ghUn2$VbMz>VGU0AjquMrMKs-B~&4ig8C%WYeycf|WxrO8mQg(8Alyl+4Sd4k-} z`tRGMs*KLYikPYxpX8!L)}bN9tP%yypcqVQ6xNeY*G4Gz5wVK>%J>Fm~ryDY^MIUS8${ISX);NP(L~8kR{=I}c}gnCN38@*^|?69YWM zjjJzDtqTvs+wdQpC`KT)kH`!_l_me(v!JDN(YtPQOJqBdB8NOYyXALD@|kBDs#@ms zC+G(C8og$q#H6iH-%QR>$$DbsbJfsn=O4Fd>p+A;P5%y9b_9eRc8(4%)ksvxByWkW zf;ylATqnmx>ka0P;6kg(Xz9zb@z+PMUt_uA(iZ%5??T@oavB`XMZrcwMc57yv92z$ zK;B@t{T*}D1ekPYgVu=%A#X`G%gVTx2l##=$&nr%K~-b8GB00Yxpqxc;`C7nN4H*W zv2I;9qW`ZTsHl*;yKQX1#9?loUjYY(@%7e{^x-w4Iz+q-8b?lli3l#t%C*;71$^VL zvA^>yIU9alyuvXK=5HU5PqxlyexQXV#m7sjnQxwXO`#Z|(i)^_D3#q0I&rY)d~ye_ zdp@M--#0sOIw~k2M>wlQ{Ngn+!VV6ezWiK*gAt+zDDb5Q+mj;aRdiLY2g1<>rBO?Q z=8#)DGvXn>_AIIPnh{763mAy)&4&>qSG$woaBqD6bV38gP=6Q18^TmQCu!#nVndeedkzAQV;MxK=HaLP>FeA zzx(H)4N+#z_{_e&V}CE@ElkR#XEvLikN|r--?X$llms-QAE#`)FP} ztv~3eFWTt=UWWK1tfpvM_3WKo(PHf1MxqN_Xmn)kiKC5sJ&=;q*f{Ny<0?)qCnu+& zsR<)@n_&~s3l0u&szY>l*RJ7P71HNWBG2Z>$Cqm*s`FL|qvL*v~-#y{k{h7|I<{w4|Nbpvg%WX~rE=kepb5C(Eg%Qt~wvhqBUj zN7r_y8U-Csi323xX%%-HlwVMg_KRKRJX0J62^1VMBgxCppKvo>V+o{qA5FfnaFfy8 z5PF}dF+3n}nxZtxdmg+}S9Q+CAIOL#B z7li#!T)yb;lfoXn6qhN52rG$SBiz@bX71DcA%{WStfH`~h?qtqimsO1N z`3t;0=T#a=WNxNrX7DE)PgGPoCd&cMaxTBEJ!^E0LYss9aO};mg5*>&f7ay;t+GZ+aPX5b_wup^1pVe)2X zbd%LX-^=-;GiSQN>GAPr!D~D<&Ss9UUcVMQq?kIU%PPQ}vqk|G2x-3ee#lJ*bf zKhtFj?6hGc*$YkHwZ2j zybf8|-c9nx$kT^X-t$K)7ZFnUF^Xz_&hhFPN60riuio@i-?cHahJSq)9QPP z;{*9BbUZY-UHD!vt+po(?|*9!GU+^q&zv|%!>NZYoRXld)g(AtdQ^mRV;@Y*T1IOM zpA=Rt5|g9<@Zzk)6ZIslXqNk8>Ys^eX!IxyS1|DWya2_R$7Hobh57G-)++r&Fh`{* z)nAKIPDA{vS{t5{hJ6%hQ_IXP$GQ_}?+TyaXVl!Tt4bG_QJrzegJ7cr>MjK zAfDXxQGD$qkSza|_^K*T*Rta%{h%V84M|6y?TWC^^RwxD<)|!v@+2?-e{pd_1JAfQ zWIN}lU;3nEJRlqdn?iwz40wsgN&*FN03c1XlatGgRnW%5;EvaL_WJ5tjy|cI-nytW z7H(WCYAbrS7j+G{*wj5G6Yti2ImVQ#HiAJ*Yc@nd$A<2)LA{8eXU)(7{R!U~7+^xH zAehCweiU^qj2Zpl$t)>TK^sNCvMva>ev!7}lN$8A+yGMUk87(dK_}db4EWFVM6WBS zF(TDQTcs*p*q?<<9*4gk!Tm3w0Z}j}mwn?YJdyBmglMGMvj>DJ$(sI{RTt~x)H77w z-B8y$m*$U{u3Fmt&9088*waoW#bCca#`Kp)_!qVI#eBZv4)?dU!e38D<~BZ}{;NCm zK3d|3*cc>XE#M+Prm`|7wHJFhqz?sE6S*c5`utIC#cofD4x?a!Ul$p_e8x?{7N@R! zfnvPOg>5EjW)67E{h z>jBrzuDzQYCKmK6CsyH3z-++!%88Byqv$P-Dtp%8m7<5};aBpYKC;y%f z)DPx{j}MUyjOs37E zL%cV`TVD>WB%E2`ltPJq4hUL<$6O}ZF8kQKKQOqAidqk!#K1Br#8gn?h^@ltbBrK+ za8!y#0v)!xZLBCtk$7}v3&-H8)#dNc*%6qSkk&g2a`AUYq0dT#Gqn|jg$+-!GZl=+F0y)G~NVxUolXI zIoN#Bta2bH{Pvn&v*;j|#$}HxG=0+|CqOae)zS zc3DHvI(1qD<5#y{)hL(3goz)em?Uc7DZ};Eb~is5%aDR%a@(##qqop#;#bcy%$MZM z22!|wojr$0-x32MfW%Y3aPg+$i@rb6qi2uU$~Y8qau6>JJ!=T-&gwEufvn1n7BHJ` z)fH7fAl;0%HuNqhmF8CdRz_RXu;#V@1MK{|#q9-97wxezaW!cLGFObtWwv-fxy;3- zUN`0tMZ!DDOID{qCx@1? zyONEeG(mYIE2^(Zodg8ru)_d{V6@P2VLGY~)xf2VJ=X1k-hp042kle#&GEuGrna5a zgMuecG`|bfAreB}>(E^GfC7;xe6KO{7is;_B`@jbBfnsi$$f2>G;@C}S^#M*mGN!# z@C0Z3tL6RTZ3SidreG2>$@{eq&@ZJ4f#68v4=EbGFp5g5w;GDaHC0dj@1Nwm+uEop z0R|474iOR2(bdN3Z7_zef7GT`39*W5KICwQUKp%~0kWgHJeW5$=Ep*IKekd2o%Q;-mBPlPH*i^o01T# zw!;jl>WEme?mu+*CAerNCKkyQLCh+~8-8jPUq~E)UiLz6ulrBD9xy-9ORmN&RsW>u zreAOEKLAf5pLxrqSWg)tP2h`Yadq#0G)=(dDN{^Lqu*^#O*r`-oLyFsU^jYnGit)Sb4mwk3G}nWKz|a9+6MQ$%W_lRj;Kg%4E0RNa0WQHNih0V zR3sOT|Do*I2i5c<**GRX@_=uTnQCp>uZjJC$<9gagDU)}cHvI*;u{-oVoY2-sNcRR ze`Y!k;W1K%`bzP=&BzQfQi_I`2TIpoxuywWHQv1C02@`dpE*-LU~H8+lbYqVi|bEk zeL1ye9F7s&@zos5@%28*ucJ-c_v7}sO$u(X;a!SS$H}Ze;bAAX$WLR2b(N+ao>mFUF;?L3jpRUM;2!b-_OE9#e^}*%C~$nS-pNLBt&e6il(i~lb!sG z#w)z`xRx#Lm1q>>EPQNM*VL*s@7Gx5BE)T zr@VEKLg%$Cw*ey4+9V6PoH#Pd+KPm@>#DM4ac7!;cHv?4+%kxQEMbB1+4BKZnA)ng zRtGEyZA;i|q6jrCI1IoLaj+>BlWwMe&d^Ngc9`>ll;T(zL>ou@7lXeFhn=}bMu1y? zCAMZuXTD_!UNq>$|G}0R3S< zsxnuPLpT<%p5c>DE(tJwJGIWcJv-CkRj(Iy;fR5o-lDjtW(}L5Hw|Nm6!)mX4Xu$|M7E0GP475-z(-@mDc;Ll4Po! z`c_ij+atn#6K|1YoU8Zq?!v>8zNmOEzU^NP-I9|eaun6HIv7!AB>~XN<{U^1&@ivh zyvWAvS^pAh^X%0BM}vJQ?qpikHd*d?LgV+TCllYR&5<(^`B97VSeW7NbM>`}LZAgm z=6hvsO8dGfu{hrFWkeEd2v`GQ&LRuonLfdjsuhl8E+Rp>KBWNrNmjjdu6t{ zogY#?1wpPf<)u2Lns!r&6M}xbM75z$u1^i`XZE=SoNx4|wk+ zam-xZ2FbHBrcE_qP7;<=){|TYzH8U6ZwS7oSQn6dGzY|KH1c^s^yR6-SS?dmXb;EN ze(FE)?oep(?mc??d>;!!k_-0}JdnC7eL)6S5n(r%NbS6iecSmNS(phMm(+#4Gag!# z5{06d-r3KVCN-Sk_nCOxsO7OqJdmJsB7#tlRs}uyTUzY8kpsXxf;{KcQ zCltnNhn1pbL6Nx(O+*J1#Z}MhRN#ApdmD^zo7+Q!>*@k=2fpR^+8A zIz7}dd3)e$gzn08o}HA~mlhGT*3ZrszP_=sfn+(-G}u9KX;jFf+86hBhEVUy2J@{k zKSYGXe({6fh}l`=_rs&a`t%?QBrzeUL`(oIrKhJiBPV=lZ*MSz6krT{I~Hsky8Plz zAsx9IkoDEec1IA^)@`Qa4@;%ThrAL-qa<`1*00BN>yj}8^#(}>0ac?k-_CYX2OgOb zrYjTi=7dg@;no!1=C~4wHRRjgrAgs?aIhe(n>#8y=gZMb-%BiGDKjhwo0!?rLtW~g*`Z>yRGyYvU zL5Q;!;h#$cG($V?RJjzragk@4X$=&Vt+YG9qQc>7!vpMO z@gKeo3aVcf9JDC@JauP#`zgfks9}jeP})Vg3o8;GBaXIE42QI|N%Vm%(!`fEbE|F= zO|z+?sAyv1Eb^FF13)`Xx*7*L8oshz1ld2e-e+bo29!-`m6ve(eJWMD8O8DVqEJLf z1;e#75yMB6``U)UPx6HEh5U=7@gSL{dy}_;4GRqS_rnsDXqbzg}Ib%2Op6Y{gR3S#jQ3g%k0)V3lt6}lnPy{ zw=u%SK!UJExPUE(5!vR8Py-c1K2{W4T+0kUm0OewmX^qZR@JJ#CA;u9j|bAZjg(FE z^L0jet~*VTS>6AHCl_U9bSWCG>sasqlh8g zOp=b9GDtC`VIUZ4BRBJCj+!ICuL{Cq;)g$Jw+al7ASm1+nL6sKX9a<54ostHxV`iw z@7B}X{qN*cZBDJOp^529UjPEcZ~rbX@df|Uqlw$mTv}fZ{3}d1v)F`o9bb^6 zI=c#;qSQCQR*Qao2D80;U3n`A33a#$Ll;NNweb#O!S6Tc@~ zmRmVo*u2=KsTc>-`XV=9GDMtij<#37K6dz5E;3@oQ#nc)sxo}YOD;W63Gev1wia@$ znF>bCFf|u#H!2CXJY;VN>vXsOsB3+w_;qHG=JRZ6;ujI$vK4{>kF}~TY`?E?gRJA^ z;jLXI|15d$jFfs?nF+zFKDIHq*b!6H{m3y9=*KU+v@DBf%^z`r%)U21LqA^GS09!u zM3TM)U9MAJ-vQlCG3PG4>+gS;)Ieu*c%(4rb%hBPUbS?UzPc4D7`&=)X^Dl78KX#O z#oCfQ180x`x|aL%d#9upL2_<7!{Ag~Tmt%~4+0atIrgTklRP6^3tYQbh+KK<2;|w> z1^giu3)!ubXk8WbP8U&FTnuWAwA}u@K!W0PMiNud{p;I*aKdw)`0t2v z%HANgK)Lg`;TLR8WznJCY6h8AvLVLaK_Ne6bHd}}Q(M636tYO?Cl{>qP`wqoyZP+^GjLd!3UpDlg5^lLr+)Z}InDGlZNE7tja*MHJHkED?mu~GC*({St%EQiJKe< z`dkzWt6DJ+RoI%fBNG#oXoCpI1bDI*d;mO^~@B(>=8IdbTje*=$pW=ax zW>|-gKM>K~7#hJaS05pRhDJz8=y2YnU-no(WF0(o^bPTxNvcgOHxCvk!;<5-#rzFd z`X+k!ZmH8cXO>cA;=$iAmp)wJ;ls)|-#NI9n}~0i^^YdRnwv34bAmF_P|yW=X$_#t zo3(*R@$Se&m@Yc+B9a6S&$GczsH$n`gqxdI6fv1nbC>$8fF_{uq@@0FTH)wpDd$FF z!Kwk6{XEAV6%}IV>~duPzP*pU%3)eA`1(X&a8#()3fz1zJ&*nC=e;IrwZF{apulMZ z6(2ONqOG8pL~lBStTrBjyFQ#5RndC=nXM zdLaOg*D$9jXn_$B=vv%et^LiaE~Az75=Mol3|3~WG6^xl;T`L=rr?~4hE5S93_8b^ zV1b$%SO7*+o;g2ozjh}H9IMo_ZEK`HGCu-}+1OVbYe9X=i*5J$M(6Ed12xWvgvx4a zU1j>GIdj#_^O9U9PbU*vNL*HXWW<ot4ugW9bGmx~Lt0^{QFws&@_UUXwdt$1C0s`1yNfeZ~piJ1^;T(OauNNO4A z3gZQ@RAx~|2n3;dn?L>X>N7J+GxRzi!*Ni9x_|akba(5Re9s9QAIVl!P#Qnnx^^v@ zS%!54!D#*Hyv+y6H=0H1flXqegA+3J095H>9<9s>17IphUKd%#aseq$Tf;pisM2bJ z;nMR)OiYf(!*lef{h`@$f&1P=vyEXWmGo26rFy%V;n-9%SBKr{gzL24p0Si$>4#%$ zG|w{}w(JX?8krC<7)-Cm0*A)VL*DOv#?oW|^M;3MSq!Dfi(i*V?X3&sGAJ94jTJ?9OYX050C11(9=5NrrmwQ!=}P4uukstSTOp+R4(EQpnEd}FR!MzNOWq z_UxHppP1LXZ|!k3`TE7@;u}{tNqS0(T6(lpgSW5m^q`=YPiLud>MIvpTl$+n#9$|$ zyQ!(ElC-!^oogh_QBhGxg90P)k`l~@pMGE9FM;kXe~Gy$5hZ0*>GO(QySe)Rm&eK- zi{*%@4UdB&y>bT1l)Q1-AS%g(4fLj>@P%5n?pPA9EC>$zVa^asF=)7xXyJE!U|`#w zP3AFmdML&i7*bjLZeO&?CFfp{K-A|w%gwy6$OXF*Zra-~G16Q9_nFc-(=4%zbxyY-BEm?0^K~XmW3so`1>-YHa|7d^n;V5oTjtkTyj~kX zIT84onvoRU(tDBXYHbs}BdIoVz&X`gXEP=4V=`WVdb)$w)w^@_6{~%BrdaRGWB^f^ zO_j}zg--b`hLYu={JHPAhTx^iuH6*%8u#oKa=^jWIFZr{2dOb)@=?H!rcFVqH0N7flUxQCU5ebLW)!P50v74>!Fm|60Va@g-yt}(ADlV=%f*1bH z6i&JMh%J91Gde2xxdYel2B-1DW36nqFs%Suh6&8+zf8@bDwdhtv%OW?^@^YEqT^Xq>f0Wwr}Y^ zpzBHo=fUH_ry1<@vLPBqPsfRX-tj#^b=bPyx4P}I80x#Jyu-xF|B?XY*llJ0 z^$kBqDn@_u-H&bUUQ^@Nb>le9EbC1o%!KLaKs!?vWG=PEjVVEzJa?#%=Y&tsnoxE>)LK}44*1naUnNr)&M%s5&y9z<*)uk6{#IzBt%_GUb0 zs3Qn!VFAoX5G-Cbu_4g##thB7&~HfV2Y}TNl9oDEiehQ(jEeO@(vrXE$@$)kn9;ml zU4Wig3>mL5@jsmcB9_^n0YLD%J{6#)Zm^cmf7lr|jt+OWl*k-nT0g}Legc_q_4U5I zyfo{MBq=H@Q@GflKiV#>0NQ_^KXq{B<=U%Rftq(i+eMt>(fyr-YVIOeepv~_nHwR# zmXHFZ&EQ3VJGUT0FME)aZE&Q}KR8sKQpw(66vAD}Dj3xcZR+Xo52&aJ$Hax!Lg&&K z>~Q< zl3l($YCql5so11j)Fph|ruOh_K7osa1D}b<5=vK3Pg?7{fX;aQct7O5_T{4DoD}%d z5^Am8&ifRMT{P@C0>O6Y7&*@S2bD5;NkgpO7o=A*q|~ax(^IP$ZpbbEZ2{-;Vq&l8 z8ti#D>-2t|ky-yL(u_rOORO);;18-V)jg7awYOEr28cMZuT4Nr#Vy53-#37i<)2j+ zIU-Xi(jSNd1z1P`!_qkLan}RHY)lDmvz%OeF|@%(NnOe{l7rv2{U!44#m|ql#$POs z+G>dS0#PY^t-^%E^z-~k&u94})8H>~6J*T?Qch*EQ&PIyUWY*L1n3+GzTF%YX8n-4 zAH1<|)>41=q~>NgC5(tx=NTAaoJ@IbX;oL7_z^0}Jt}HB@z5s}f+)x! z?E=K!_a;}hY(ItzWUWv6aHS}kO|tPzB~8I!vo&GAK6OX+_oT^W2YVj9h{E&zk4BIW zkSc{uJ=;JD+sqZ+r%6flMNXUx{D+Kt?^u~qhI5y zY!WCvh+1moamH)@a>Inf(#m+rwb49llBlYe*0skA-lPZ2kHj#7|Lm+7&};mB&mXcC)Ko@-OA(!-~h+@QiN=nH=A5KI(A} zul+ZDcu0b2mA_&k2P*MEmJ&N==PD!=6P*J&G#E5PrI;7=%*-P+*m4;Epw;)$1vQ~D z7EU}(aIgk#d>RD}S|c6ei~D#=X-kn16JDB2Q;+5;nd!9eY)=iPrio#!iZLraSGyA( zo#Ev(O|axKI-e^?i#^a?RKHhA%J574PYCX3%IJvb@%Hwfx+|{5t{`c^ypXTYNyv4A zgAp=;0qgr}zE-%f_2n+Fwsq=0LA5@Ngwf7Q`mFanpz4wq@VrrEP1J>lCw1p z`3E5Z7y$18@>u)2!wbjv6|!uiMOF(L&7s?GOw`@Q^WyUcW21;T5lCpq%->+7+?%-2 zk5HvYRdza=lA|czD{(SbbCrE@+I^&gL+MHlR-C#T_m~2ec^e;EES=7MQlhn=kpT4g z_Lp&^H+G{8Zw{Ba29^T;Xa4|ECg?FPE2EV&jFd{JQkMRPei4~~K&K%y#lmv0ArMjx z2@HJtrA%yXO><3W>JucTaXBy!FJ_{`n5fQabD}un@QfMe=Fx}+8JeZ^K{2R@vok>6 zOQi=NC8{>2l&~k>C%y4yXZy;qoXXQ|w2| z;aPs-_pfp_l`WrQn~^0<+FN~VUki|S$cFR*QGot8L7j|MAK4%nFEc*gjL<4fx>gxR z`o_L8<5hRM{aE>ABuQiC3EJ+!kFZ9BXmn(aDH2wJA4Hv5MP3Ffwy9ULyb+7BXsf{1v6tG%7E!Gc5_Y4Xq6e z9GYKD$P|A=9#*4}O$5xxXL!W0e#lDa-J`=T?6ozQLVOaG(@py5q+O3qdh*^ML3MVI z7rQt4TBcmAMdzC_0La+&11EcqG=+)^w3A^_OYT2H7a$3AfuByA?N!_gxh$=?!Zp8- zN8U6$0U^Y;hCa>IED&Jii?OQg;j}ZuEIh07tcH73K7T%qU_dNPe@XpOJen6m!ofx; zxaA^x+C$W5hl)M$3jd^+U`oum<*|KNoOG`Gp{akqG=*D=mov#fINV2I+6@ET!MM3U z-(MhrQ5!^XdUsn(EnMc9O$c-jJm!Y4%Rr(MGy2Y2Nzj^Rs#z+`>yh=0)t&;#aSux- z${|9&{r-+MmZ%d^!rkJf={Xz#cDWI8E9;6zad0B~1J|G6Vt^MQ)c8IL`b-Ka&YMw z+oQsOEH045a(tM%Fx0K0M%C&?{(0>)IjICTw`H4hy}~mRS7)y+=5*Z<;S49IBBKg> zQv;3yB|HP&og{T)!zG3PPFD;Fv){t%Upi8W$xUunny1hd0_w#&KhLD*o+YwM8LziH zJ!!eX^kcs{yePCuO6T+trXLs>h~grv|Jns%KsI=K7AnLlR<-qm1=RlWQ<7CL7B9%u z_fd8lt)Fs(!$GrruAxC>*QS-1fZqwuym+16lb^sq-ayLe%VAxHc=G?w`veo?5xSsZ zkJ};Lt^t+NIVv4P5jqBa2%(iDh)TLPC_di&6`@stZEOignl3wNeJT=1%Ff4%0MgIhnBvva#QUcitNB+xwWE+rBY5T?~S{2_J5kx|9-8U;^Hzh23)aBl|2c8%n4{ z#tTuGV%E2-!T+DbQBp?N7#?dDQziQJdDzae@;lKagj=-e@R;FxJ}nB=qu;*&HBv3=bE~xyFXO2yFOr#>^*my?4m_-D7a9Y`;oW_*=oBR;%IH!W@TH6ImKDMa$D5_zkU zAU2}8T+x>J?D(*ALf|pmDsGx^);xOq;KHKZ!NO^`Q(|(0=)am`wE8S37eM+7HDH;u zo^i9zrg*J;qPV_^o}(zfJ#68|Q0y7fw7!g*us0IEalg4=IRv8o5ew4_j0~D^!oz=M zZ#xa0>rX<_Tv}xk8r+MCFlH?YOw#+q-ZcVz&fRMVK8;j+N)Cqr4;O%N!981H{7X5#7QDRTdl25(@8<>L!+rybI{MD{x67qNzyobd zL_Gc*n*uBw2&1`~YHN%mAkk?wbKGSB`@Nq zk9@|fqipaf#Ll;Twm!d-Pax?Vc?jmIVzv9DIZ7j-fu!dxucr3t55?OvB-q4+cqkrrU0uceGr|Pu z5PciN4)*P_hrn484t963HZe3L1U!&B*MRvB@Tw(H%E=Jq>k6F#f>ZAUZ~zT&3)9Qb zWytjOQ7TEB0sT3J;MieC;9}9h4%x^2f%Api!`Cvh^#KJa{RZoa9qrp zjh(cxrEW6VMwYX*2o!kaqmQxTGq0xJWTxZ*q2+qw7e2LWjRNwo zyYo)~e;H}CFX&>3!sNVXL}r{RBZ1L6F@ZCl(dxxs482_#ow%q0Nrq9#-(wAcZGev; zc7>XWjBM0|X$ZcdFFTdWMokK(l+Sb_QKVqwCN})6@Jg{=<5CQwfMMxZ_7nAvTnKEW z<{KX0`|SHeKkJSRBe0GA(|s16mOlel_dh-a2kb!XET!al8Ufc~Mz}dLskD6l${=So z%X|7mEM_q4Ism6gRmcPDA~Y5g3@bo4u$L%D;5oW$pYpyyNu6(*azyq_s9lh1hI}QR)YO%^3Rzh3HSrux@>&#C}iy=3GkFe z9uo(S9dJI1X_%Pe5MzDteBe`Ly~ye!6UyJ(HgHfb3g`yB1~5eTn9GP-HZ0UBg^!Ie ztJYfHaNKEgcq#n(2@v?kNscZL{DJw(UfKx+hji98rV6;YBAN3$CD*@mezm*wIp|UH zIvP3Sqin$Vr-S-y(8R0a>O|+nNGmnD(NENN0uMcCm*bzyN6g0*9C_UPmTU+s$;!Uu zz6MNGn(%{*mI-I5dF9qFi)NDc>M|3_A0f}<5gmb5=XBz~tB~2sXU{NXCe>)IQpGS} z=rFgwoo^)Vy=eE>MMBYvZ;rbbI@VV~?h7h!zpcbQ5%F^Sra~9HPSyRrx|IBf8{dmS z6rMc>BG%&H|B1msxep~D2*D58h?{7VGrM0HuQMStAkXqtL}G4l&E_Cwd5lrA-lMi& zLN8hIwa1spl--z0Cox{8Ms)3!aD6?+cuf)%ycoXX!w+M}M^PjBg(>^P) zNe1BXvN5jIJsfs1fLHL>KM1(%1*YC?YB2O+vEhq8dR2!C@FDY5GEM}wgu(9laW0|F zb1%E_btZf*9sA1vMm?*;f{7FEG`f7{6>#1&mye zCTS1m{5wnG85aD)OHKeg#GZg4&;knd9=oCTdU&%q5bo|eae(9o(9eJK zcnpMW@-fx4(CHi>hne9=c0Fx=~7`W9Sg1OQahlq@;)LF6r)+9!Y6Q zkq!ZAhHjAVZ}Z(g@BO^T@y_3U?6uddwXVL-bEPDyKFSF;_zO3^pm$d5&C`Ow#By0a zvef+PkrR5nJ%yO}b7?t!#KI;QHuA&{ft#4HjO)^)X=!9Rd3mVKZ_X$R!RJ6q|0|?byVkt4yj+q#^y(N;&a1+CQs{NMk`its z^oXQndl_hFyNSObpH2fAE17x=geiMBWiz0w{#dCCww#WUjvN58tU|c0;e_1oEC+6; zI%(=6gdlU>4^Ux@v|~FvJ59HTEv)T6SF0#>R@-Z1Rr-Q@^}8E2_RAs_df%Sa*VhAx zzSv&VR!Iplh{|>$XBXwd^Jl!aat(fonL)JNi4}!Wn105`?tivYp|;4a&IYr`oycCI ztDXw`0(c26ihUs{yha1LFOGra&fK)sY;r^Nh;d1PBvX?|WnsmV(ce=*fVbV$#J|7r zZ4$N@_m)!J-25eoRI%2?HaeMAH@~%Y!Nkzr9n5Pp>sP7UXfl}03i6wpQlDgC+LHx< zh+Sjs1>(NzVQX8^+Zd`6Z@;xI?E`rknU!;)3~wx0+2{bC>;BFpI{^`bs;X;F%IDA2 zQIRaSa}7w$%*Wp^?P1@tM!z@yAlf_9jYVk|F>=~p;BY#T-r`dkom%r}qv{-S671eQ>XZFCl+Ba?dxMFfB+~U}tm`Gr=*i1;} zeTF9&T-8p`M;rnkXhJ*P8nxIQO6yk05=i18#vIz2EJ+K-AmT(qLK*^!;i+J!P4-yf z3*vY0-kCZq%tB4CJASOiKA{j>+)h0o0Qo;nd4+eJ!9Em@&wwbrj93t7?BzvV{6XHh zU;mqgP$*_xb^>t}Sw8`yU%1Xx=65n9ANkv1rfRnM4x?k_)YMp$)Ct+k@gt|>T(u}L zI9%M(l{1j9bKz#V@)@Vv)lonGe>2z6ta{qKTSj(1TB2AaODFNYJ!o`|#V3CTf5(bl zBndPc3ayjzfeOZHsRtPc#6F5M2iygWb-eeza?eoAvnF;BEQ6?U3t7!zKQ?1DspIQWcBfH@RTf*lec={?mZZU&fH_e@H=fO>sV&wussn}EdD`_YnwAL(Z3E8 zm{=lB0UrWcAz3wD_Iu(r(qp?-w~#^05x zG|X!I_vehrHWJVA$GwNEgD(3ptnl8bB`)^jHDY2rQuZU?)8o!!6q!QzsN5uUWoB!Z zld0hx5NJ?7gm!A#0SDj7fHhDDmeoUKdIDejgwX5c8=KhKlNe_?>Thgq@*lcQy-Fkryq}T+wd@4vx}e1j^IZM4r+hNTan(BB+nf<6!^*>OqOm%geB245$CbhVfy3r zL)+oOTi6G$hJ%zu2YNHnxl=0EH<=j?a!7Z^|N zb30i%Cc{BIP1UE~GHkN_sV=6U*!QT>oW=ZBHm_eJ0W=wqL%S7;dzUBNz$wb;mY2@$ z<^l9Puk!fxqT7j8X=Rk3dyY=c2^PN5pRy*sfh7bP4BS>D`dAk1ib!Svl-!JX*_ms| zUK8qfDR244$FdtL34NkHLgbX=WG_9Z9yq{)X0C_)Gd~P0H0xG>!2;J+lW{4avW#u9 znfd(`A*Y{CnDRRt&V9fOH&$^)Z>+vbs!(}_yp?gT6&)35)c}&$JXzQmoSt2W@;s-p zLMxehnyZ_oYU3$5+MnDW2K+WOiq};Y&H_7qA(}d*pp^~EQ4nZJ z@QRX{!4 zs^05$7uqF^@qA@rt;OOVL0=!-JG1H>#3%?A3Rzrn}`sDyBl zfqt0UgZ|_2AnC}+q#o#pH-;j#OH(Ar>f33{+l1iQKjL2YMs)?F8g2UR1%Sh`n3sgF zUd!1fuMOaL{-`50=--ZrJlRn~7Li`5^tN9GP#3b`6LA-Po+UvUm;GvN?<7~=shYJj ztI&R(<7S6IX0>O}a;@hG*l^rQK*xvvZZEcmGVpQ8M?|8ACKP}~HlnRWlf`%DVOg}l z5$xyB$3@-t`4c!-m&@YY1a`j9f*Mo!rd>Qppf5T{SNUr8{=62?>_H?1e*kNFI5WX} z!5`1jEceZsm!ND?=Yb%H9)gK8$)?}4k>VX-kx2|1wAHjYPyO(h&#}4|Y8h;mvm7B6 zNY#l1bG%n!PY-TuE@l(jQTm-?Ro8TN@( zh_=90{&czO@E#E!F78dby7E1{U&wM&)ioTCKB9BNa)M`xKl_vLF z^G=C%n3G5R=#=sD{G*%sLzo7QL8P>*ysrhtD(H6Zc!rc}dpxi~NqX#g>#T28i1(7g z5@csO1=Ptcp$RWh^*#%VO*U#5)HtHc`>4&xQ1s*i1HI~A5?unO6xoZ{uRtr3-iQGa z%%u!>UGD14~SmI^{k zl-IlhHQ+(W8eCn9XA$ zzrJNDUfQJ=#*h7ZE+o3*u0Ex)Qr}h$7Ac042#8qEG_wJBAXR46_toB8C9-McdbfF> z%(25Pg(H&bgnk1>fhq#S@7q`7+hmh77idCWmiX22&sKgC=1QXMGT4Tc+=@g~cTiV= z+mMk$;J<7OZmlE!a``pu=!0?Rea9hU+{(<)^fv}@eg5qAY%QpWrwCH~$VFgua?o$g z8~JIqCiqFCmnFXE_gt%Zr{oog9%6j_$F58rVtPdu{ccD*GLX>lZOv{sM}5D#Gx|}G z=;Y%YHIH~BC9m)7Q3IYn?#F`*%CJ?*3`mxc?EF%jZ8koPDqyFrxBxi0EXoxG!)a_+ z2IW~qZuu3(n5F_T;(&laQ@Eq4#2+t~K|n%$iS*<-XysR-Ca_tB zs{?WSbh8;yqO2}K@P$EOpq2FM#-QIXwGXcpfYZJS)RLc0DX25PK>p3iCWgq&Zc3e@ zioRwvn*LBTRdZUx#9xMms?Rk|WyBa4ul%qDynHUVjwjOYKT04rYjbs0J2b35*%P*6 zs+cWGJ|6R}fZAN3H{yBqgDltKQrm(4Jf5X9w{2E*E(P~4RSr94_;@&fwkLUq!PWbT zh54^Dep%99@LcFQpwYa9H@^5#?XJoCnN?b2G?YeE`OH4X&N8)i= zn1p5_5e~+(l~lP?^cz81RvVO2v5VO(006yehf)bwbiSdy2Ufqb3>W^9*Mqx_;$lSV z3bD|{8r~KP|4AMIVAiI_G@SwBpUE8p5-GvkTS8KFC}3LNAWE+^G|ii>(F7$3{?)S6 zGev4mQb_2j68X4{R^Sj&=Ox#Z%ETM7>RlMCHdw2TCPTfTn-xl7(n!Z>Y_Wy0MP^R~Fr6v{ZHd zLmP!K=}dbB3zk;1e{bXijgaz?oz~-vhelO3TTjZj;=OM}MJ3v4sA4a?>Y(;WfM5hG zA?lP&@{GJ*s2|(dywsGhhJ{XwQcsBzC||h8FN*`#2ssdr|C683_Mob~ZLrv~*pOUi zuz+dSB94sa>MKhfH**Tm#{PfpVMb`s=pp1e5jvZZO$ijzd^EX$B11j4nBQzHeMG`o z#@q!%Xeco96qhn^rxX!7qq%FnSGWk&N5!*5iN3LqY{BX_4W&DfOCR7PjEs%-QkdMl z6^&eZ`TY*tq{J&np!c^fJ&Q2KY<*hb&Yr{OO=SBFz>mV^sG9SS_r0G+W!e)96k zkJyP*c6haw+H0yOF4YF5jJ5T~OyXlX(Rsb90dUBXWpl)d*z{)o`l>W4?ih-&usr@0Ix8%jikU!J7w5BFR}8M3FCJotUE z*5#$ZgC3+yb*xU{0}nAfL<7PvZUSZ zVU8Sf5=*2CL@n2WTXbbsi8X=!QF#X!2fR2tnw9zXhwZv!OvBw0Uo`cgMBKkf_G<2W z96>AgCo+itqj9~9Hqj5YJ#m=rHDLEZ5UVMbO2-%`?II<0k<;x2owWcqi)pchx||yW z$KHK*{X_?aPbI7uOo|U1t5#FLDOqNo|;D+hM?Ce zm2^iyS;mCR1YHe(N4q~}gnF2^NvY(GPOjy#;(P~k+&251^ly$^k(|sO5);SJ=?~@+ zL_`)wIl5Vm8=ubik5|Jz5%?D=eP5-sP9E#sF2q$cEkKWOc&fA#glI2hK%%J3R?%2K zMvAz$d+OFEVU-e!Np&WQA*o@NT5sriT*wLhWlS+Y)|tVq$ zGb>Z}mBM=M-hn;2)$m+_HTZscG75vZ#BOORk?9WyL&;8k6lNJ{bJ!Y+KcSW%`eyC6 zl#MFcSEiSU@aLk=xIx~I&4KoX;jFUQ5lFwmRTpMvAC%C}Z{~-MgWV{?aC5cxXFRjp zR?(&2tB%?Q#J(bkz!=U#GsF-D`)LA;?K?edQBF`x?8>q~IxZ;{gSAJTg2qcH#avu6Q!j@3kO1+4ZBLq>M=8e2|l-; zg(^4YIS}3Rj(;ZJVE%Fyp;?YfU32iuXQkxodUKT=@x;EQKQlxRKOv1LcQ(hPTKpa~ zTL!{!VX1HS0y7fugMxktznUAAOi3Ahi-A!04HD1)GL9**~uwjL5`0%Q>=}52t^)$Enn!k)@W^*0vq> zK8>aa3(CjmscW`>KEFpk1fL`r0>=BP`(YAFSb{E`? zq4xOe{)O7-cpQ3`dV6`>N_r&ZpG(xTadGiyzckC(Zp#-t;$_5tG7gN1eP#6(d9Vm4 zD4f-Y@_uXYOl6E<@bfy*D9;F-KvPyy~?)cY!h$ zKoC4#8CXbvwI#{{F7G%Pl0TfL)GzmFq_6X;rkAC?)#3%6`sDgLj78x^8g|JVl5>jb zawgPZZIS~s!FGDkmhzjfP#IVCYj;yNFxlF%Ql+UCE@97Mk9oY|U-endECcuM1|R*9LGozEr-zwDUi11Q##G! z$zZy~V)IVB%PpdBH_45cuf5l^2G>G%kn@)E6g(YC5pFTnt*d9{(a`Ad8OvVLu2~`{ zpeRod?NmzI!~MB%9PX&7=nU&+1HOWNjaXSGGiwkb83?M>&ZFhmS_+#G5KBq^`PPY! zmQV@!DWGO|?ow5npPpu1>-QWvV~k|EYYzhLi)d$= z<8ar7&W1A_M>jStadUZC)l+ppQ3_0*rGMKQBOYw|LQ)UOFPd4vb8_!GMY~8mGCum& zR+b5@djINfH|8E+lp*G1b)}_+lC&iMPNXEVrIeJ!Hvh2kYfh~u*&<A1QH|jGl@w#>{I;OwB3*3qxsRFi{Zm#bt)Xj0bZ{@mpHxCVYYu48&>ntvraXV zLB~@OD@{&ag}}mkH2!^h(KabcyMWm5dQNF!k%)o7DMIr4uCSb(e>8hUM9^5BPm?>` z;ie*ZdGCnrh+aLcI5@)ReqB{%YjiL=1RY~zrc%b<@(`G^Cm1jmCu^8`nR+rza;s{R zDD&5XD?`4cHAa*0lny>YLpf%1E*sKLQCHXhY+Gfi3@W>Gsh5q2?cCiFjifr*%Al0J z1UxZG!}ZRmi;s`$Yf6IjY!{12$qs-*MlpH1BVbA}F(?j=d%^_~PIjclN{KWO@rz@4tT{GUx!w@^nFn1<;bAo52wzT54eP$@3i9J1rnz!&oSM^EEA0=jTZUJz}YqO)PNAx-x9po(})^6I+Ln(T#>oGO=&&ea60gF5qO>rX{HTyXZ|asH~IZ@${nNm-|f0S1SI1(0kuFgExxkqI86y^_r>5#MfM> zPl9*(dojp7G~SCkjWx%Hrk*j0<_ObxW~&AD0)6vGCS8=LNhZxQ%c>cPwDSad=zeeB z)7S`SV|jmyla4=qQoY=&#KRzzc82$E?L&^*n7mTH?)De0DK^6zjMT+G$m0I=ETh=Y zgWYB}>?rBeS4=tqtKfeYz!H7z#S6v3L-k@lvDfNn`(KP)bFet_@wc}&4tJGqVkg4M zDEs?~M2eg9NSEJwrx6RqsT?Jr;znN?pfL=x=O<_Y)?Gu79+8+w&cNbsHB`mHw|Ei9 zw{-4HS=rA|-`ME{{2vSeRb~tCXdU%mFJbFA07AdiW8coSm7|=KJn!~!w^f!E1qmb` zM~m8dk@@OLlJybJ?ygTsgdprQRr=OWcIUgldU|{DC@6-{H4>UwhFRDXpHZ<9ErT>F z)v|n%XTLd>>{?q!<7IWG8gvbC0ycb>N6g~G2M&+3ZQknTcJB-r%w);)NEl#H6$8{^ zzC_`b2-8|}78>_(DNS0u_zC6qS8U#c^Of2K$;}g22 zdvx&!U{)+HM*ciW@=$rhJu^N1jD!U3$F=2quvK(vb@kDiTOA6pnWi4V5<`fy0Py6O z>S~nSJeO*S;2OQ`1e4gk02k8JoeMyrkMrXc;}T>Ii(b|Q02>w-7sqF3D*<&t7Z(=* zi3VF+GsThe7o)BrbHy5)<^-^a=s@7|`gLD_KT5*D8{qm0N=g6>>HY-IY`GcE7AC{N z!Ku>wCIQr0+uPe+&*6Ry4;K{{24xF-el;6-=91yXP0z|2-5rWUwrRBw5DEg=#?-^b z7E{i&*&1U6;G&D#$jI?SYrp&3%UaucUQ#kLG;D11@PH-uhFmB#ZXki-bia1=tK~SY z=bmMZBx@}f5#ca+?_j=>%Vk&H5oqZvFR!(pOk``n3eat|g96MNz$F#@uKTHrdP7rF zQ#B52(t7&(4z8|!lH#H`x++>)Km6bj_AAHX4Gj$)oty>+2J-Be+Y?h#=7)P_F6+}y z*7|FG?md7!`jkM(l^yT`!BHo8fv50-lKW@U3z8YsL16Z-&M($RYNXVBFp9qjayK`E0OXh%@Y z;N97rNYYeLz~r(RSzjMW;O6FLpK4v3!k*o0ZwOjg&@0yZowA@oKu9>=Xm5y&Ovg96*rcSSko)r)u;;}qpjN6dW%;Fml+6#Q$kzbmt-ON*=Y0m3 zE}DfUC6jHQZu&=iBaDoUoo1X+wD{fG2ohWJF`xNS?X|fmRHIQs=c{yTZ{@8%LVL zg$5of0nO78>x*v7AJ%`w*(@~0Az?f(B};gTJBtP#QliF1#f*Ri{O4n0)VRb1c)3XM z{++qN7jRJ#k);qV0l8vl1ja^$_ZP>5)A|4JppL=&iy5i>JJ0{))eT?ClnVW0LfH-w P@ROBNdQ&A~6!8B5qiZSz diff --git a/dev-py3k/_images/ai_c.png b/dev-py3k/_images/ai_c.png deleted file mode 100644 index 5963ed08695378bd5492263f02c7a374eb656510..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61953 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 uu}uOpQdETq6QWqs%2K*1w%QDgc diff --git a/dev-py3k/_images/ber.png b/dev-py3k/_images/ber.png deleted file mode 100644 index 2646482bd3b438ca1c0fcc392088d13efaf085ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19451 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

t0OkCv9b7u>cUIvb_&r>PEIhzXnT8k zC=|M@!2vUj7P)?iwOky*Hut%@7x-gW3xI`f9L!c5e_ql#{Ny@4;&u&y4sjn|u(PRE zM@{uWvwW%y{S%jTPg%{$`D4WDEEX$#upTxI`3-pST%U0`@=x7gKc128h%}!FFjZ<^ zUGVuLreKkS!G#y7-bF^i8DI+tzy$!J>e$(5`*K2gYNw(4`avs_xg+5NxnL7XZiJec zJ6^$nOk}mTA_DX05(v-l6BC_+f~xwS3l%^Ph~4&M?JG;7#;F=|(NW~ywJ(d!)N^&d zNT&Lm_;*?7%wBqLFvxSz5vZg%Apty!)tLMrHp-UOir<3O-Pf&san%(l&MY4cx`M)>cugNaw XugWD~ZAbw0E*VFd+ldE12jKn&^PNG= diff --git a/dev-py3k/_images/besseli.png b/dev-py3k/_images/besseli.png deleted file mode 100644 index 1aae4b354db9ed3226e9ad3ef394b764662d2c98..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19101 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 diff --git a/dev-py3k/_images/besselj_c.png b/dev-py3k/_images/besselj_c.png deleted file mode 100644 index 507634b31c3e56e9bda1c6819d846731a39f519d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37422 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 diff --git a/dev-py3k/_images/besselk.png b/dev-py3k/_images/besselk.png deleted file mode 100644 index 19501d1fe0d4e88d5041e314b04fb3a50999ed41..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15252 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 diff --git a/dev-py3k/_images/bessely.png b/dev-py3k/_images/bessely.png deleted file mode 100644 index 0766d7c50e7725cce302c1c21688a083896b05c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19658 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 diff --git a/dev-py3k/_images/bi.png b/dev-py3k/_images/bi.png deleted file mode 100644 index b8800d7fdc48c7f7a89a95f3644e8201a91f04b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25211 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≈Z`jX|JhTy(=+_ioBZ9JAaTQo$gLr@ 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 diff --git a/dev-py3k/_images/bi_c.png b/dev-py3k/_images/bi_c.png deleted file mode 100644 index cbd17badae2117be6b05bdc0eee8f32596cc466c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 67342 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

_f-Pks@ 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 ReRT{#;OXk;vd$@?2>|I-TDSlJ diff --git a/dev-py3k/_images/chebyt.png b/dev-py3k/_images/chebyt.png deleted file mode 100644 index ce78cecc25022efbef0aa69a0fd84f2b909f9575..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33025 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 MiOY*sis<|Oe}>9x`2YX_ diff --git a/dev-py3k/_images/chebyu.png b/dev-py3k/_images/chebyu.png deleted file mode 100644 index 591bb0e6fbf279ab6620a41d870fdcbb3d2553be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22495 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®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 bDf%z}%x-Psf+LTW7=Xaj)z4*}Q$iB}?>c*+ diff --git a/dev-py3k/_images/coulombf_c.png b/dev-py3k/_images/coulombf_c.png deleted file mode 100644 index bc34dffcd48483f924a525c2e3e4d5db3217ed2a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39897 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 diff --git a/dev-py3k/_images/coulombg.png b/dev-py3k/_images/coulombg.png deleted file mode 100644 index bc6c02c0df220c3ea1a925990cb7c69568cb1db9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34197 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 WFXxDyp$`Q3B`c{YQ6pv)`2PT1LJtc7 diff --git a/dev-py3k/_images/cplot.png b/dev-py3k/_images/cplot.png deleted file mode 100644 index 1b8c261875d428b3abef462a795119eafca8305b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 42543 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 Xn`zJ)#Fhf!x+T!jG|(u!WfS^8y8iW@ diff --git a/dev-py3k/_images/ellipk.png b/dev-py3k/_images/ellipk.png deleted file mode 100644 index e44bbee2ac320fcfcd808f89c311614032cf8a4d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12183 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 E4>c~V8vpBYnNXidFD(_ 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 diff --git a/dev-py3k/_images/ex_rd.svg b/dev-py3k/_images/ex_rd.svg deleted file mode 100644 index 3b9422b60ab..00000000000 --- a/dev-py3k/_images/ex_rd.svg +++ /dev/null @@ -1,237 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - R - N - nz - nx - ny - rx - ry - rz - - - - - - - - diff --git a/dev-py3k/_images/featured-downloads.png b/dev-py3k/_images/featured-downloads.png deleted file mode 100644 index db50c63ed3fb3c3360c067158b1a5e1f05ecad0c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4470 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 diff --git a/dev-py3k/_images/gi_c.png b/dev-py3k/_images/gi_c.png deleted file mode 100644 index d627aeb7f9a3c429a13053b893b4a96db3dca683..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57729 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

  • Fo>e9s9=Ab(@?i0qd0< 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≶ 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 LbEm1Mu95!%lO#i( diff --git a/dev-py3k/_images/hankel2.png b/dev-py3k/_images/hankel2.png deleted file mode 100644 index f3691442c5f0dfbe9932bc5d9fedf9f2b006edd1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27100 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

    fMRStTH4nwxc6jewEhlHFR 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 diff --git a/dev-py3k/_images/hankel2_c.png b/dev-py3k/_images/hankel2_c.png deleted file mode 100644 index 4eb83ba2d078b800ead1ae1adf72798f5b49b6d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35840 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 diff --git a/dev-py3k/_images/hi_c.png b/dev-py3k/_images/hi_c.png deleted file mode 100644 index bb580ee9d8ac6f2f30ccb8504deff1e06b0022c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47358 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 diff --git a/dev-py3k/_images/kin_1.svg b/dev-py3k/_images/kin_1.svg deleted file mode 100644 index 6d77b001397..00000000000 --- a/dev-py3k/_images/kin_1.svg +++ /dev/null @@ -1,211 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - P - - O - N - rOP - - - diff --git a/dev-py3k/_images/kin_1pt.svg b/dev-py3k/_images/kin_1pt.svg deleted file mode 100644 index 0e23a3cffd6..00000000000 --- a/dev-py3k/_images/kin_1pt.svg +++ /dev/null @@ -1,216 +0,0 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - B - N - Ofixed in N - Pnot fixed in B - Sfixed in B - - - - - diff --git a/dev-py3k/_images/kin_2.svg b/dev-py3k/_images/kin_2.svg deleted file mode 100644 index 8efcae90f46..00000000000 --- a/dev-py3k/_images/kin_2.svg +++ /dev/null @@ -1,317 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - N - nx - ny - nz - - - - B - O - P - bz - by - bx - - - - - - rOP - - - diff --git a/dev-py3k/_images/kin_2pt.svg b/dev-py3k/_images/kin_2pt.svg deleted file mode 100644 index 8cf25b21a4f..00000000000 --- a/dev-py3k/_images/kin_2pt.svg +++ /dev/null @@ -1,198 +0,0 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - N - B - - - Ofixed in N - Pfixed in B - Sfixed in B - - - diff --git a/dev-py3k/_images/kin_3.svg b/dev-py3k/_images/kin_3.svg deleted file mode 100644 index a369b405208..00000000000 --- a/dev-py3k/_images/kin_3.svg +++ /dev/null @@ -1,291 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - nx - ny - bx - by - cx - cy - P - Q - O - - N - B - C - - - - diff --git a/dev-py3k/_images/kin_4.svg b/dev-py3k/_images/kin_4.svg deleted file mode 100644 index 2dcef8fd0f3..00000000000 --- a/dev-py3k/_images/kin_4.svg +++ /dev/null @@ -1,413 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - O - P - Q - nx - nz - ny - q1 - q2 - cx - cy - cz - C - N - - - - - - - - diff --git a/dev-py3k/_images/kin_angvel1.svg b/dev-py3k/_images/kin_angvel1.svg deleted file mode 100644 index 3d382759ad6..00000000000 --- a/dev-py3k/_images/kin_angvel1.svg +++ /dev/null @@ -1,238 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - B - N - ny - nx - nz - - nx - - - - - NwB=q nx - - - - diff --git a/dev-py3k/_images/kin_angvel2.svg b/dev-py3k/_images/kin_angvel2.svg deleted file mode 100644 index f3325749ff3..00000000000 --- a/dev-py3k/_images/kin_angvel2.svg +++ /dev/null @@ -1,541 +0,0 @@ - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - nx - ny - nz - q1 - q2 - q3 - - ax - bz - cy - - - - - - - - - - - - - - A - - B - - C - D - N - - diff --git a/dev-py3k/_images/kin_angvel3.svg b/dev-py3k/_images/kin_angvel3.svg deleted file mode 100644 index 40aa2d3a48a..00000000000 --- a/dev-py3k/_images/kin_angvel3.svg +++ /dev/null @@ -1,319 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - p1 - w1 - w2 - p2 - p3 - w3 - - - - - - - - - - diff --git a/dev-py3k/_images/kin_rolling.svg b/dev-py3k/_images/kin_rolling.svg deleted file mode 100644 index d4451032e79..00000000000 --- a/dev-py3k/_images/kin_rolling.svg +++ /dev/null @@ -1,261 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - A - B - - - - - - - - - - - - N - P - Pa is the contact point on body A - Pb is the contact point on body B - Rolling without slip iff: - NvPa = NvPb - - - - diff --git a/dev-py3k/_images/kleinj.png b/dev-py3k/_images/kleinj.png deleted file mode 100644 index 098c79adacffe5b59bb167925d48035506e83351..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 77369 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

    L$h?soD`&H5MDFj*m)atO`7n70tj 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 ZiqN|Q(HZq;fNN$MJYD@<);T3K0RVHJ+!6o) diff --git a/dev-py3k/_images/lambertw.png b/dev-py3k/_images/lambertw.png deleted file mode 100644 index 0d96a6265814e0d9ccfdeeeaef7ad5047d5bd9b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15860 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

    H{g+-u5vrM<4t~ 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≪M5 zJmU%S4cz3VVai)44$fUM&1E4Ta(-VrRzoCIcGp zS914Xs!AumP^-G@F%t6i%5wCs00?zAN0GczjVE)k*`&IF{+*bu8oBAPy(uwdn4h+m z1g#nHSvl(2*W0aBY_Fccv4V@(>6*x8@bd@1|T!<4%e-a%8DMJ~he zR6BY8>hk>e(yn9Y8J><*@3~9ZRZ;D}$%L;!yK`G_^`^+S=jx;-BqYq^*@>~SvE3yj zBO~Q*IAFXGSX)OYI+%yy$;Ew}?OIdI6^75{3Z|Bz6`}VGUY6PEFJ?>41 zf7>QkMJG6xJm(PhX>7D=BW08G6t=yrGY% zv@jS9|9JbU9Q;)}KA;rqu_D(G^(Z#^@V=GIs;byXX353l^NBhSq4p@&%peYw8APXEUr^WzTa#qVra zb?!%t`nekj= z7z`$)%}OEQs=Fsil81-K3O`(Y5+frYss{9?2HlBoUwX($F*rM`%}v`Xrlp+GpL`rL zw05vDh7baO=!zHEn5YBcpSr@-sM2fb^jC1>?^tgkgkKd{{I^vo6yf~-X+mP+ z;zXql-W*@PdbPH?$`PlryFS8a2R#hUuSU!U$E(7eoMMi*=inzh3yz~Qva)2KlnsrH zq+?z$U$2pY=#evZ9tH*mqT3*0MnmbZh4^du2|gee`2Pw1`nfvxziBt@fAmN&`y>55 sXCAt$&!08((^X%XX^`CfG~m{)xW79UMgnR0)BFh3Rq-evWy_EM2df-*N&o-= diff --git a/dev-py3k/_images/legendre.png b/dev-py3k/_images/legendre.png deleted file mode 100644 index a02e14ea8e46ca7fd375a7105a3a63fc6583159f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28034 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 diff --git a/dev-py3k/_images/lommels1.png b/dev-py3k/_images/lommels1.png deleted file mode 100644 index 555a7158397061076440c0ff77b7f05bf5b3f07d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26654 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 z zqRVzyZY|zrZmo+5GZ=BG&ohFyH-HPQwSB6TJ=D%ehN|=1jn7fI8PIai6)Wm#=1I=~ zVzy!ALH4CX?wFBgZ6D@{QC6L#q@w$@A%?v8>Z{ z^Cg5nsE*{a*44m!8!nNLMrt8XYQC9ybQH1+jIa`Gc5H(2FoS zsbCn8%#VvAbCp2@>90h1-0(XWnVp}P%vUj4X>e!?d=`Y9Z^Q$XB4(pD!gk*qNe2g( z28R{I)K=kbb&zsjS18gchvk6f0J;VArfPKEcOP|gkLIa88U+BDY9RyhgMVIIzW zfb*)~ZK7JG`piM-n_$NG1fr6cPCMa@OZv7Po4x#Llonai@rbPOhhpM*!+`HA@)g1E zVM5TB(8N=kl9At`Zfw>~&?!=hY6)j(q2D9CI|s?9M)}LB!R#=&L5o3eXuv!d<6l}n z7#O6i*BSxf6J7kwRxh?ZjhaB?$6NSLsEGP_PbB;!WXVtfUL4A&TlP!ec)iAZqbAf+ zWkaLqKp>Ylh06;&qagL$r~+c=QK0wkfgGj_7hF#j+JMxCu={65YdPselnkM_;Y1LS zST)}DN4Dx5Cm;#(`WT_9x%yXho3P2T#wI9t@c=#e+}+sy>MTMC+5xZGF?{kY|B?P7 zOyDOx0b;|w;Tbh|_=q&Iyqd(7lB6lXFN@fN5&*YBN0U<;fJ}q zTlD*lnB^z6V$lH zy8Ww*)uX)$sRJ#V=PLszUEFl!iiAH&tn>AeZUMBNa} z%=KmVXm)>NKszn|UxRmq5=Xva+d+7Fipk$9P1Wv?AA1*77ErkNYSaw*q z^FTbi0c){yx;(rBqs?T@KuwKd_#auemQ1^yo74WEAvn9k z`d_$h9Jy(efztm7gX;B!+_PtflKPGN$FHyZ%95r{J931q1nU2-bzaygJ)~m)-=>&o zBea`z!3p1OhSIa}I5^cgt_hB23lU6BP5l_z8*9MB#dV*@rRf6_drm|UBE$aEFQ&LC z;q9PF)KE*S>^bA?q*{A<=Daj`Z zFU78xwl?9mn|b{)(M$kOWaN8915=FB2=I41I?4}LgJI!SG&E+vv?_zZf0q;q>h&DX z8ThIHYY0OBX9@8T!s2-hGB!3|?Fe`e!)5&j0fA@5NJ}dMlp9D4Q!D3|$t{(yQpa%M zhxeZj`A(0E(lgv#Brvx=7O*_bfFFGa$i<~?jzyUr1Y5Pt7_ng~1J5#mRpOrGW-_C$OH{Rl& zN`7nx3IG(CeOUr-m}F#*=M0{L1M%bo>+OE|Q#S0*o8n5DyklFHcnUnRYd}#0 zfS3REXa=-1#_)BN5nkUWopMt@-uqS5%Hk$?+aPQf+N*egTE!WKaH= zmlt{l23Q!IdOe|u{)>M%H|-!UH{f0|EiHn*y}kUq^;D&J?(Xi~MgWc)sFcz*G7?jj zlgaPA2@^*q5)J%nG)rJ+eWscTfIk2V`P%F2zF-z+y*gwdY2Mk*t>+9V$DgMOp*Dox<^A&^~W^X*tR=+JS>a5eoeo*jE zBaQ6Y;-~jV7~68@M@P|~1X{3@=ls9-@ky+5=;wi1Vp=&2@KTUbmaddE`TBnV9G^e; diff --git a/dev-py3k/_images/lommels2.png b/dev-py3k/_images/lommels2.png deleted file mode 100644 index 4b496808e9545921663e341efea6142944f644ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17657 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 N002ovPDHLkV1iY&pI-m~ diff --git a/dev-py3k/_images/pngview1.png b/dev-py3k/_images/pngview1.png deleted file mode 100644 index fa151694bfa218003a4d79a2b7edf02ffc9f4943..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3251 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 BF3SJ_ diff --git a/dev-py3k/_images/simp_rot.svg b/dev-py3k/_images/simp_rot.svg deleted file mode 100644 index 8fc9982c30e..00000000000 --- a/dev-py3k/_images/simp_rot.svg +++ /dev/null @@ -1,255 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - A - B - - - θ - θ - azbz - ax - bx - ay - by - - - - - - - - - - diff --git a/dev-py3k/_images/spherharm40.png b/dev-py3k/_images/spherharm40.png deleted file mode 100644 index b0a0cc7c994dad22f1324c102f1742cfb068d24c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23638 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 aVvE!jrbnl0X*pmFj;E@qp@5RJ4E-M@p%%UX diff --git a/dev-py3k/_images/spherharm41.png b/dev-py3k/_images/spherharm41.png deleted file mode 100644 index 25ff1e9fcde75bfd4ed9c839b29f1f4cac6abcc9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37882 zcmbSyWmH>V^d+uEg1dy`?(V^hQ`}nIp-|i@B{(TiiWCh6iWYY&NN{)e7MB8rAoG6n zpZPT3CTqO~c{l6ox%Zy4_dfA@I%))XG#c6)i-Ll6_}_#2LNH$w1%(Mk1EOdYn0NF!Adqf$u`i;so$Dj( zO#&?y4%UkS;$U{NxDW4$nFO)ynBM8VHjh-ekYvsv&T%P{B11<-qe6dg8-qIiObp8| zp~EMgxAM5AIW!_KM3g0nwHj{aVv>I;6a0L1Y;1+PdQ8@L?M>J>(<3We30bkqcgGq3nP4YHxxXfnUnNIsZQ@Nyr;xh>WX=G38OUvZ622 zgLZbx)HgH?4h{X>zkGW=ArpvD2O2C|h4#IMl;|HE9Q^kW806~ahI)1zdSxRiDY>(= zllJ-ZF{~Mz2=_bC%VVYIB+PV#q=|iz@82D%iqNo9JtKLBb$By8Gz6BG?m%@3=>>*> z4)nhf*R${03~A*N;)kzgjF?g@t(xwEeVi z=e2*Numk=5A?W_KV0FO!d0Sgse(f=H$p%mnFEPLHujG$Leoh|>mgoI{J&;Z0EnROg zfBvs9xc9Z7QjuPI@5)Sb;DCmBy&K=vcvouH-TwcS!yO&%NpdcmxVgCp$H!~e2eck8 zC#cVGg1J?~V$c?@wzJz%ZEFLSgWhR4RPzp}`}F&GrR zVx74K#hYqhZv3!KyT1>;4}YTAUBB)!Gc&ug$%Z&v9~8umMXF6$fDB;SAi_9Y3^ZV^ z^eio(DOg!qp~t$sI*w6)K4trCadFY*k44M7j|NXwXg540L>_=0THu-wjW`ojp%G!A z{R@>#NJyZ`2o_gqHkK5ZGu~F!ItddVm4as7VOzh2$2I?8`OTMNkBw42esp=u!NK7i zpi=ZQ%YO?oI9S2%v0V16dv1YY@VS!PAUHnj&S|zeu9}iTF+d+4{VC=L6R?{99>Om! zFVh7_g8zkfV;!uKWFYU2UkzL~2~VDSXde`e@3V-D&sV0b3~hk?C}WnxapHcFk&!hb z15Z#2J$l#amNXegUy`(Ypx!(hd&K?P#7fsU!Bbsg$IYeRE`9WnJ;P~FER@Zr)&^|T z(85B?)03~SuMZd*T9iViR=r_tAe>;V%b708J?}1y)7!=gToIAnpe|$2Clfv9Ekan| z)-}@nvj+MhGxMeH2u1c8kyn_d_)53G-PBmG?Q@Y+H;dd}V13*JF+Trr&dZ)$i!>0S zsWw5x#>RTcSxbExX$w1ES+R_CjDh)we1Tl|i7-UKBQEx$VRLfoWw!Pf<9|vY9scVIlj&An+s{G7h&MLM>g|X&GtAfVLcS@t z-2IED6b6goCNiWV;tx@3p)iUEE=n`Ja2pdvwpY#f2O=;NmQ9m{!GFcS3baeZLY_@6 zhVlv6x*%Wa$qbb#O93HkQHORG*o?e)&7=htbI=)%l=iFmHrXhymS`Pgl!^VzS@|t? zQmb9Ywo+SrJDUzs`lqQs45FELl)x4P%Eu4NfI5gsY)9>;+vUoqtVIY>j7?0~AAWU;KKR1%dGvR8cW1qPkxR+E-%{{Y{Z$Vb zC1zn3$_E8%6ZA>_+0=tLt*qQApLiO-sP`)`Pvq6o*KXUe@yR<*T2g3qDuHjfjJ+90r&gH|!{%nu*-{<;zSE>h~}Gdt-xR zgM%&?`tvLvM7(eB-7F)ga=^KMEDP5^S$<={5=NfUq9j4Ueus0stf=2pOc^J*B09LV zo|7yVD^1ZWB=746zw3Hi!b|_OrG0&T+gY}4o@3PzDH~rmG(qAbG-JaR=4OIOyzEE32ACJm%!}0hk&FTQ$k}!4na0N3_bxeo8L3nt0n7%P^lmHjg z<0T0W#(})}q;unFL9VmN(jPN}e0t>V}P< z-z%}u;EMyAJHhW4O^ZeY@-0(@7-&~WHkzcWuV0H>TO}N}J@I9tQ(~?i9eb4xQNJ6B zQPdCU%*lUXo%EJ?p{R)JyEn3lV9`ath%AZn{EoN5*>FtN@W97}WBE*mkPeM!=iw%z zP`BVSVmMJS;Dq)WokZPW;Rm%PhMbrt^`j>P9UYxY>BpDG#s&rkQ#+EbuC7$_;WDhO ztQnb^f54SQ2{&TDIzDf9q=cs1ci+e>(!N*7N_V(6-j-Qla z_weAL{Oeb=zneXKIfoJ#2;AQ+Gw7g88e0}`o-><5B5zUfi9Gr|+HjCI5b?oCLn+)IzHqYPQgjm4)lFkeayIqj2n{oQ{IpJ+e_NcfW8FsXEY)>k<7~P>q2kepiuCd z_YP%pOKwD>e+ZYgXzGJGyZF1|ELowN>glCrsNQ?1gj_0aTd7IV?Nv4Kp+DkOVRfTe zi=G}}K0`UdRQjIyoj=Nb!^oCbV7T)p7_xMOCXeZe{wwOrGW@n_o5xP64Cl(RI$W*D;qF2v@X}h0`tl9V{ z!I;@f6W?D1;%Ad;jOW1fZ9hMR&B+~bhq0f>G}AOTJE;m#_3pd17OQmHwWCLh^$3ID zToU^K`&#y_9O|`bPm#Trc*oz3K>HfW`u+Sn!`>|Q_bi0~$Q)^uJ|b}peN5FK!TUY) zyL=>R6noUK$TkIDJro`k+ub3r-*eR#Zv0(+ZjJEcW}_9jheg{LeBhQhPaat7m%Z_SuC;c4X2yF=a$nEfToW))7S$wM!11Q@9T7y~v1-rg zc{ui33AkUsRM2}Jk8()SH6UJ3kS9@GYiFqtR zdcu_>i@sNXUytNYyTHW6Mn|lf;>bJ;O`iGdi-@x(T!Es!FGIgUksS@cRlps30LBZ&UpKjtMiXFPOWpb!MNecCZIQj+L|6f7@p`ieDE_S7SOGs>6AjMZ<4H zPI(o2@9EC4z?0KyqcF@+yp>nY1*y=GXLb1AnYT1aIFF%_F=N=`47zro&{ljPAAt}o`xMFpFoxya0pFE5xB^{>D{`P>zT~!JNt6J5M|w9 zqyw%C?k9+&NupBu;GcY#V_UODgBE}kn4!1MfJ%xS}QU{7v z{~9F}emm@&Eq+S>o~WfeV82ANl)l!^!Xm@J9i-N7PIs)*Ze^Io{M!iBK!AegawyD> z)-t|AVc~dvM5IrAYQMuRPcVXh5UG{cK8D7J(u}^QLjI2F&4VoXQiDUwPI`kEvbD=W zd56Biu_3#G4TmF{hv9gYb84w;1x;=}x|LP$`<*N7EZo#PD!IIgcv&v<$7z39fp+-N zJ^5G2Bj<(?kT=cYZO80W#^ass!NQu^(&Sf*RMDNkt)lRZ8V%mb{Fzmk)J6AHeHThL z6!n;O8^jDgc0eT7DEkk*$k3u!_&wbO!14}h zRn0eaUy0B7YX%izXOfpnz-huE|BNvr9mm9NC%>b@7&rAdMP(?&2A&ba!EG;c zRupuq!(rRXP_ZviudJjX_$w~e_43LOgCS@K6nuT8t1*ZIxC%v$ejL)rjpFjRgZ$Kj zuhld)u_n8a#P6R`4~}FDx12Sua_a)YMKVKGkuzyq=!W%)htXu}Ss1mpiF-Zys zfs){T573WV6*Ib3Slk0i6^Q%4-M+?mp`_Og*Tm%`3*3Y zkog&AQy=TMHY*yq3DHcECnFoJeLE6(see97zS~2L_n9JsdBJB6(2NM8 zB;_By*!r*`Hv4RMRmllt&bFsg(1fBeRvK&l;Ju}?gk|5XiA{R1oA$G1&yFtUeI4N4x7g3&3qS6_w=sWj&hJUyh$*C%-$MYp@>O?5hu=;Mo+wlVMR*jPV zl`s6~Jd>LpnZcAXrxP>Dc)!oy#pKQeDTnRIC*|}v)uWpkhT;9;Ncw1;HsXp~?lk^W z<)|rcJ+UErXrPxWB&?||g>rQAT8(re1`#)=)cshdKy6|jB%78qaQZQ4ae(DGB?o0W z;d-SEfUV#G+ApxC;!1NAz~Y^p-~&xmg`1|G-QBnUp8M`*X0h0a64iy}+^9s=NQ2s} z30|l~$)w3!MKLUgm6!e|wuh}G-1*oSeK^wswK1m5T)INum?756mF&CFDz2n5SYHXt zeEoMZ9Gj7qRkm>u3q(4XOKzu3qLy)J1-W|5GnFu~90BNtPeLm)bHh}utO##Ve8LdH zryy9bX3VlRj`jX*#&FuepZ9#iNEjK&ownfq!H@kI0!vB$z=Vz?1?pvSz9G>DKoysH zipk`U8J^p28jWGjLgarr%krXSeoH~V+n{BkQiAI-!}7wDm(u3I!><|1WbUb8i>GPz zp)?hLn$&$BDao6we&XWrLF`R!#}*09Xjs)Hy_8&~XfY`p3_j3W85$~UXz0HF#x(ZM zhb`w5R<4h>f-b&c+LbSUPGJp%bX<7PIrF#12K|nFK_ps!LCH)95%H^tgsQwT6+^g( zyoKk~iF@hlnvVZywc3K&ZxW1SRW9_%iod&C^wOWBvkeXpiGQrTQ*WZI=jPI%!&C=# zM;U9h5rN!zU1jgMGj7lG7bCDCFWg2S#hE)^lWJDbM%{bnnr9^%`N|no=VM{`@0@-?94jq?AO}*mKdL?8qXqeXYjn2WZ)AKq4t71S1 zE<>ak%hjWr;7bj@;^1O^Vg&C|Y3kw^7pKzM<3Ce*o{w)-hdi+>FE?F2ISf1Px{)|2 zFB$a`B!hSUv>bl{zvxkecj7p-=0V9WSAXNO5dE9891q!jL$vHi0@T1Ad+x+*W zB4ks{pH}I3Tsodk-Lch|b7of)S64%xXQO?UW$-JI3;Q6HVh`RXZzMob1;aA1Pz=pZ zJW@ZqkIO7R&CFZ+qJjrv)%E3)5fx;x-6y@_i5R`2_$xhZmdb6xOC%?>mqDQ-xVGe3 znhETuWn#u#`5keh9~57H52r$x9DR8dUFA@AND_CyIgaM|TOH;}`U2yy)3-UG*T>ve z`L4EiC^WNv$7KI!L=koRGw~)vTy(^f66)0D=#^<|=SpHp{jlkC}h_y_}%dnn)hxVP|JA1!C=+?K*!fc&$_TW-Mi~?W#TG z{r2R=OPdl~D=~jw#nQ3W)GBN}%8m$TR$Olayl0Q3oa6cG+VUv~Z{B+GYf*VGB|J$+ zm>*6Vv@~^bAeGFG9(m;Jom=a>))|yUA3_2il@dX%QmFhM8Z9J-2U9s`BwMKJ3t~zx zeq?sj_q_3fZLJgSb8caaw2i9t%8J z4cOpT%?(C>sQvd-rS8nD-NY zo&T{pq0AYRZ;jmdABgv@^tU@U;7pr$IM=U*$50H0Ito|zVBbyuHh#;Myt_v92R^&K zm1d63cdGgIh3sQ)f#FlO4tDD?2zgVlo6$_90ltQ2InEFM9w{`dC->W)HGAXhJDroq zODKOo0Bu!L*Isw(CK6oMRpWFzWhtdshCk3;l*JWyZafu=;_<6hhDXG!pUO4xz7Jh} zRo>#qS}$CzUg5+NORKa)p};lwm_H6vW0?NxmIwj6cN{k(5`MqGKlLD%OjXNAPO8&K z#JS^lES&z}Fx)l!`~1w1?75|jHBreR$2+ulS;Y$FVLwp@>>^MQC3SV>kC`w2xj!fM z&|f;{HPR?k3E~xsF7&{(6`*pjmtdKtfC!;eS6g{pgmj4Iwy`4*tD{ z2sjm5Q_nNW2V;S)a`D6PgLlT24uZWdk(3<&uCeXWGr-Z;%unrTuttOl{Ih>>#tQDdMX0+&FWY z7kQ;~rNTodX5JWPPk1dg3uMmsWUJdhH)yV9DP17o>}i-NiFjs4*oSy*MRD+YrYhEd zwRgy`?$I5?g-@iyb^j`37Np4Oita7*ik7F*VXnzBM9!c}cH~rr$Fwd|zr=hj`DEIu zF36^adR@~uX;?pD<3&c$IkxXu1B}-&i6?=Z_?vtr8`;FW1`sAOzbJCm>3aB=1}HBf zNB46)PugjuWli_p5XC@O(=PxYa(p$q$a3;t{~Tg%SYA=_>i*w(Mn;C7v2p*-U^?o* z`^)jRhwJ74xUbV2-vRo7=T~=A){5*)?%ja5X_4wxfTC1H450b{9v+i|>^&^aPi~Mw z$YT?I#X*0L80O*72-HOy?81%pAn1<2^4D`Vi1EwsJ^E{Bs|cDlP{jsttvr!c9O|a+ z>cu0*u&{iICwD)2T!%Y$gx7ADm*_a_Yis8P%0($W1MQutOJ=d?u3(9Z7ss<;Ys>jg zVl?t=GLHyu*DAU`DQ4`})7ERc7ho4q2lVDSp$?J!iJkGY@vT8AEj5lWqnv8G%vF&o zJ-$I6dO(&Vd5LlA+!uxNLoYM{tS)DCHXH~VsHOdR48Od1wcVf0WAWuAzT>DKpGfW> z)6IH>Jhw?r713XId>7q`>RTqbd2I9bKe2+g(%mmp`idRZpZZI8fhdR+T(+w&$*0_V zNn9@)o}3_+q8Kz2<#5pDbCS zb_jvzVtWkoPO?Y(fq$z%R|2kE0N5Rzx?nH3r!>fZm|(eGMSwY!sj1#_m^Nc^jz=)( zGC|39Q}xrIbU~XBjCT^<(a@(*2o%XoQl;>YLU_PB2C)*Zk|tCuh$Lf*SQ!U`A=-Pp z_OM|q|3y+v#v3&Zq#bgLrANdDSqLVpEca3(?fb)Fhb|JdkhiD8 z(RJbmS#xlHW9}0Eh)Uj#@k#Ws`YzA-19p!aJ^a6gQCHurVH zW#u~^qbQREBOmgV)7)6-qB(=ua0#u!}cr3ZF~j z$pXjzMqEtPW11xNQP{;quMa+WoqM#a-2XFgQoh)U0;vu&9(->TLsnj`bd`3b&G+|W z!#>5$s`A0cV;&_rxySikMOprk7JpYSCZ(mq&BK~}))753SJ zaB*>c2J?o(3J}H<>W)S8jZo4ghqTHo?tD(hsi6hS z7u?n>_R<{vtS2sR4;~KPYV+N%4}YM^i0dS&*eOZF4u5{pXQPBVkpN< zt7l^?R-*Nmc{B0+Qof;fb-#8`wDPYVBCbYo-|`1Ozs&3muP!Y-vHUj&Kw}8KJ(y{& ztu}Asa@Zfmq0lf=EThqG5^0k2b!tTQ;QzVl9Y&Ns=Qji>5AH9rjB&kG87{h@&SK14 zVbm2z^srbTHt?7)Xfs8**D4Avy65Mg%8W&+>vJYJnPwJj{fQ| zhX=KjTW7y?1@@`l-=E{<;clY9KTwvOyqO0(bV}E=JA!9BDeK>^Xc?-EwlXMniM5wR z=Ub~!TD)yWBpp-aDdpm%jWWm4P2G>4+QZZ7vz{$frTRhk1T^q}KO<^y0}dB@|0uIu zv#Fe7#+&fzFS75Udxj7p<_&S^Cw`^5A7vcNuXw;aPt5YS$`E^B*oQ9kikYCju0;Ly z6kTG)kIb1axvVP0pyhK@a*tv7vH5#U%h2<_D?7O@XbJJ3R1qY-N53qFHd{f7dxjEZ z;rf(Dz-}qh04?B$Vdxk=>&dEAe%SRS?@55k zMd&o>PiTZ}wCAsL>EN~s`=8eW000HBGPSGzBLFU&I^KdSdyuO9Jb=mI2RF>Mn+$ZZ z2ogq$Y!dDAWD(gg6??+m^0DiR?FJe3ugI2REhnhFmQ|B4r`Z^9);n? z{qk_VXC&e_uG*-Lc;HTF)7}#Axy8KW;8mM_8}$49sQ`i9%?P`cviI;)lPzgcUZim< z@@vFJq{@9i5@rPLX~LbwnWP;+2AvdQ4`V;CAL{0mKbgLmUn(0XhSYbZEp?NMzB!0O z$Rs2fCg^RP@Mdg3u#jOSiAbgL1~5W3SA0jleb?w%PD;=>F0%cd3cGupNRmZy-*~uj z5)0~)ZA`irIn*7Hquap5C5+&<1FeN@@YCX?2{8wB6r<`*^u7!*H5{Wiy3e$glboKG7oUZ0 zaPK@Y#2vwCAV*?-Ue(mU9EHWa6HMKSYf#D^7wa8*Uk6DnQUw%ceCL6G>R^#yiaSck zM(B1k?Ax&7!~RJlzE-HTtr{OfEVoVFQ-m=wX|W6cc36xwJxnLne*6)G6m(w@K#M{F zq!|R=Q=mU=FP1KQ3TmoVi||1raJInBlRT&897jC&20sg~g5L|LJx}3$Z97tc;KutR zB~d^8luvzfOfmK86=X(v;ATM+m#Ni=;eLGn=_#2l-Y%cEolM*8f z?Y4DZI773^UsW=kF4~u7-O-3{XtuD;S9zt*OpT*`0h(kIyx4-piM} zt{Iu;bUwYHBnH_WTv9ggWsjDo)(Bv$sr~#is!}rP&o4ep4OK0<>a_$uPrkm$(}wXM zBLzKJK@H&$COJrSDic}~Q&aFW*B_GTFT0Z!Klb9mH7{Xa%cRw^5{y@?pWeOsT%a=7 zD=|atA~Ce`3HtR`N8~huA!t4zdw!1v^N|27-7(?HnWBkRVYWQO_!pG=XhawZdDCr1Wq_dt1~xY+3tTFxrb z;LGZ|rs=Z|IVL~VH6&U_UwklY9F25WX2RE6sCa%{^QBPSYi$Hjt)@#>*w|8F+!z&YUZ8UVwbYw* z7@B3=yN%dQ45FE2T{xLm!fzNCb#1@-K1#@Q%m<6i1b9J^nJ;cC+6CgQk=7&I%Vnde0r zIqJ?#2N4*QH0Y_U{ZjWBQ{8z>2wvgV|9-3srQ=r~x~7AFuU2^@R*$P%g!|e8zEj4` zAAfum58COVa?t~I-xT`M?!<}d zUqXwi?f{Oc;|>KQL9yHjt1S@rrWsTOIG2=7iA6~j5$45jNlGGo( zpd#a<0gmVg_(G%=fA}Xzqx;ZyEFv6gp2B@c3kx@*pc!Q>T08Q4NXF5yrO}l*93uHp zuPNz*zL@d-Ww3;MHK=2)V3mI|vw2!Sz9p+rddq8)!X87BJ(sweo;V(2zAId2Allem z{naBY8=-{dDRaGGG$!dp0`AjSAGMC>x)5U``5XmxmBo8@5{=V}QGsA5_Q@>dd0)7A zO%+_#LkgQ_ht9Zx%p6&!!a<;o0%DEuXdL7#dvc=^kuIB@Z+V1y2{Omn#D9XavuCv#*V0b+km;zpqSnv&#K^Q^M4$I_$K;fWmVH@Rm5qs=8;_ISwip%M# zrhVK!0qlZ&FEHfO<#DvZWij0w1Ztk&l!hM#P1z}H{iDscUyq;R#IYg|h1RmEA#gOg zO{~12N$fkA7rI0~09v}<)ml#Yo7s<$%hpwY)#8S3Wb1yOF9>g{D^X_djZs@xDvlEs zxVVSe!dCV|&r42*#;?Uj$7eAW+^j>@iIjqV>J~BC>OwA-nGAB15D%2< zeNU*M>lnmO(d0&T9;*R&ku$_wV(Nc}0e6tPHR?>LO(pGv+d_8OA!(%QZ@v3E@Rrt;$X~ z(?OREZpBqdn$;VMdI~-TGrEE;_~MmHRM%tqw3RNTxmg|4_Xyn3MY7BE#CXy16hCdT z^mU6IG5^?`hXg^!nDFg&Axlpxq(4p^mj|4uf}b_WuXr{w+F@~rQfQ-m)*N37tS^LPucM}sd+(=f*`(!%0cLbQZ`5yCPe4h zT&o&YC#wFfe`5RLnu9V3r5?3g)%ZP8X~%JD=T>Ff5S^{HMO;<78*Hn&bUX}X*8Lvu z)wSx-mC4>de;V2ox9cpmD%ZWd`te#~y~5C>+CT4|_$S*MVfTK|5aEJk#JwuqzegGM zmt7hqgDUBdIF86b9vyEGJhDez4yDFC4}_D~cf0Tvr9?mk2MD0UXs2fiNiZ+wSav(H!6jO-1V743#_;b(l!EcWDkPiUZuQvTS)9Ubb|!kT_R|!(tr8HYuGqdsO2$9fo@{vDbt#b@rRVHpVcya7=v9>(!#{hG zpyR0E_Y0*Ba^mykw$oqUTHIsGNI1V(^3b?BJU~!Ir(g%j)vce_K5c)&t6*|=bNWvr zU8&7Z>OT_kgq*buZ@cn3RpI(;0db`S$KupuyW8w+A}FX67*?^F$mQs6dyP>~qtN1R za%+pq5f02;z6>7M05WrqPN(%+w_$yDuQQ7|`iV7%*nRQur86UI?MV<1Gg^QWPG^J#zas&0r0iGhpQA7{s}rjC05Bd%w=yvCEXvY)bGB zF;-)@-TyJuSA@o4vRGv#Up5zzhc$QSGeYuS zGg)OUQoqiNJ#UFT3hoegv;e~|KY#uVj^JDLjtFGekx3oqFGxc0GA>eu6u#MAbj;;N z!!0^`7pLd=)0GTvk(^#5M$`0+K9=nI7r;yDpLsDD^e!_aH?1;eHDdERNobdwxy)m#K#p57Slo7GNT1 zlD~Ms@a%~dmuc55QL0(8x?H(wD2Gk^@-%n72J+I=m0LW6jEt>Q$W8>{&>LYJab^P< zV%ZuKmctD($fxGPaH0(MdsIBE?kGfWYAAq}Onp@dKPQH4$ie&fb79F!@J5CTyWF@g zvRyS@2qcv8qVy@bLE!shIs0F*ebh6-(`RBDP>+uO*9<8KHnE6Kc9=S7T=oWaT((76PqEgzE9mZz+JdjH#hbOONdqn8fIW3B z$)!J*dj!;w34hqMDbJieN_ELcU?ProNNP>lUfOxx7g=`SYiVj}fk3NJqr++r)*6zs zqgy8^Pd^jpJ@F8dfawh`Q|aB;#c(R{eXN9Rp&FRnZTj%8sL-AjT=ABJw>mY*h7WJX zDhcNd3m$xf-9dPkI!{Q}}ZQJLB$;Z(y#eg@a1?m^|iBBJ zAg?kY*mmJm3GXS0c)TRsS+!0MbzTOYoFbfLi55W}ER|d09EPuOFD$%7k=iQP2sHQw zgJMEGR>ce^^nQ-zVpJJVVm!}_R3~_yI1eq|2{r{pK|}m3rQO7!RS^-NDrLxRY0xq4 zWjCGg%JgWEyK-4>noSt_#lQp9n^9SXt9fwpt)qKMnn(S@5fYrkG{=H=T@pWJtqjt6 z^rP4L#dfGxD_Xmseca|Ogiv*Dr}Piv>ssfNBNn*vd$-sR69EJ3Cu>%}zTnb!yrVxbj0zBpD*^eY)=sDtYtm3;rKHFS_kqm6?0Xs{e}b zxa>1pu_UtwM-QJzdnP-w#`RNnI>MG#8!0n7PwA3U)-r>NLR!Wf;FqrsW}I#}VopJe z+N!;81@TZ$V$_j%5Rxz-WBu&+Xm-ol^GZ-#YZZ8>u)$cYdc~N?GIEngKhwk7=5Q%L z5r-?1%m)Q@b`6#3$UYADy>B6Y)s>jx!U(C+@-}`5M|PQ<*S4Cw_fOB?sZr_V9tk`dY!lt z5^5}~V80o8YhD|i);8DnonvUY=Zn`mbCp>`Mm27Ag;=vDvZd#N*LQ!+0U&q)W<%Ix zNhJI{(WVYiDb1etyzWc5EBo+&^u*1Jjc%(KMzbQ_TTyA`%$iNQ8H28$Q zo#C*asP2ji?#%DUr!=j`RU9op@RbsV7y=M9j z*mUc2drI?#jotjPI61^Uq3g`w=Ukq*U6RJQntjqZn5gq5k4ytBXsZ<3=5uS`wj06} zq9xwZ3d@^-e~t>CezUp)JAX*Xp1&Yo+{--$3Flij*reQcwMfo;Ix7Of()FKLYYjA1 zN*>h(tNzlTjF?t)o+~0By^dVD)EL7=02MbQZJO|R0MsyRH%eaxWX*>fi(O%ZA6Pjp zV11N{ec=iuoYk*Lmn@XNYCnBUVNC2RudZgLNYn>Z*WPPiniPRFDf6{9a_2v-PI-bU zF4K-InC*C_*~cmF@3)B+G4(UoL*Z;%-s(=mU|2Xbuc{qC*X~LUekl)UKyFrflPEz7 zVhtq>Bh~Bh;(CKi3U`Si=9wI&UScWrsat8b)sp+V`r+4CHw`j4uqiw#= zF+%=8rJh)Jvr6%urN@aH)oyhyBz>GH6jK1dRWipQB+%)^|K1 z(p)u}>(yA^6YPcQG9Li_?|zoX0i+-Uj1hUHx-u|SbS${7{c)VyL^Y&yd)fF4kU0pP z(xtOQgxa8VK~GtUfChlhsB58Kaqv`Rmp->FhGhQ zl}7HZUyUtcbM~n~^Hjk~m5>8jY|efr9WN)OwFR~Fpfn|D(i{>v*TAQ2pPPuM@fdA@ zOC)f5n9TNBccgpHWQ`FMwMC^*lFL1VCn?(G)7<4~AE#a*dn8T4mK2-b?>S@W znHa(J_Fc)(B=!mOFb8NIkpe`B(Lv^gLhlt7_$jV>4lXPMnFD=zJ2}X|M$gAFzUUi#kb9ov+L*}*B)bC2D)*oP9i@JNJ1Oe&S9M~VO#0BW9-a)i zwI4tGFOh!JL=zOmr>Or0Km zFq4$vj$0lfmj`cC>EIQ;9B+PQSk&h9@zbxpL+tr5dMLsl+t;f>U*YfDoi-w6T`sx5 z5MN?*1NW%5ej+`oO`meo!ts@K%ahh$*CZMYQ=;k%bAoRjDZ3zPk0Vo9@C#)!Kui7g z>%k29cME(s-{_vQf+RNQ&s6>Il_+l`4?UW1^~`gs%a@asqxFvx7V5>^HT*Dj(w`sREV;^R z-A5>J-4b5rG%?+6F9^gs8`NnzE+&yKch7uub;>=S`%uU5kIi0@jH$B!Be6y003ThQ z{DH4WB~F9_1gQ(DK{bK}@r?ES4u;=3<@(w0t%g>f{1(@Voi-fTnJ(ww%;FYY6 zo21{s`PV~Y(6z$O%5>7P{?0N*?)Fl`uUQESRRRL!YbTa1Ol{)Zy|AFY;p_XlbS1A# z28&4?9b(@Q`_n3wkK>;m^R9>aOa-uD*Jb5(Y)NcU0^p;Ev~d~Cic3+B@>}{GxHaZM z>qhjY!RxltlIW8mAT)g6n|sYpdM>r+@DqEciv~!MbD5WSGc)6qvw3Ga!qw+Rym+Pb z4$0cQVrGBN0{^?0&2p4<7YKooU!WTlbfxv#hUhJ9SWtmnnu10h14Q0G@L+gXP42xl$rViQHM75w z7Je@XMF8JU6V<~7xx}cW-HAr1JlfZ#GB#nmOU=D21?L>Lf#efJ1w?GR-GXBe2RVq0 zr@>~X5&&*^-T)r|{Kr;$uk?+>sXf!$Ry~lWqdh<)?kV&1crz{GfMA6>SJhW5#6JIc zbPvt5Rz7GVl>K_6#Bc__4Cd497*?$ao`w2p>bWjELCam!v<1o3ny!VG(bb!lR(8r4 zJuCmcmXI7UlNf$Q-g+I7b+xy-Q*Qod>;Gy2_+zG9mdt6I1NvPXWC!R%L{prs-5xfiMWbd@R#1}J;9boTCY{LbKDD{FU$3Ce2H{^{OP>OkW^!+%P-ZU zBq5A5oRgQNmq#9-7fu6t>;q9DMT>#?9#SNY7Jpit2>O9MFOVADaGoS1(}#oD>E+kt zLvCXOoNQ)^WUnz`H{#F=)KIm&)jyJ8ShZx@0e&uGsI<$;J|4AACY~v z=kAP8VGZ9k5};$*zC(czqBnHwF-cT*M9$DVkB=g0!Os2bhZo~89Mg2_0!os zKgjCB3i@MHa&aa;Dw>~I5^0Bsc#_sJRzn?P?wL!vRm`C5627=KDSnadMf~kX;XJZQ zf;?f^ti_WPxX65v?Cf^C$9@k-0Wa;z6xm&U8n1{x;tZB9$B}MvKQwPlY4LB@beyO# zCU$eoj>Y9X@TevDU|xJgvKSn{$3cA9#iFxmVjtQ$W0@LL)NCfa>a2@pY<}%U3_dGg zY#E}fB_41a6I)i6XhxTC@fJ&6@LJm!J{=U@)|YIwenPw53sPTQ9)Wp`ARU=r%lt2z z&N3>>E?mPP4H83$Gz_f>(p|#<14@b_t#o&%^w12Tbhi@H(h5pyUrFX;wp2G^4e3q~r5|>Vj{nd@ zKtTULIsKw7Ok%wTWt1JtR;4#^cGELIEx6Zm@_f;6S|_F6>3+@it=F8~E=mJ46L(g5 z^*5u~v3A>>FEr4vuaU?rX3flSWS6LPgAG9_ZqQ z`trU6{P==tQtV;rc;(=kUr0^J8@#jG$J1ZHlZj2al5H zpf;UvD2U*g(zJE@bg?(vIve!A)Mbv8-cf)C2Qho5E2&Cu>6?tBM^1KDR;8|5BX;s^ z`Y)1Mwx$Hy9eRh59yeN~u26=wDc4>bJdb%N%48#dNoX{FlHI%H9Niei7EbMr)}y|P zujrM!Cuy%MF;KeC6!E9st7@M3@T%^P`kvI3x9z@PoTi(>pPH_ZYM*73s}pg@6L}Nb ze4Vb+UGp_^{>}B7(6mMnTPpG3q5tXHyUD*1J@p*&oZc9Ne#o?{!)S!_BUJ?@Da~02 z=1Ct^BU@CQ#-_wvA+bxgespGCX3+6?qIq}!ZX~CTOaEI~J>w#@!v16C zUSXj`Eg_ZsNQAEpDeK6&XML&uOf##%^l5vY$i;kw#MPDy;`Si=^QaJ0_Q^X_R^Jc4 zliCCR%{gp{sCepmB(6IIDsID&#Jp}hz8V=%THAKwov~hN?by`$I(1au2{}(%zhHj5 z+4Uks-U(mZR!Kypaa>4##M~zu7INDQFnwQzL<+^6o0vt5lPj7BfeQf9XzQYx}lcK8%`w1JIvrEw-|Qdxv_?wuZ1bgw^E2 z`@^SNyPTQGY}0=;nK9}dNeO~7i4oDzEqFYxZ1aBf)YNkEF~*F37*f1gl*()!w9Bb^ z+HW2~=UyWQ&*XF{vhxN$?5mSyNfaa{=PRBsnzv_a$j=#{EP9MEHyQN$P(k$>pm}^5 zVG5y0%jDH0>x+V13;l?3`+b)o@n~85?^bb-DJ!`)o&Ekugi~Kf^T5Vskm9p>K$MtOB@~^&0G- z9sZ8^{TWxx5MP%sF8j$hmox%=d|iJz0+H2dr+(|-i0t{y^uLNh*A z1{k;T$Vl0!kxS6TOe`fMIT&OD>;2R#4!lnUF;Oe{8GHwlB)#jap3OiGVn;GYT3#pr z-OAG_aC~KOgL7^m77c%ckpKD_yl>Gx#fQFF^Eu57Ma=#CO5r)%vv)+g+hzi3j^3%e zWzUz0UFmA5Rcwuzn~f<{8@(wsuiE*h&jj&V{9|oNUd(#Y`u{V0nSO4n%Vlq^G-nrQ z`dLs@M@I=L()1Rq$WXisdyG|CBS*6~O_LTrnjgj#VsHruX6Sa{u{%!wgO8OYN*|f! zXBcL1e<6N6TIMnmd=}-*v91JLSEV5LhH2L?!>0dKKDzki(OK$xwc33a?GNL67#TsI>O>X}W$sSVOh*Ug1I6@& z3}Yu6%ZX~=MA-G1_AVCT+sBc$)D|8-4SY3NXD86!j%;z)Qzw&CzETuP<)ayV?5v^C zbMgCQhg%Fys;~ohXEo_v?2`eoS2+s{bg+#EyMT$SVGA0Coim2yS{z-@&#lYKQ~WSE{J7-rqX+_ zLVitr>P7+!(l4`?`6gHKkb$I>HVm;ROa5{mskNu3{=QEenM5p4f6k9|48u^&O{x>! z_}2}k`Bg#6H$Y1X&wEO8sYCF+!rzX<8lw7j877%R)d;ub%>83}DPIE_dzg)sotM)?JnjA(= zc{^@YrqE=YJa|@jD>n<*YLYo-mBWsf-^|#L*>g@-9`VjLb@YF!Z0iHb#BX4r*1KJd zg(aWl>@&31*9&p()bMJq7tc(H<`{?$Myy=)1T1{G*)$>H#fg7F8~cW~k(RS%DQ~h) zJ74kSgV&P7$m+lLc(M9=O;KI_Db*r*gx{QDzgdrNRm}CRNWaQ{H1fa?KIVpyo!fiZ zobhp=%i^PexqkiH{F?=x!e+E$V8$j*q2bYR~;f*Uo6u)ZNjHwX}TImfC{9{rf}E zxDqCf+P2Ktu_#A*`n$VKA$-cDhl5q2x|$;Zsr2qS0^leL3JQ8}XT`$+s-%`}=^(?L z=9!xNjvDZ>f_e$S0s!kKPQLVoWzy!gVgczeOzPOW!1#rF<)#|RB{t+JE4F3stmKtJ ztmqICVqI09#g*?kF&n<-$pLL1R{uO3hr+oNgXaA}rnCzm9)|Y>U}>hkxzyETj;?`g zdt@lQn2I}yl1ss19P`KOZWF;SPxR#0fZQ$gTM?#38QzT>RuW?pn?7h?>){PpOZWPDaKD(?$A&*EQ6aP0n@>#e7jn z(5NSuzyWw_5;DH%2JruJ}PElj+$qHu9{7Xm&pEp6@Y z1s)v}#G6Nos~P4E)(9-`)2+Z7p+$7$I~?XQPkP8(62WB#B*Iz>+GNCdoE5B`^Mxeh zLheULGi2pv!q$iQvhvn3ZKyp2QTDj>*FBWg3kv;w=!Ez}YLUwECFzGoQN3{$Zt-ME z=suwzC;ru-Y~+=?%k7r|L084bU!Qu^ZhJ-G>(k}h$8<*6ezogL3ezurZT$;j9jey<$rPa3j z#&GsvJd@nkXUos{W2C^nU#h(bA{FH4in6lPu{}+^^h{977kBL~Jx{Q75gi2mJn>S? z_X8^5ht&o3I5kHg8B#;L@j=`$7FTRdH?%|OA5n(Oe2rx$xCr}-KtTX0J_ovck#Z^AORgIH@ZVi!3h+%>YRfbb0lW zzcS5~(0&FciW>=Nta3pMCG+kIC;U-y+Jzl_O#&OWDU1l^mI=9ipjvUvSWqzyCH)Kw z`8IQt5gKG&FJ~YOZ(8q3KW~gRu_JN(l=TE=dM#CLy7+umC-6aHVGl+&5?{6g2KVC7 zmD(YKe_vvBZb!!w$>yKaPWAD4@?;>@cqjlvb36cMdZTTs-4bD2IJhBUW}f{EIk8LP zl$<+hk@ft-iOM_jAC5QaC5GmfSINU$6IIdmm*&G+KS0LQH-)YDo1zkbtScYrAu-16 z?Zk0EJgxW^q>?TKELXi4#1iWF?KDK;Q~;KOg@tAN`)eQ|fh1Q~S3ipVBvM`3e&_Ac zYNgw0E&^yH%YdWB#T8A1S<9MHioZ3(6*a{OpboEIy=wn%+u}5**kD@ev~@t(GOj8P ziLl+s^jMTnB~#2-wA+mul80aGoj#VrE?qLpjkT%wy4fwM6=lGxZTROyZ7vG2xMPr!~cd3i$P5>M3NF^k7w;Y!60eIB}YIS4OgTzYEA4VR8a~z@5TF?W zTEK5?92N3My@T94XKCw8*x1>N4V4^bYa{x3+B3XPHh@SbTf@*$)yc^z@n@51FRA%Z zMlXQ5;D*8mvZdSo`3Cy>jQ4(5(SppT%&;}>^XDfDv9wedB6hM0Sa>E?k0~f9);2eV zZ7jZx_i3VgtSG+tY9%8iSlL*XpjG%6>p|cZap^B@Bfno;qdv@IPvy=#-%Usxb3otg zJ=P3H6wyEj8r0=j^^zbk!eH!Wfl6X#@j^66v|o~bL``kdzNvOh;zyK4{V&I{6=f%H zrMb@?bsIP9B3~MRpEf;)PCB3X1uSzXxm7~6&_J>uQaCs0?q(6c;l!qy=V zkYicGK%J`B2A$g$cn8=VtgIsJ4uc-Vo#U;LlFRr)bl9Y2$%gnj5MJDA67+J)0c4I( z(?p7yvHXiJZ#iA0E8*7nK8)#=ZC5>SbxLIwaq#qDK=;rZGpal9y<4vDxe_{%DV_|Z z)2h<9u5I^sK10&?H`v$v?e}*;^5``ATKfplrj9n!9h?C~L-@A^u#NmjMgqN(S{3yE zqIjuS?;m{qEIOJsl4)Q(J1aFuKubff=pE;74*Z&&K`6BvU(zbja5;U z*L}~Z8DIo9UX1r%M{yw%*Dk!-4H?3WM3WP_{N*nBgDZo`MW-&tN?hW@CujzI8f2C4oCRd}BYUpc z5t;4L6Bh`Vf!+G-bT4s#B$9J8jkhh5 z4cR$R-#oBukvqSg{8(@Y4K&rvY-D{vKV+tc9S})g-qyeG4CD2?I>o+zrc)i#o5((W z{~z5EyK*>NIy>FIDFTPyld*K|AyrXMAhy5eD@%R8OxxOmoCUxCvKUe#Lj@=#=*PP; z&3{}#AOz&#POaYJ5dgSjFz?t|4$p^V=o{UHdsKS2_O^6av>7kiTnu9y zKV*=Kzdmfw1`9mfA{~@CYkpsG8?U*9%x4mUer!Z59b(ix-5of5Bda(tJeRBWT>~MA zgQ7NwnK%;ak@m+nbDm(-yh#`BVj!UkN7;>n%w3Qp>A@`lbY-z;)&HScjH{gTEuKr(9mj|#rRH%s zG$8;Db4ez}~yiET?JSLXV2cZO5_^dcvn4tx& zg$>EhAD+*5rjR@^<9KEzXEiK}h5yW?*OpYAhzgG?Q#3Z_nO;5D_TABe+N?X5UgxK0 ztlrt|YPLGFSGBCIOS6aG)4C|3A2Y2!!ubj@3YfM}a8K|kf*;C-*m$#Fqsko7P?OsT z%@6Uk#tuq5OacL4N$jZe@d)GSV3wHE`z6r7Dsy&S8qQ=p)fWtiNRb7xu3H2?r^4tC zm+9nFn0>TG`Sqzh-*EnSkMeF|rUZN00s`k2Uxeki1JkvX%Lfn-!sj^hyVE*xx6ET* z#d-|^9|ZP&%CDOOVL*rd$i8{7R^AQ!DCspjzKV(pZ{=n$d-mGh^`VES{7{}ve|!_J z!3^mHJTE3D=6^M>7tE;L^XuDh3--FYx*SiQM5blzw~_&q0zh-JOG{_(?b!kh+~DA# zrvyD=)*1dUU5evh4VPR%{JX)lT)SX_}JhHUI@STDcWm}04q@I z;8J$TnA{i9gPk0B`sWNuF56q*t$}$h6lvV9pm9b^{=?92f&D|z9rv#PAV>8b74)y< zmh!T!Rz}VDrv1n8y>Sher4Zy9CTwvhT?2k2GAlyQO#Z_`E*5pjm}mH*%l+2DwDXHX z^?<+s_1rX(9|p19PyPS_9ffJPkff*_J@8N;ee1*F^_ZdR9^XEFdZZ}6o@$~f4$t9# zOc}ws74arr_`DrK#%CvYu!U%r3^crBWesDgnDihRo4x9RD2>gvzs2Zz22e#UWvKmf!2@j;;^lRF9Dtm2oti45f zd=}#YvzCkIEFV+9KY;2IM~YjQXKO<@JEgQ9YktU^*43K)=T{5S4~1Ir>s%|4?;9#x z)-Sh)aJfkpm^r{$kImUuxpw{i%H7(+pvGiX_t;)v8W;o4E=#sHd9}C$h=5r2gYQ@; z>Of-JeQ$2G%S>Wy_-z0Ua%pK~RF9lYlj%=1`9`Gh49b}_JkbR&Kh!Vwy`iBYtaa+B zBPmf>NGKsWS<&6yUCX9U@drsd*2uoQ%Y;Tgp8U5ugmp>1{RyMPVPnsnIF)>;Klwte z?9VqyQYZF2#3YFmTQ1+B7|dWRx{!xP=fU9Zy{Y7#)EfVt^pu~2!Y^iR(j7%-=_g%Q z3i8bDHLcY?5UQ)PX(x(VS1BfekJnOKoR$jye#F}=?bU`xOwKX3b+Ms!Q{nMk$7E}> zJ#r9N!Y1!;ICd=UU#Hxw&*qDCCSI=nP>q~n^=b)Q-x%ZFTqd%+GNTH(YdFPkMv;n zS|wod&|-o{o8L9*tnnOz#n0f_A=xHswL`Xr9i#kgHt#iD7 zOphxQ)H%8Cz?`MQH>es&01lv2cP8AxKr+&mfJ7%& z7OpE-5*I>zmfnVs(kD<}+n1a42>5C*j)YDTa_k8BrSX@q0vTXJH#+Rui$Qn-I)Z-@ z$r<%&o|l0Yg2D0QP25^^Falc?1GV*hLi(y35!Z&xdea8e{i|M!{xQd&T2W_sfd;be zz3arDPuQ)cdH10AX)$!)2fe8S?PIQRPGsjv*(Rt3d!dPHcry*&(N46Tp{aKRVf-V% z>vO_chW)9YPoF-$IdmyF^!V0Bxe)QzC-}eg%dpj(PLm?V7Lmoj&-f>X)K`k(?CcD% zh(VY_bcyELZF=ZvX_HBC@t&~3u-T0}tI<&*`+kKu0P%W~9vvBvvNOuz+_osz zm0rK9B3E!XnH0~64w>0ifxQL$n#tXo5-QPaJ;k{>7aXHuJz zWGoPedE1y*IsW#S_fLA)M#Bi@v?X|mzj&mh2UF_zmrOb_NLL0XuTB!jn*Ga%yur|6 z$duhQlZp@WY@R=UEg_sVkPTfY6nV+J#zz$^u&smqMxY@boeiB?@iI;HDIBD_M7gCm zj`e?~Kf#8&xXclkZyH)Gya^V{okToHjc~n8FF&IT7PxuIV%8E#8aY=sr+_Z5^YtA) z-H+(BzQI94Qg;9&{Yvs{^=SFXGYBJLKl`jx9;!qU!MrZ81^1H?+ZmsyZ%WKjnKTCf z5w4Ol3w^Y!|7}%3SU3@dk~K7>2RtUaD+C3jGq>F6ntuV4QFwxFH`4=KnWnEeGkf*9 z99KkZZ@0n`H9X2BL^7x?a~mvrSTBrBmL`?C`|8rBRdW6m{hZ7wjRc*TWlexn28%ry?_;5Gt|3Ef zJS|_J_k2Hc0!O#f;vsa;L3i_?-)^H6Oj6;1k>7<#+}#&ahxxuDW0olPIn6gJev6_{ zEKviH;mwAM3hwPq2n?UJb{t@4!F!y5LP4t3L_UQrsWMLC@v;4JNXLv0&xrYCMf)sY zfi4cw{qpl-2={fmdQb(eK~O*>^v69sqINevqYAQJi^ajD9ppN7hoGkl6L_2WnEw}t z`R963%A)t0Le;1H4fFv}>zSGHCL{hQzNK);ZlmSNKQx^??0nLFebZJSEcp?yp2}Ov z0-Ef|iU;)2q~#N)+w0^h8;7c?)WC{IslxgkKuxVT971s0eX@KpT{L>i!xe38V)#eLIua zurgx7vW25|q~P8@Bu-~=%yZ;4-~Vd?=4c?d=DWW;Ay&eB! zB^T>$=JVvz&sgL~b_Guz7EzCdb9wQjEanE@`QqEr*92w)Q&M}C-Q14c;urY$(=^^O zB$c??bhXN)wMYL4x*z(4EaIBnLGD4c`TOJZi`1lND>ifevs-^;^gn*cseVgF@yj`4 zR>&ILAW+}$j4)(2E(>N^JHg38O=q!Cj}H06NbW^1GlX;*ExgWFS2}-A{yJ81Ejb)9 z5${FNbXv(a(K^(^kQ$Q4ZkCH|#zdWQz(%?c zdiP6#JxeT)Wn&+X)BKD-q~8^d$!F~syK^YNPQl|pD?#fLQTEOc`Qu&?m~QeKidT@9 zB^hNwU`cZYyBn_LZDJcwC&@#=#{K1eI_TigTH2)5|`lL4uiNoQ}&IKY1{NJ$lqWZ zyl^8oJ5YdZG>ks%e5HQcf(^O0hP_4>9VJY+81D+Mj}@!oU7ufSnBIv*!u$~32WpnM zd)o*C9LTY7yn>(ftPDQNB@df*eWz@ij{p@=wV%S;LvP8QnXYlv+YU+7Jdj^&N1wS; zRu@>H5tQ_v_qHXQjn9IsCKybe2p!jCFLVy1dNj7O<( zKX6q~<;3LRL4RPaDEA&2?nV`C=?IDYZcJGlXw;!d;9@@~A{45yNm?Nzos2cHPBhzXiRChe^<0W{t0_T|iFr1~GP=4A6 z4^rdBaC#rk;Qub_yp`h#-TT{nmt7+|=sHyE=)!`$FrZV{g5dMUH5GL0MB{#)CVq7d zy?}67R;j}$P!Gk$JKS_gJhE|Gbk=ZR8{0T0W>ohfS<*)+Av-$&SR9y)id8 z{{p3R@v?q03TRxSyJaUM-4-I&^Q*7|av8{mF$)71-1#Ra{JY_>wDfOk9qwhDioWJt`ST0 zF{DY7WrDRiMgQ)rhsM@RRu_4vY!-t1+Oy$?>kqU%opG8TJeMqvW1WDV>F)mK4kHx* zw6U=fB7(p7I^_Sbx?FyA&zPuJIGnlj%(IfS3;@v9dudZWCXbd@%T&vz@?hI~SJ|ruk9!5u|g0#k;W-8%KWWGE%+UY&KY(N@|I4E?G9>p<~L zK}=x)4^Cz__bk2lflqIfUo|SCdTwqnZzVL9--6HY%C(~X?kWeah~-MC~AB@im)%jc_kC??qzgo3Z6AgX%KA1C@(r5$g~ z(qVCFX@oSepZ(`}v7P^h0;T`Yoh}V#{f~f#+3W6dGgZQikKhrP<=F+8Za_^@lC8 z;4t`|<`ShZ&U-8wa4=1Oz<nVJ0uFOMK&ufMvu+O5Qy zL&ZMk4ceb;z=_*E^7I5?hkr+L0U7F9N=lUB)4PRjZEbRDF4gdnb6y=3{PX_V!ue^6F+RJ={#EuxW5IWwehKwVTJXXxO^$vPL0$vW<>ql z&?|!s<&veYi!wpqST4_d8u>fQx0RPdu-24HnXzANS5nmbIxK`b_0QmutPCbiGj-FGRplpn;=VIg(5FwM#?_=V(;eCxC-;F8&Hl?$ zgY8U5C$Ti`&CH^BQ(4)=hPgq>i}}tbx7`%!04YEeD_^xyQ3@LB>)V+a&v=LliUpRI zmdM6}mi(YZ%TdErcP`KK8A|n2oe%TGJP_;n31U<#$lYBFo>jur_Ck9G;{(059x8mQ zr6z}H=Wdfk9ot)M1CmtA)71@eJVLPw$L7r_vx9(<*K!z<$BK*(gj@(!*8e<*1zXvSZ2aZg**-cfS-aV^%r7xp zY@ux2uh$dzQ&}OYIBjD7qU*C&;#`}*X+QkLLd}#tibs4*)g93Ob1a6l{^OTSOil4Q zEJ{3i`V^#@=)@9#(JZq4gwS@UE7fpCF-$QZ1TJt4C{g{EY@}N#uBt+T<0GUjx2;Wz zYkRhNs8PS7L<;E+b$enh^g7~5H3gl6OTgqy3-W^z^V*4EqAGP+dm!!=17d6|v}IUa zgR=R)-k(0o)?-PIG_YSxK`H_8JqEc?(fx^NMbK*53LQ7C0KshD=a5H3c0gL4U}AN| zJRzd5{(_lvRZHuqGfuA5a6=v@`_!&?pYEKRM#nfp;+JKO*ax8y2;%c4vvW+DoHhnK zbM;ct>0i{=PDk)uYs13yGWjg?OAw_+%7Y0hq?+RI;;QG)Q)7ktg!TjTe^s0HgFI5- zRinFFlaEofLX`9aoaT2Y-BJ%vmG0(;cgMdXKIMRl0yrtfU_&r8ua7mTU?!aHk(d&J zYc^PKD^`fEUxiFa3*bhPlD}SzMDD&^3+IME5`WdP&$_l4Z_~N&-f(P+5iXF{plAbH zv^bGzl)A&K3XqyA3}bNNgnV_psF1gXa!=qh-IG1@id9dUcWz&+7 zUf7b{`dTv6{uNFSrzS2rtA4xSYu|mV8E}xvGWbH9MnheqUF$yGo=qDS!)yFS2JpmC zq>6K^bCSR9%3V+$zMLRb)MfS0qm!R7e4)O%Ecd1fV@q>Mfc6Re7!z4yo{u;agU^3a z@5~=3DJ=)bneXmnFx=w;_H!L}EGxw%o|z4r_Dz%44_8vkTD@BB7Km26vtd1in&Eww z3||eqi$fxfD&)&0(^00Sa{XFxwpSR>T_l7t;j~FMFG+gVr-G>4I;MaG?N(?Xea6E=p|dEyJ9Y`n{@R! zoeZWAHT2g~9(;C?BkhI~S~*?b9+0hmAGy!*q`{+vALU{I@2xnWCknXT%_hOqX4LLo zojSxSd9h#&>$C`m5}%4CVLl|W=CtmcshSeSG*B{SOt||lCcMdbp-^>}!p9PxVjmCy z=S~m=KiwK;&o0|LJrl)q`Dn(YZtXD6uTjsm2OoMKEzlP`nVk7#wSo~m4b?j-E;_>G z{fDTxo3s8p9hdJ_;%pevHC(>D&X*dzRbOiuZt`Pc$~-61DmxN;GVu+UqFRfoqlsqw z>#aKMfSAL}GE!Xzub01Y7U zV!XF-m>qThYb#<^`ZUirF}rtgFov^F^`nz^Jlj^vJ1KJdIGp5uicy@FR+FEaj$?*# z51RdkSiDFzzB8IKOHQ#IQfml&gUAnB=v_QzaqVqKM~!J}{XIgR`7lRzxuEC`%x>qS zIpF}F^>be+QTdOY**f3uHyrh~j5ty^uaaJkiZfjqmKH)g4z-+;YkAYmel~%H>m_v* zQ3y-cBbZ;-MINqLr+3T}Fdtz!hjGbo?z5Q=@Ad+<$!5SX6WTTzk-QIzL=+dR|L@f3qBqNwsClV^5j~@KWC<*vR z896b?FPlcYw}@Qv?#0(^3!oZFB+M&DKPL`k`CF7SbOfst_Lo*SeBdx~l z`kI#z;+yMMw!S5i6(!)y-~E+AP2woYq_Xi@iy;qa%>6KYq?*@Sq&wpDK$tq^J97q4 zv*fep)*Z#l>2v`;D+`2DP*?@KmCtk|IIC0GGk&Mj{0fduqp8zov!y3Hq%@lJhWJ_W@XKrCU z?fNm$Glv|*kIORq-w}Y{D1<#4XV_W)0zY7>v37hbe#WG)h7twJv&f5UQs&7!n?BQwU%BZ40 zWnaHCC_kKn!cHIw*!D7?>H>14C{>W(TBnAYA)NQZ&bA*A288kW1O|Hx&HNkF6nLs) z+e-iue>ols^@a0!V`@OHd;jrLp$i7j(M&HmfdaA`3jA`=vM@}jP&lw&~+GTi-zj=fNm zEFu#Y}VTU4Omq(`U;iKMEJ=c7tykDvn8J9>yO{&cU&>BI9*?}LY42qg&JDU{&! z=uF-JLe6IfDlsbRJ)eBN#nC6!W}B!N@jB1O(sCJ8$Bn;#K>tra!zniu-+@Wjs}ws) zV3%M|r?V+0UZ2>SW@3d${Y?7bw?GD;)AV3mYdFb8va92p-c z+xzViOf?2=dA(Tr7Gm%5jMB)rYPIs|yiKOiz9vgU2D(Ia8-7vKD9f$>_uvxWYeV>s z!ueyl9`EG9tArHIqm%er4CrzB)NnVmB>fs;0Hvt(-3NS~yP(>=tuL#y9oz))Ig&5U z?-OFWlI_~9W8UP=HM#)Tz%kxtrU(7fzb;|1)_`gzGdwZUpRf_BY~qGFI+Pptd{t(e zqj6uYpOn!{ldpedEzaAd?~crXUIWh2m>kGGvm}qQV1PTajn)&3vhBpzm7`Mm1A^xK z;&B*!zxEp@|Jbc;S5Qz8Ql44}gBl4Z2%AHmEG&j2Zag@STd`Qt6-l9nBP@@|G#J!+ z!jfZL52#&Z!PWNUK!NNbBS-1;~H1m%`o_Q6d4$HD`G9J20RukV58Q{%_(lUtS z^-|`@V!tTM?j}o+Q&otCsIzX&S)N>x%OCHe)s4n~Hr z@&b;)Do?;AfkWG+`6doRv!E=hQ;X0oq^O9EkB_gz|6Pn5Hwi9WCCvPtjI3-R^UsGv zb(s4^kC)s))#Q2FHkVvB7Y8>za_uFh^Q#-O%S;AE=U;j~21mSO`HFgRH+ah2^fDS3 zd@KgO7`>F{37$e@AnjfYp!sa-OG(kGA5$S?4{H@`%c-!J`(oI?GGqd_*w@EkE~xN& zo_R4XTcFH`v)$<$lWrnz0JAR0R;1=k*wiCs{S%X;GTjK^_bE8_X_1AV>(M`jgq0=I z($j;D_?kDUteEqEzLs<+0*%dsh4lCdR8&HGa(>S@eAYul1(i%Gx#}=ko`NsLGSug2 z1)bTkJ(n&MBxT(cfEYp5P&V_B9_t-1$WJP!II&e;@N8W zh{y0Hs4M{1wx}(r{icB0uMehOnai_h&z#&c0ecP1s`_bAZsCBJ_H9EUBCu-R%-md| z%qy(sqAs&o=yh{2MWBCo;nsOtC(xb_q*GF8Aq>-PE6zyaEd!#Jm!;yEi zYgXK{sI#Ytjc@(ld*wX@Qn;+IF2HL=sjU1)vDSawp(Q7WF?TXwwNoj)7fcbp{M$(^ zx)MSrax%bAr~CeR8Odaszn^B46AFdUBBI(Cs?80#Ifb2Y6v{_I+&aG6XYK$pi&Wx zO}KL-%ZZ#gUj9u|`V+l8yue-P$y3@{)f~&i#@TVBeTp~*VS%*wc@l#WAqFC(L{BV z0NgqrteEzzfh(`)x!fNLOXkJKS-2`H~lu}}Z z78T^Bvg3xOwZ{O2xs>yQE)tvb#Ry0$n1ImFI8sw(g0{T4v6TnONrmGl+G6N103WB@ z0*0~(#O9tGTwnwLb4`rd!Tiia>QfqepFP0>nqk;4p#TNR%y#(Vy6uF~s>rTCzk1m=*|-xu!672+%rs?&zi;Jv+xdrDG2E!}|%!+UO zQ7<(DZwWp{hZ5Tv5ZOhrJ#uc|t;qg++#euk+Dx$BeNWuTkDcrECN95ViDOseM4ci} ziRA#+svIj%+GVSCWf(Qj5F9_08M>C3{vmtCJzT)@)uc5`G`xd*MbxS8!O%JSOq-v$ zk?#=}8M|uq6MP47olfaF6e`BipYBXP6)Bzj@1Lyf?BSv=8zG{w3#Q99J4jwWqL;)) zLjz*p(7bT%7Vuuwen2LlQd zlak0!))L$0vNp2w*%Y;zhEMl7pm&Y?8fV`E%MVJ7 z^>%YzU*C74d0wt3ivpC4&Ns*R?26m;1#iqhlRe(eC|!wT$HN_h53cA2)rTXl-n+gw zZxf$=C5C0JIvUne2?ece7)%%P(jMTez)bt>W zLrDZnP_&6aRO}7o3j^-RuY=ds-QEuoQ_sczITy%H{~Z!1pq;cK3mi)L#?})tU5NhG z=feI!@{ByC0r@oYbFN%?wcGBSt~iEaFvKy~^cK{ct8AvZ={=Xx`}+Dgo<3cz9aoLA z8B}F96SQP}jOW$5_;oM@q$=XJNygrS;-hc|tyWxDo!8nDewlL}@xhX)BxPjR`p6@c zmZWO>IUT65xsXqi5#A!yZ`Gp{!Y=Z9E(78>B=Bbb^{JB;QpgBiEC>gK zfRY}(y2@`7U{6#aBrq2Tq%+5mmfDMrheP6p95HWc6oXE zXllVo@GN|QIFD9uzt9J;JSmbs?)t-txCLCYI1kW?=p`OcxObx7D`M`Ol8%&?h#LM( z^L)9^=YqSPscIj+Z|4tuK|g}yib_k_$MWw(PuFxOPG)>RZa}Z~d7o@92F}yyl#rN7 zK>yZdcr0$;Tn-0peSId&mtPaJum~@!k;!XpOf7toJO*YD0gplww@v^bvl$*WY}8uO zGA<`G!_k=GcUrVjMSaM^xu(=){zips%J1Rzj!=SBzI8DcH36pLE_5wVzASacmX}Fd zzBHbs+rJO7nMj;k=Q`T{keh6@V`L|7}yWrU7zp&7uBEc&j|;9 zQKc&okW~~r?^7L7RTwON>^94@zR$XA4t0K#+gx`n!zcp`P{mo^LO$o78rv8CYf%TG z&<)!p2?+|TvE4v@?~9?LYG)@$KA+E-K8`~xWVo3BQP#j*nKFasPq)_>{NQBsKS^H_ z`8GQNS0;01Vf}93kRBBD0Ij>sh0Qw39cfnmz3mR%{YhIQ7K+shw?GZBF(e+)v2+ha zM(BDYv>l{5p-!u@E2YKstKfAZdc?^(e$~WS3}yt7*($+hwU|Q_7x#d25^O*)OL%>+ zX4u}%htK<Nqz+TLaer*6YL^v`~i4k_|p3xp$cUc&{|Fud_KDgZnv->A3*_>Td&MY0u4x`aKNSla{|Za=Laq~v$IdrH9c0p0*M}h z>dTtjSnk!B%5y|?TE1Uaee3hQqgHp!L|oNhR!k&%Q2taFHg@6eurO>8&J1jg=8J*} zGjzHuD=|rImL{Wt{|HoP6++q4lqkw;o;AJt;s$;4@*GhZ(NDU^ph2 zbob+D1Q{?mx>|;YOA`?UxsQoiK(S9&8Ui?@SrYb8`h3=PqwvrLyX+{+h)8_U!VWjP-!!@Y0rcjeS({db`1w$#wrl}L@z(+(Z;UJQK@?Db9V^6{#cp@5eD!9g}H@IeqEOIe)f6n-=mkV z$9-mpi>3S=qXf`cX45OD`zqQwC%GfhFOr zq28btOi`PgPH;tyO4` zx8^0%DPqdf0018nJYsYKnuTpM1gQ`d#uJ`6pCivCEcw!A8p|hE;Fas+K zf{Rt>#;v&qp2EVy1?Au8$iI%rwC1$mq$>=n3Irwsj@BhVHU`rHFM|%Wnn6elaCM)P z7RnN+6v<^3@j;dmMo9A;xT3+!4J=4tNC^W#8G`SAtC~2k)LpER%O2+CL^cib8(iPQL)ts+_JV>CQ0PuC$f*=%vS%wy5fv$+ z2r-GQku6&)>lr%sPz@;&C3_1g914SMNf8o~lqF8R_rG)A<;&X_SC^XQ|9gJVb3faC z*C(k6<*Rl_IUd+A;Iv6G<)i1#f_e3=MNj|6L_2qKp~PXkCnL^mXKEG&;L@fpmVztG zX0vsJf;0(b@We#+J;&x;&3a_HPMu7?r-~QC#BJo_!DyIIcFV3g7!{HbuWx5Z1z@*r z&)rWfZVBWo#JoUFm0J=5Mt}im%1rrclv!=TnrJn#W!YEh(ALxGTS$wu z)18fq-FJ@gA-UreFZb)d?<|67VFeiwaNyMiR?*fOq-IreAXUU1g_m(CXoUXV^T_0aZFdZWx^A^Z9QQsupg);Baq{d<&hNda<59bD?vdakCKIpwcPTjkW_bqlHt6} zn*3Wp!x_2+ZmKDmldHn4E+)r!y~Via)VkJ&rh;5wuLJC^N9VNAnkI5=Gpzec&jJQ>VtEnjZK6&nkKa`uEM2jl=PCr*B^sHcL0iPRW zv8o{vG-qpm=jdLT;@qdV`ziiS$y-6(ZpTMayNq2x82ze_LDSVY=bzuTogRMv%Ds+~ z@0zAytLwJs6E@Z7kQs-aoA%Yv0Fo#8YoUtu$gs2LSBl;~Y%YWMPPMvUj7%3U`eTnMaa~I~gzdGA{y4vskTccFpIZ zM))Nw_ou73Qm9tTD*(;8(?7qWlHr!{nm@Q+fueTeEouEcyL#678Lxrk0kCRcEJBM3(=j zeX9LQlhBmGdg|!2gKEpcw9;D=0|GpRyHQLZ6mnh`u8XVHOlndpwl6q2PKqd;7*SGJ zPXlBtJ5~ojO7KS{S)Z|ZCFWly-~~1A4O(;6g7wYKn0DBZL-NH^VpHn&D1v+slI0r< zymN#EEJHQf#8XDuig3jZ4GpT{g;beF-wDr#0~&42Qq_Yt{Wjw){#IFuzk;+Ne)D)_ z+8qz_%))Sr{sLY2%gQD2)988=bw)5#5Plj+8amIRjYB7Ae@P`RSRE2RqtVwJ&vV5^ zq4f&`zC#WxfrD{c8T+s6%b*Wg8%xt!dlmhn>exVxeg*U-Bb9@rPZQHRfs&@q?9CEqtuRLsoQs zRrY2@n8Xf0ud(nCPCV)>NZgL&*$1xYl(>1kyxKc4jIcj~&b|X?XyNue`@B2vd+=s6 z1L$W(3^qzywHhIup(MEpP6TmP!RO3f{PRbM~P-N3AzXh37RXZri<4`J{)RbXUd>|HDPcK{)8fxx;_P%jP`8ONPC& zIP7AH(=(f~m-z$&7;U@Z)1da`D)m5PeSKDOG3~tzKlc(MRXdnx-rCZ#;uObr`}!5% zzKkZcyua@f-p6JO$#uS{uYWxDHOGD`8es@t16r8lugVh`Z8_OrI%=8GG#z!+?@wqu zj9_p5H~8t}dq%TGMMVMD6b{pH0xE8AIEci=s$2-oKk4y@feY)SC8HOanVD+?V$C*X zaH^hI40=S3kLVn8fYAabMq!!xRY*s6t2NzF+&b7zPZ6xxHt*`0RzdSaXs&xSZZ36$ zRG-hR2SrD6#TyWl0;}yaH4SSVB1(b?S#8*tHn`SH7vq-koQiIXCv;&!-|C2X@ny2V zKV)@I>lM1N7uh-jo$6HV`eLr2<(AzsgrMsszds#xF-{y1y&)3=_%H?K=&ffzzXJi( zANtNL9<>#Y;30VEkKjVi~js`AgzBf zhbzyJYJq@Fi(mE+u0$l;sWy{Bno72d-93u8@^?CihCGoBC75WYV4e19K)P9BsKh1W zQ|^vJge0gjQQ?OZdRr@OV*kG7Dj%7aYv_McW&7D@jJO&2I*ZCPo8eU=^Q6o-TIj^l zSm$q1Nte=nnIzmYqF?}>H{W+m11zE1!aWC|RDs7i;87k5!G!TK@zYa$;nMuFFGpt= zHpt7@4NfVz^c3gDCVen(q4JNCzL9Exu5@*DII#e}9RO`a$2c6HT5+)xD>lPW^c zpr%Cl?JPVP7v)eT^A=RP#8=wo#PbmEtGiD2s5CEyc?1F4?pd@gqE;~71MeI#)9qLt zv6#bYhEvexsdNcFM_4M1+Z_Td>U%E#&oqn9Cak_XwD9^>FBNe^ zVagL?z7poQ+h-1XYEYvDpA zyA@Jnq%sCl5QRT<Y?QrMEVtv~lDw*Z#RCjV4-!1mn`+&+oumf~85zl2f)Q zl2p6z$6d9r|6K~ggetmSISCno!!~92cfk|4x7QM>nTnevx(iYMfehBKZ0;-1Rz~4{ zVkzpmm_SO5N`gw~kt1Z&x`)12f4U3;eoTOxe{QnG27+g9+7}xPcX`-$pu)|2yYQG}9BxGtWN`d%E!cIjEP2obPnM zCR#hF_0zj1ypyNr0|?)Ruvb2k(K|6=(njCH3T$C`ZK_$_yrydQwZFLkc+qaX7sSw| z%j^tDe_x=)`eqX2Hh7w*AS#zW)y8oPbtf6aM7J6qS-1B62HqX)HoDYLXQEd&Ao_cB zA;`b7#+i}7A=iZhYjr;&2=U2aDELy-eUm}TR~8Maa%>?3a$ls|Q$>{1tbj|6uBiQi zbEU><=z_V^M_xIGR)5bBdVV+hXlZzNC|y)pl_WWNDV*@YWL&s|Yp3huV=&3t96Y zBxf{vV)~wxbMW1}+g`aU17->$e%XCroF{O;b;U3(UGGSkF*QbsbV$R_Z7jbuJ|W== zG^{He0ud#$FmcvZIgbEhUmdzsIH(E0VC=+EMF!?c>c7*VjY90ieQM_23QT^ zyux0RS`2mhsI)DaQ5+Ij6TKmW^YEjf9S08{BoJ+1c@-^Ik~k|`&*N3i9Ql!ssK4$w zGtu)D+$E2%=|!=XN@zl{#kEX*vdfAB-xCA1mxAg3;8_Pmu1q_W`s^(*4=xayA-#b( zKn9DcAHQyI3-Ob@JMzE3gRv98Wc~b<#6KGN!F8*s@wUR^<_He_*{x^1t3cN#^j|Je B_JRNa diff --git a/dev-py3k/_images/spherharm42.png b/dev-py3k/_images/spherharm42.png deleted file mode 100644 index 6ba62f79db409c703c0810dfad3cd97003070dc5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51314 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 z 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 Nl~j|ZlpUTq+1%LYjl?g3?!vQ`rY^U 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 aJ|ZI+HdRj(X%<6(hpeQMM77wbfd2q~Xlu#< diff --git a/dev-py3k/_images/spherharm44.png b/dev-py3k/_images/spherharm44.png deleted file mode 100644 index 83a712d48717b1d0ada125992bbb3b70082fb7e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41468 zcmcF~g;yJ2v^5lWf)$sbMM`lf4#A;laVt%cq6G@HxCeK4FU8%hh2riKpv4OWid*2D z-+Sx*3E!-hm6gmSb7jswd+)Pvgw`7+0$gfb6ciK!6=jGH3JR(%@FK*<1imR&j!6XG z(B0%z^ss@$2OA0lKI6D38@ZvNU>^SWLRII>RYgHzL{Wjr>Um`!<#=YBzC}K6GzfGE zn9M56$&tvBsG{IJL&blFs@IzjA|?hYL=;*&TVMp%1YqEuA`1cnz|^v}YNQFAj3F>( z+d!0q&^R~&uNbD=w|<2rd{xj z#QlH%BDC=T=S6b=HO7OMB}wJ0xA(!pK|ytOtmP|Z4Gn@o8Oz!%eFbuPlD9$Cg&WFj zNh~Bdz(p!-N&Q=&UW<#PVxvDRH{c)xli`B!F)NCJc4OoNZN0$z=ap^9t;nB&en(Qz z5CI87Ae`DeI__x=baj*Jn+1GpA~`**^>ErF6E|DZ1Ig-Eo@Nswe763H!r}0Kt2(Jc z^x(ea60;QL?@JlltoxjiK?h^gw#mvYGrVUPCewcXR(wE1TEu>_By!pr)HVEfSF#W& zzX~@6GkqLdy&qO2OM}s>wH?}v#4D54w-LpWbOb;z!0clOdudFVXv^-7v(4}8C_jJx z{KBRf)rI~nP^!SsBU*)}sj2C$Sw|#OC@@oX z1pErtWWjwb(j6WzBUmIaCY>FD>0>Pb+EE;@&IXL*i;Q&A2=tts9H2kR^B-&aQmI`- zA4?c?36K?qI$A)dbo$rk=M_Y0UuMQ94zrkzj9896rUGG@RT_ z7d4>srW?xN7SkkY$iaHSONp=Fz+6E|tcUZJ9nw4$EyrWbynJ=#@#FU^4Gj(7p%*mT zB}sWk2w+J1xBewJIlfZ!u2rG=Uf};0pYS_+7ij@)0#%)1banMhpt#EU&84$XV1}dGxNk##*(y41YB8&d;PXQn33*azRKKh4+SqMCKob z8-GS|LqifU$t)yHl=y@Y0lc8RM&$A6PBg8$*{XBQUXn6@=wn8c*}y~;r|e07?NB5H zteuE9N*hpTSg8qBEa`KO3H$kb?%rCv>~KZ#Yl>wZ1@)bcV-;{%DNd#mWEPyM^8n5))&ZO0XgKQ)NDnMDBGhCOYu_&BTOnoQP z2*d+Z^ur?ofJR8<}RE}Rc0zByaq5Tt(rOkxqw*vy@FHAa{Ev$y-4OWX7qHvJsHp=>+j{{{> z^0_FH@NpJmY@nHT&d%(V_^CPRNaF@E>fk=c-!841&=3pen&ctaaf~{N3SYZNTS>mv zofOdK-~PbET7IQ3qR7DjESm+dtX>NFE`Jml-1?N6l{MPU-E>tfPHq43BQR4hM7Jgp z4XP{jm3a^C&BLrajNPvBT~Bw9-a{-2dh$=INVKG76y$1j78R&zJwQnEd;u%G50<_f((Y!P?j}1H?0~tk_5ECD+-H0%b(I zuFbQ;b}PpaKsnUqM*3Sf>C4^ZU8;S3x@l4WZ)R}5PFaS`PERMeeka50dH*sAt=5#N{$M#!hOtCI zsc5=ByF&|Ve0BeGSsgEY6Sa;k@lL@$Xqo}SkpW+`O2_DMZF%fgCFRB}&1 zDCZ>5XJ|(Xy>H40Bzw2pZeD(RICgn?iJ=Kb1AUG^el08mEO-gZEH(vCa>^Y#JpUel z^l$>nBIM`qph%@1=3`fz80*&-!VxTH3(Fw+|NfFLq1@Jw4{E)=zPxNi{4Vv(#n@!# z^2l^hpXAK0>40!a)wR} zL|K@d+iuEKQ(z=kJ{ZfmIJ$Trj_YdIMA4mUZrY;*v-M`SuqZiPxoRQ#TLT3<;%SVf z(it59i(3%A3>isA!er1J;5qj2AXGMSJjHkxNLN2+`?@|9Nrjq{dECMbHb{(-+&Exz zPbsyxNmK^1S&QAZ8gEyBK{)+u%JDH6rz{c52q@!SsskIArC15ZQ>uP}*0*o9ZK}zl zcWBZ$8NpTY-(WczjmMAUe{gTRc z+ydmvh@oPYl?^!te1E$MD_XY!DX&{7seBz#Sr6SKxeN!{->plLBaUJ{{Ik&dx2|R} z$1xZCxbofHGY*rwFOrl>Zr=hi%W|e*4dmF)VfM%5gS)#sy^r=x9J_%HSmM+%6e+ek z>f#SsC`-7#?#m7eN66r@^;IJkaSJm7=6qFV!k<^zHKQf=24{rFJ2ENxco4n>s%1=F zHnoKb0}kA}DP*zTJGan6N^ouRZ#fUt$);i+p+jdSx$c|y{+#y{^jBD+tjkM;$1)6R z;wJuY<+wdxe@D;38lV`TY@Vw(YY;H1-+5^}x4fLOB34pVRJ3_Qap!XPdH2_^Ux|{Z z!iFF1?HgXl;fr(GWj%iUVCEKzB%rWM6R7_ty{F3Vn6(ttXDKV*$W9WGJ?Sb){b9ZH zOJif>E31C&?(43T%Ud7di@AjbSLEelnh((dFr*YqjQjih9N!~TH-0o}HLtIm|2;eu zSWtgCFq*9*_s?VxsUkQmWOVb{`iYQw2f{?}ADq|dNdX1&OOi)vZBJnIe6`*IhQtp| z|2G1cix0QwwfBy(Q>UnwuvW3)M$N7Z#Z7-Ho??GAKM42?wE|u3}qF5yf zrFAFOBwf;5SX1I3#OUPR-k18QfRd-o1=FK3C$-9}tE*(RS0=LeR@spmmJ45OY}gZ& zL&Czs60xkROY-1x!&GQ*^Iw>BMAtGWU<0RIM6)>)p>=xzw7CF|i0{i(o9qr1({vG# zZ&g*S#Ms4=JD#6EPs8CkGfrGpam`A(?@b8`Y+Js6{l~;Q&Ns^ORn zpWOB0-jH=p8d_+`E&=9DT7!N8Pg+sc)ILnLZSWCnQrYYns;>^|p9p~R4+Eh*@}ev| z@!xp_DSN%cGQahB=0N?2Q5~r76Y{#jdYt-Y#}PlpOVgnrp?G53@$w@xrJS-!;`~Kg zc#GVk=b~l2arFhseU>zRyIi(yjy>6k687F;Z{!P((lXK#UK>5` zil4+jI~Q3>l%mAHijbes@AR{YVpPdn8k0_V5nYvB+tZgS`d=>hK)PC%G1)exi@$yy z8R>pX>i#wQpA3mxwMxEWnU(Mj573t|y)|3^$&R94h8gSAq~^SM59^r;@?ZMu5et29 z$m|>)tBj{sX$}Z`tr&u27~@?>$VoG_IUnpc(V9RG!e2_4Du3mG2I89smQ$I(6ze3) z;>y0LTPb}aVw3gs@R*sJdS$yf8A6wue?C9Pfu8yR0`Q8H6TQ~xx2YgSEn~x6CWG>6 zSJB4@{WfWLdnHBTnP$KJlNo=d_(c9o&;?Hjw1jNNfO$2lvBeY9nrZ1bb~`baWp~C( z9^Z4qaNRfYfey|E7%{580qQm{_c$5NvP3F6o12BVNIY#4Q5|Ztf(cxCk)8`H)D)>7 zYad6*u399mnz*V?JSl@-3i2~E@RgldJDw0gypN3oWjt2jkiH4Y^b6(*uK2#}8iHcbE8!`8Ni}Q}OpnqZ(=#x&Fu-n*qS1 ztCiAADn=9A)d}SlMVZG}qjJp5m?S&Grm9SHrp3pTVLTA?3P_c|GD)MDdF44?9pP+` z^Bb|D+>x~n;T202wyvCp21j(0*pjl6ta~2gRsHUCkG;FGA5Q>W&_OHC9Q|4=JGe3GEF|29wE-s1T`3<%Q>Paee#v6d^;a4-Ecsr;@3RF= z+NNgeFGwwrq=9zYU-4sd3Gu2EA{!ysUCK8~2iDs;P7lt;Qjb0hY~ zI5Vo;HDX#rZ@TsUp zu%n1d?|Y&BM*LVXjDSXe!SHV+o2@jT2{Jo z*?)Hi+!ShXUEpA#Ymu(?puF{Tch@p7Xc?b)aeL?Y3e)GcUD;v`{YWu6$HJV+EI*#h`&i5uY zV@4&f*BW}pH46%HR_LQ0(34{F-NaDTQb_Cj^5(2En?qyCBjr}Md)~aFq>WG3GkG*N zz0#tDl`y82I<{9SU>!Sz9?g;}F^b!*Zm2dF<0$}xw=SpBHWD+DE#}6U`F0tdU?An$ zI3zV=!r<>2JDkP|M36d{SY?KkEj>(c)W*h4d`#tk5T1LMwbuBck5%|NytS_$3qfWP8!TtC!@xfYuw_pr=UqnO0p*S zOJR-01~{Z%St_IKM0avvJusy{*7m3-Dn+vHr>3T&I%wEY&Y%+*r#^2$>b@X?fxZn0 z>?NV7*3BRXpX-R0Q{pk{nlu~CjA_YtZ&Xp@^!|X~Wy$E2lgROBtSL&3&4~nS8x$=` z+1uM!khs4XS{SxAV@n!veA46ml38k|Os32iD<-~Poupa#kHKQBjo>!X`VNoT&;M_s)~0fDZ!0Vtowk3@hh5Yw#J?iofBBW5}eEwi>3>4 zD$y8te998<4%@xuE5l~^FSP{G?{9vPOap?Cv-3%S zVcNk^@wo9vuL6#K)$1Dc=()%*`M7p*A5ej)F*Y_Hks3-GhH^&LX(2a;mXtdxD!9n~ zI|V3|IH4UJ%;j1fdfFWg_=1rMO8f~PbtFn9$4c119BU+vD5C5{{#4({S}6gJ?;)Ymq*h6-G7wjL!prhr7D#j9pdxS zDUITzmUEXw3M-EUDdU?jF1{QbAM>>DuUyFtkh%k+#I4q4T0Y(jTfNHnd?qI9xXK$& z-;@~msdvFCyvaPaBx?gxsQM|_Po8|B zT<0%KUR5Ied{TPy3Ayp4t*a}~Lh^gXzJ}+p>+J)tsFr@9*f{PB+qsQa>s5E*1Z8OD zq;@P$Z}mH&OynX5)|UZ5hJ5{6=Q_UPN~sRn30U#>BtwUjRJF{O*hbbjKUb`05+kED* zzS6KRsG_DjlYmBeYGoz!(no(75k})=34l{>i*DKgG(ypUyr+k~HTPMKO5>*Q-&pG9 zwEm&z5S4_CvywzeL6d6owaW{>pc&yzSj>HEXb|`u1JE1Cp`M;as&C$g^-!FxU&rH( zcH!rT7Sih63+SD6OL(@>;lz||w}~`cJF0=Dx}+~#F(P+98<_9R%;@?oVWlwwkGC_vVNxnf z?PVJ@>ty#k6wBp5EPK7~o#uPk9+@VJ`$>ZQETxg2Yw;G>&A>ra zM{ct_PG+UpF|T!XzTr@aDt-uB6UpXcqwFzm_|J0eOw~_grR-e}y#Fzu%|NXBMRV^@ z2PJ8xD~W^^P!aouN=MFRjTV(o8*WZ;KzFA;b(WkB~N_FHgp?$&YuQ|FU9N_6dN`ZhaWfi#eC!o*TXo z=Xp=tqZW_+S;CHg1oCe2O^nHugTmHWvJV>^gkDE}=B98p{pw||q@-jtN#tvQ z0w`Ta=6gV%j~qofr@m|U1_A-i>#(Y+mrn-YFl^{rAJXfOvJ!CeOf7&tfCGHO*XtxB zo@VC3>bZXVoPzttM#WJrF8Nhi)-cnqA?LvN&fDFr+p{!Y#UxES7sgtKT6#kS0x7fk zhVdC~KN`{6FEpY~!phA^uI|1)X@A-2>v@K4w=wI;((*Z2yiWYdN*m{Lm3h}!#!Pg4 zP|yU1TEDVVwOPAb3K{>qeBH8R>PO`ySCcP~5o5HaXo0uqdkr3rWJ}~;c%q~?7_X=f zCIkX4P%WO(Kch#L!a92P!0RGBh4w(d)DQ5OUrrqwALhR=-ou5N9V=}2YOD>ETP-|2 zMSyS>< zSl0YjS=?85flddrJ}Ca5*{(si(h6 zVO2Bd_V&iZxjsBPf}V8QOwG>+vShuJAx-@7t3ZYQH)02zKhSkeeJb7SKc)IxeG_-n zYE$*kzjx6LJ-pKQ%S58L+eFF8b=A-w&4;XdYurHYutqF!WRcd=-*OT@pxw`-JEQ8L z_cRN;EfdIpV>=-V0njYlyL*}2sytVdM`24O+WE%QqsUYgb~}wxtTWT{pSPP{o93JN zmM_3n;Q@mIp3nBsh`=OIuUy2zhxEoKt6%oS<39ZU5xD4rL>IDONZ>Z_9BE=kQJ2^E zL)YL~?6_({b4D{q??z0xV}LI(SwaKYn6>g~(Hu5A&$|`W)CAmi$EZ%dxyUpA#5a6+ z*!f2n+Vup-V&s*2>Bc^?zCyzpJ?hAtCO1y@7HWuNGwNoo!? zeXdDvnwmuav4$fz5u1sFUTmnO2#N}e0Xp%!aQFsW@$4gUE6p&?iGofa$tK38_|V;> z=uE}om&^IZ(NvcB8vi`sD0g~}<=a`EN-XIlcjBbqVpbOh6Uz&(Y z`BaY?U!pmp@u3x|EP=@fbO70WXlpsf@W+OhwtjwkjNc&)Bz1&gq4BnO>y zK*a_@w@wOpZRlXTcnoR}|3%cO0KCA1UJ zHC4S-0w*XNZ9U48W23LV8Dc6Hsd=^zDHdWd%G(MgfHn3t2+{=BF#fq`1b>MCa7zt7 z_SV`SnvyRST90YeVsGj#-y72_vB|a=%eHBT$Wk1! zhhNLo4?4J?XkS0l^^DQk-fN4(7oOHOxN{H%a>=F?yYUgC}UjX3E4d7bINKuiBWpnqc=qU4V7u=H$Li zx(i3)1-HUidty;+&?%Y>mkVn>qc^;Q)M(`A+WnJE@l_A3mtHZ`Nn~BFmVSTxY_w+c zPgKv_=KHlqJJKX}O}FEmNbWV#K+g_lCyZ{Q>_e%RkkD~k@&Nl2re2Gw?RxrzhnGWN zmSQURJvK>uk-dFGKcuj|l2@HNVgx278mum7T$!@mgjYR`51JaeGV&R`Hc{!lkNC{J zkA(n=(+aG!ZuHVn>=lm#4key_Q}jJvv=s(>;SKyebBUfcu?PFb>K#DT*<1HM*(pgg zw?^LSCk~@TKx(p<{s@YQ)NT0P|3_^2Y$j;Kh!}D|T69OSQpY5!TW^g2@{jF#LTW=* z78qS~P{w46g-}7OK9;1X7*K5wX0@+)GqyG;7K(3Le+K5E-ZKua5lsUR5%&2{26J8E zAU`0niawk&$0zgS8a;ikuHxIqc5rpfjbvk_(bq)n=xFXnbT>Rxp;l@=dE`BPil6Al z0G-2?G24kh==$M!MFa35QCCy)bJ=%<%}_Ga$Ksq^rjP!3tbYO2tr$(I{itZu+p&Rk zB9u6lnGipbsP4Y|+c`*t6NETEW+lT-siJUa3u-R;+QuGufyDyn=lx(=($WLe)$D7! zQb&1vA`yt!ET}9ALc*k>)k0vb1*u+c=zx>ezdt6oTMQQ4L3mDb`u1cBhK)08YyE3& z?4Q=Z?0q`z!y?5-zq?4#fZo<~sB*o* z^!`LL+-k(Z#YV-mH~6!lr-5DtD@qG{^|`N&StxAJVP&N~L;6W(kOE5Vi-mUWRtZk5 z#YI4s{u`&xu3!`dT+R~XLw*c!0-(&yaf2$t)mBvW%wnL=OEi zu_d+$sT|RV*wWdybrG4f^fi2Yl@QwaxsdhV3lgG2<~W6t9Vs}=23dKI%FY;d^%;_D zLM7#6ushk4>)AD$ZDn4-Dz7$b%c2{tpzrofg|q`zli5m~-Y8EkK4dx|+c^ndr%`>J zmEh#tbboyv@8`?sD!9PtDDq=a+K$bfJc602`CIoYU<*@hxkWS|&Hz5iqkb zDjsG}csM119!tl;UPo3QFcd|cw7~jJ60RhktI%w?4rWKlVYL))XB`s1vk3jubs{&T zfxchZbXgL8Jb{^n8eRM#-f&^V0J)Drn&@3(0ctbGz2#X!SsDB1xD3lLq=06{@OT<7 zaXuyhaHYg@-7?o9rtnvuh&8FLi?p5xRssToY`?n$pS#(--5+?o(3xVD^2z^-0*H>2 z?)pYaI(hI|^;&IhTW&5rH4P13tj$Z|wt+tl=b_`|UmtJJ{eO;SFj(K7xe@{}IKL-p#%O|4>~o@m%T=30)D6*z+*_T(VHXQir_V9VY!!pH&o{=!gsPZ9@v1o1Ph=R?Ew@(ondq} zzGiM;#tH~^sWWc2?|q3fKz&G?;uD{KSb~fMJ#XuZY4!OAAo@pBxe=f5`l1r(B@}V5 zpMZ6cxb^D(d);q?NnUryaf@hG?Yxf{T02o#3dQ!Lo>5$6JiK@f;mjH8yW}C$ z@pu6)$t%S9;~!vW>UTRow2 zS0X)FJ~cAS-Q(8FJ^g-!2UMQc**>$C#d&4?!}zImaC7DRFzkC#QLsGW7cB6Pt~I10 zi0`StscZT2*x!ui#66E8a>%>zaC9p`x&Rh!!Ps_y$-;+3)9@$?ybsW1w!eGN;ZQq} zvuDm?)YN`|Xi*}_TrF2|f2-oH1XzVw$Z+{S{ekRGF4^ZU&#d}y^uP$Kf!XJ`-+|-|Dbkj@9oFRcxp4uYO#183oTTh; zw);snx1%f`!2ZArkS<_D_}s#WZ|heK*C0=Etvs>&%dp?4=m5GEGsiL|| z*-b54F)wE&B`&7qeNKb-NMo9dP<<)Ddl|j=Yuc0G;ooYoZJF4{P+0mPqfRUBLAr$u zNoCihvS^QXXl9|gwqjj=zZ_U!3uY<8NMKL3&Y zLU+0L9akpzdSG_I9@DTbb0)fQhrJPBv94C*FZ{-5l!%p%`6joNj-JbEjToQG<$hO?!Q))AysqfE>C+`5+4NJS6m_;`(y1{dN z$PSvGqLx$zc~d}kZYHNAC%R4 zNR4`H+PV)uAnz{4{KNml#G3whK8r0Kt@d;6iy&K_*URv8EYqh>n$VS;7?xZy#N+y| z6jA;#*Z!U0(cOyRrJL4#x0|*X9CL9p_0M{gKAuvCl+S3Few#-qyv=bo_{}2k0wTcv z+8ps@W*a(#h;{s-R^#l7{O~TeB2eSu&vS$GJ|e$Y@AHiGQjY)G5Gu>_{q|G53Epw3 zByp#TkIF}GAJpVzbA2o{1dN6VHlVId4}L+!D)^=rQhJ|L-J zuVmEr{lSNyiLTeHhUD&uIp4QY5^Sc}Mo3}V<>~9~gp2o5~{o62Lv>v^aqSTYH`G z)-X;V$CK%g7*-XDlN|I53;58?O#Fuok+c7Opaf94FuFH4f(qGEUKCQ#iF90dhq3hp z*bkP)O9(E*pTH0Ye;F20S`E3`HQ#hMURdSIOGu(53}z4iG}vn2xzoMz`_9VXdS%@A zTD$mq)vb%_7JI@2i2+_0U+c==>d@udz0me_cxH4@#1uro^XObL|JL?$W>|%t?&rg; zNHIPg^S)_n^DuLmL$Mj_{t+cV`Y~^ME+Mn#y{9Pc#Qpo{YEx`RKysrHLTJmjnjE7y~1JD+<9) z8gWE79l;XgOqvLznLEF+pLBSkRB78`3s~n!0m6rOF#IG#ahF>iLzCdK8jx*a@!8QE z1R9qp-m3nzxK02vpY!de0Hr7BQ5qpT@^P#P>aAM9lAZH#H=az&g*K^=ScyiS4o-G; zcQY!Uh%N0U6YPp)_QKagbPbetx7TgnCgI9wIn{Lc9UH76LtmNi=$^MX3%1{9Y)|w| zbg{KB<|5Kpr%xP?O+vE6@_wF+NW^7F?zKJwX{*O|kBO3~9m@nw#$~xEEy6tIPDKc-YuFICj82-Ue9x*|?CCYxHct-9- z%t+UliM$`9-pDLi;T!dn zYP0;v>J>sPC9T;=4rSbM%oJ$KZvPAE_?1xYE3E+W#s`1vN-#%~dd5-<7KE&Qeh+&P zW_(t^AB|ogp9Q+z__N|6u6orvP4`@B!H)4gYRRf|PYz%f*|;>p))1tL|LNx6sJgs) zcjC;U^ImesMWo#u#x#CsXK8WmM+>Sr7>TKVGJzxU-SSRO5bwEiOYTb#e>T7+uUnsL z6i8wZMkfn#4s0QnOi+xTSbfRexFuNS0m@A0_#YuKLs%|G5{mfL$B(%5ea@eJ?WqcQ z0LO|G5Gf4zJ1pv&^TMO;Jz2LDYEuRlzMp^PbaJ7}hvjSVpZ<&_Fgo?+GYS}B$z39Z z%k&*AQ+zbA%~`e;RCRXd1;8w={{wBVk1I|jrLE%30GDmRaUkXa@04Ywe8{~DlmH%V zWvl77;k)kZk4_CEO%U=R1qZlIO+ImL?~BueP6S{#gsy|!GR6lTYi<4^l}j~XymLj! z{uZ%aUKI98R4=L*_1Uy?uGf^=o&oIQ4FrcHRnnH?9RYZo0ER!vI8&iSvHu9eW@r3y zB1<}fefa+nE%8O zuUqnWZoK0$E{T$NAKtLEj46ejf&`6xu#B?zz&I>0_tg`os6`U9H!jCFoS7_tstMBC zn%P00L>R&9ts84NQ{1GqJh|1H*gr1t<<{c>dAWu_tZ$WQ_w#%_1y66cwff&MaxM4R zz}jRbI1W@^{RQEw?=Dg{<~#J}N*r_VLS9R!v*&SlAylL!#SL05O2>VonDzmyk==j1Y=mQO)=c&Ki-SXdBc56c126mFGh|^EOG|Lb z2jV9%z1hPcmSo-Lh9m>cml0>P1#7?Yy?B;38Vc+ zYHGpzd2s{At*S;@u0%RK3TymXt?`pMP*dhHz!QAvCXhS!F7$TN!x32|ej7Bx; zmz?FtXfaIkKoWyDwzQO0j=A%?)U7T+qwFUrM9hwaD%X|TnfXa?S3f%{5J#l{90Lo| z%cK2fgmil{1vMq6xlQql1#o}AkN+(4oX5L`F?>x&&}HL9ss{};%OXgu#3XtD88TDG zI5-D~`FuUaovM*T6NIJi#UQeClm}@MD{koEa>*(5Pc*n49rQJizx+=<3~q(NzEP%z zk=$DW9bw1n^#i_T17;hAB$;bp7TP>8Yqdn&$OvhKNn#Gr>whIx^U4p^H+_pGuXc7> zRBv~y>cg|C@H`PIQaK>pmC;3e0lqLQ# zw{)E)|>qQf<@^*EblBnH9A7V*2zz?S5k7$wY45Je^C(T5tCmsALMy>y#! zb~IbS8=}E$$~#Z!rxOI?S_Ufl3-0UDabo=E7p5R+ zu49r=Hdc!Z{cUiiffyZ7R*V?1il%LgI=p0qxP~*Anar5oD|k*|=j!r@J)eI^^|fIN z(uSRIr_VA=8<+jDqvJh7CTaM78hRm^S-Bp|);n*@DnJgp1_+`g2(*cYSQ-3%ut8Kr z{>$7JT>ZIcE4aF+tmCmo@3st-5e{lQIk;ntWDD|d^S;X$Uu1wIX=5vI80*h?bYF(C zrPyd=+K}BaEtJI}|TNgk^ z1mT7PJ1o|`wqlS@-fZ|(L9qi?zR!NoYJW6skal=U60yBj#+#&&jkmz&kLv~O6ZwTT ztvNMYHn}}6l9hmg#HQ}YDnfnar0(Z~zMuBVy|occCvwY+QA9g`*;pw_W;7iI z`jV+TveJmjGn=*MjS7`?{9!`RzIKQqXi2m7#R}mZCvJnnlgGn-EYta#77MInD@(Cj zr@Sil?4Y27$=)gT{s{p?FRUi89{2cdHz8yjAx)s^x9>4H@a3?og;Kzp8+=5ZNonrp zc;s>YZkV=cP3S9JY+Q96L*whue;=J`k5)u!j7mp|aisQRb3$#7JqqRn9c@CrYsh&I z8!$mnl#6A$r`hqm`4K<4kA;TXuEXqSO*_*MLSa7MQ&7tVS6=gE4Xi}xH@$cbCUM)9 zX{H;;&wPIQwqR+T#kINR9(B*6`Gm_@l4gnU@!w&!H?&-M)cOBs^07OpaY?mK{@lAJBkY`Ua!PZ>hzob(#_mYusofJcS zD5>>`z@zp8=47&6-Z3&E{8$H5hj4ogutr6dL&nGaz)qZmyi48Z;BrhzqxFH}zah!U zp)$<40Eg3~aPDJz!E%TGRml$+U<3v%+q%8EY3uEvy=?LPacoRmw{a*oU(kT8d^TVQ zwdjhWfW8oJ-5Sh)8!ny9`!^N`T+eVEo3VLk&#;IXN0=i%U5l-EZS1V1Ky-8^!G;Z1 zi{NLDkp6ZIkcBuHi3Xy2oQ1FAcUHqyjC1xZx!BLacMbR{aj7Qz9mMGmQOy{5%_%BvE{P!;Y@%Gs0$!Ecl_`|jYdPm zo_GMu+<2DA?A%;GP>}f|2|7eOT$&B26b>XzAbfd&BV^qp)4*jBt>U&EU~;16-UzDdG9$ zkoNUUz2{rnkfqsa{+pjLi5i99Za<0N#VJAf>E8N!QxnY0+XhHQaUaO9JrT(mUmU|2 zr9)YWr=X`?kcWn;{&xu^(vadaatLRXw!0EBYoCKc;;`D#*P~B^(m`cBgKL)t;K6xn zs1QRW>`D{Xxe7;SG_6J3#AHf2O!RcG!Zh-v=p@gC5Bg2-v&^K8#MSBT+<6=F7mILQ zn7}=QYhOG*Inu|m5t+PbESlHoI6Tybk>z<@3&1b0w}xJH&?GkKga0%ing;r9uunl> z5o#DVA+B$5>g%3W`|cIkSkq*s1+VxD`VBW@*>$e?1yfDMxH?vWJp1rEq#@O4P*u zz?hqFoy2N&jiw$Qs{q3#P&`x^P^0)tkR$-?YU*2UE3;p-$^Inle>{6=BxvJr{7CuKX;k}rE_ z59Ki?Q6)JjeUc3k#z@$=M@S@1vDAVzUg~P?!}Ex7sO+2}P}@(vRYM<-T^#)|hC!bZ zH-r#B3I&jdW1Yi~Dz8}S9tjGUYpV@APvkqADP!ud{l5P79X-}iM8juCd8i9Y(wNY7w#gjw~8xh#5jpV_?@bp8p= zXowW19}J;#I5wLDSv|4EB_FC8;^gXe3Cz9V-Ci?ON>k_|f zbG1IVTji23&kM2#?%YC;KKuZTMK;zG*~3!zM@j$A9a4_oi2g>Gx-8cOML+-#YW2q{ zf~2BKiflb;-sS>B-u zp{-nC@71mnD$w;;NC=Iow;W7Hu|T$mKB25iDy;1uuXQq7ixj3_h!Ehi|0oV)Kdr|& zT)W4C)YBm(SB<$slv~y>qd=?J*M4I;9R!-Z&FbAw5;`Io#b}dMcE`3q|Ay>mf;>%W zjiysA8^m2#F7s0+X{vKb6MnipzrN>&Ky!gy>onA!7r?gtw0SJtAOil%Ro?y!+1*?* zD(Zu zuMXF)E#wm%phd;o``YGH=+Z?9^@bw8dDCc$#+6ORKG4 z7Wse%Or0#kgm>X8_$qd=fxqe*d`r1wExNKmclvCJR*8tw{*q!Wg6)qNgZY>Pmn*GV zMA{2a7IVjgy@VZia_ZLbBI6l*Ey3Xm82(=Cl^-SuF+KF>skCE4{FL*k3k@XCjEk}Q zxB3Ir@k|i~WEt$udOFlj3R-yu$yD+5!FFb+N*Yz4sNI+tslz<2+`-NyTRCML_Kio- z%9HMkLK;otY0qDFYBh=we>w?fYj4qd+g5vs74(Hr(C=pn*w$QE9_`GI0Im90_$FzUxi>gwpH z!SQ!}K_c<&z8KVcX^i$eWGpt7a+w=>Q=8IsMtV%e_7jS!Yg@{r)qNGr4^+@;>Sjjd z*;h!Z3S@T>97LE8iS#d^JR0|+-eaF)?7>KoEwT?cYPe=TqYZ>Pe79Gt>B!^bn6q!N zA2jT=4W;&kgQ|0yyatjh7hK7lxj4NiD-EU9cTqrZP~y@%PPajLRE_T)`C;wN)v`&e z%GY;Y;s{H@K*`wp7lVa3HWrY=IIvu`+aMjH`zh@HNJ?UcJ`Pvb=-)R{@Syk9$Uvc9 z7}F0QykQzHxbsvfwvT7yk1yB>PvY=PC#9YSh2EoZHt*RO@6$iuEj;&VqH@E(4Dod` z)GsmgbBW!fZ4Mr_Z1|OHi9E4Y0oC5sVtb<9$FP*{+1vQ(e7xl=PBPpQIS*W%)fd7J z)m>dVfKRec#=m(hsyuyheSIR%&s_+xP4UrF zU$Es9_vXp0wT;)FFK3gzPqTv$;bPY=z7PDlZnabz{ML0Q9DnFJ{#dsk*05GP8?-hb zMPfTSU&9rKK&@j<9|&;g?-|V(w)s8BDys9q)&3fBQX@Iu5r?yvUNpR8?ma0Q$y^EN z|2_zc{W8n4{_U6>b@-&P-*yB^IySv-A)UYXN?cf&-wyKkOwG;BO}1X!fEz^;ELUtB zPrVAkrmlZ}tjR(G>`DT7MdS*0Gi4C41-r4iSqI=sr$UkC=`A1UISEBK4PU=@VCdE0 z2sxlr-m8*bm}R!D?L@0tSIFy-(?5OI*|>pH-){{@uJjZiDyl6$F;-0d)reMve!{Lt z+2UBCJ_%5=Bygx=X5o%EI76v*kf0-o?ln7@_FyG$(2-)tOMtY;=I?>D5HJKs0 zKV8pkAso5r53U$>BDW6un-F)3{i#z{!#sS8kA&CHCJ$^?UV=KSDveFEI!wKTVZ>z} z&7F~cEw8{WEk)!%-`m#XGLqOUa1}C}^(>v#VTwJZwszaQ_w?aIa-TdLSBVsyBu@Sx zO=lSuW%sr52T{7aYmg4PD&=}yI=Tj?(8_x8Wm`*l9d ztU336&faJ5>-z0UO8LZ5Hu;>!s!Gh20Gh};sU`TKr)XWY!Mex?Z<+3ke2Bis_@f{< zkZF~Wm1hWzK1#CB$Uic@3Elg#wz*e)@=Yr(>CM|exz@O(ZDM0_x)rn$WHQBwuN(U9}JS0we04ztSOk1l>ar*e@$alrV067D~mr~9dCP^?!`YtN2^-V;mVwDG{zRA&vLxd$g5oJHWF1{H?y&}tlnF2 zHv(>fc9d$3NQKZaeC?xy#X1VjF`BkOLA$tuV__6mJise1SNb$!M`W7T3`zGo+Bs*m>-Xp)x;Z0vcBSqD-rWD@NJY@K^ zqIQbK3wSTD{GclShXtHj*f0T*r#rbF8O0WRJA)ZNrZ|%_#7;3ddhpDuo(!_<@{?c( zE(JfJ$orB^eZ=7}CwJ1A3KJXLj`qFD!7p$Uxnjupd95J8Wme=eCpzYj)&7jwJd$+I zIAgrLKa6WGk5x}&UkNZ_jBrX3o4XpTuQH7U&H7ISoNyw3)i_qeAF7VK+DxT7q54Eg z(`R|%)m7q^Zbh2#fO*Nv`Bp@53EdxGgETb(5(fQ{i0HTXVF z+=nzL=*gTY)Ixc#8`YvM$MoaEhDy~dY`KIy82=AU9OCvEK`Z9nkN4Pr*t9dy7vg~3 z5A>NMLUBY>Dot+W!|>$qHvM)TMV#a}9J$+`1UfuaHu4I8#*hFyKrv)$!U3uwg)7$|5Oz0F zeUFJHJ2*aAJ6>~K5!pW@c%N|^E$G?e7?~hI>vluzx|X|rXUo4TXw9C`*!63rTK$d0 z@daaa-aIM3`(0fZwgg{w%f);kEOX^?s*gRw$}oQWieSwtYKmoHyB4xAS??Lk|XTJ-w|2)D%uTyC`^{1*lLTGffss3?3w$3W(ogS}H zc9uRh%};k+ZcWUz0_WuQ%8envu;7N%r#m5jmbl&@NQ(K%j(sL}w|BNy-W~Oe(e)lt!+4JX?QGI)hUpj2x zD2O_K-s}^?;d_kvHKOB9>x&LHe|qmpp5vBAXocUq+>bJDkptG5f*qK~oK)+JHl+s< zLG+!^MjhFfYV1v%4IE{`YSt?Cw^~lP zV_>zG6D~zbycn|D{w!_=$7fL*$hy#L1^z3c=w7QhKx?3@?%7Q^o4c+*?z|P~zeTzB zek_F(h&iqo6GAt&v)rARI>%l2&QK zzCk(KW~3XUJvpIxgL%3BR1}9&3SpO0(1%UvJB~vqOp325EEaM7EhpB*#}%Vldl{ga zm*-0)5>FSimQ|eMH2>M)b6L;THxbek?`}llq~X%T+?V?KQIdb|J}|mT zYJAFd^0>k8%Y3kHyO4~vsvb&k_rdmFK6BR)KkMf>8pGi;?%nwG6pAS=DY5hw9!VRR z1}GCkVU$8{$E`cO?tj#Z)H73NPEFc8SOIC;YlL}_1dwlu0jZY%Wg+d6`*?HrqZ4ok z4XY!(OsLBPfErB;snc2xo66!e#pUHOfSFbZvt~Yhb&&`L28a*x4EVq!lQ;9{iAI^x zw-Q+FTlmZ%IQi+hxvg8@yT?JZ<4329@d+37)6nLZmsfpF?|Cs16|0%}`RcNvx0Dbo zO7FGu9+O2AEH{Z91-lcYt2W`f1Vy@oLoWGIWvg%(ubU@9c{BsMm|I!#Vk07TStq4T z+W6>8k9Bon^<%RX2G;Lcu>y8(b;=ExE(I7{#EA}rV@(ZSOWPiWVg`|pQxhzVoKZ|+ zJjZL<5PCn7P->60MnjkK$N*8}xt|;GR($`W4|bUZhcQ_VQf^K)DuG^3_t+8bQ??-9 z#t}b(Xif9jtm>;O%^df~*^q{vKDhNGT6FvxE%%de<-yKQf~VArfwdD*Wo|!uuwvQUgTNFj>O3ZAe-d^d@|d>+H#`O`oFn9Zs!J+kAb--)IbSQEvsa^ zawLiQVp)+I}~xyk+OOixU|hL z?_(F5nQ6GBcks^efme`|&)UI#v&f%ctrt1b!|%}<8m8zMeyht9c{8Z4jrxH z{~9t`e*7qZ*2pCEa;4i`mS;gf6!h6Zca7uilIp3j_apAQQH@K%+-NOC{|xX54ynhUV_>l+}ra{Wv&*(c| zDBa?LaaI-a&YWeZ=9je3bz%<6fpB~r&Gf6&|Awcx(UM+8-d@gaNMhL&!THFr)1#&X zaCtubJ>AHHY(@oCJOc|`J>{yJ%B4=cZ{Fr%A6?Om8bKtpBg?aG?*<0gN7}55fT5`T z)k$O!XdZBc&He`J?9nuL&C_O~^tvg8;+uZ z>2&<4V0RiX`_jp^*9M)!=zCLG(&~`@OFK~Awx-8G8WyYb|rkb)6B7n%d(|C2h+_nvaE zY0D|YkuY6ZlyJJgfjwh)Q|Y`W+TWAK@yx!V5H{ecsnEScdF*GYjj~~Uz3NAstSz^7*RrnQG7i-~_X>LV2!1-aVbE!q=5?OS#d?o?oRLI+I@kI{l9@m!yKAI;?egQ2*6qw^nx2UX`ZtGP^nB z4|ltu9=4x9T95HfLzRNFCsMT{QTYP7@VwDr* zDoI_n35p!^>C|K-5L=WwNFnq}?QVa_hT23oY)#hL)2U|b(bqAv{TMNm_^hA&5?}tM z#H+o^_D%c!HTlE=MsdY;6nUBht;_H1m!4{Z`QNUwyt7fKb7DaW)(>TlE~$?0q-G1c zeZ_FKpTSv9^o*>WvST-hGg*?#!Fr=^8|qA}H@yKnsK?Jt_|#BRb<5f{*A5GN!qiYx>X-9MAYAXN4 zy&k6p2Yy$jmQG;P6M|Y&UOwh{btkv{z`m+R@BEu_*Z&^BXe)|2hmIiXuh*M0aV*vl zt|Gf`eA_o^kAaa{FgYsW1*zxj8 zh;ANRYKmujzcvHsKbn~y8(S_rHXKBnuB`PYRjrdApA0Q{{Z7P02M!1ak7`g%E6$v6 zsSHlPH{XX|Su@|g+C(J*lTk0;bPfmN%nOa>Fs+iFDDfWl*o~7m>rM2bJxH znQX0OOQV=uE6&x1)*xFaGoSrvXRT8~v#q4u4`UYQ-U=@vbPO~x9cgjRV+uS_5Q7A< zk!v7zD>mnh5a?VJ5nV|aK$xL4NXKj{mSOb=bZ|)^KY8MYSf1;F?Xz1_;-Yg58cHhOp$bndnjC=cJ5FUZDYrGK&9__S21i8 z4C zm(yRBueuMkt;gcLB(-N^N|1?FK~yd!?#9nc@)XCEeF-_>`z&VITF1VFAxQcn_`4t@ z(2r2Vc*$)oMutUlB+eno@D(Vzedy!;qFF+9sPz)4zUjP{!5kUlPRW}HsV*&Lwc8;Q zd&U;>&ji{Z7sQa#wQhMUiUD_u1p>j99^0{XCu11Yg^(gykUZ_J zE2-5UOhG5?2-}Wzq&(rUjsmWXSw~@_RnY5moOMGVWl2>hADKVoSbP73#6s{r(X?q@ zf$C?QBXy|9!VlASL3-O?ns^)7a@!Xfj`j9aDO6jj&fnP?pLtdCgGee>{?JW`Yn#vFKtVn$foE2 zc=xRCeIu3V6j_2_b!6jASO&k%K9*qr>S9H8-C4J{BDY|n5$-hEqUq8_NE_A)MO_!3$DflfMvM;DzYW# zJ?i_sA>$|`Q{_$HUa%$25ALEcio~{4{zx98KeXv|-1%x)#t?@CT)MooE`ldSPhbXf z@c+vx@Pk_sG;zryukx%ASLk@g(XgYXw5Yfug4huGrVlFrD z<~T4{VTK$Nuk~H*S9pBk$cD9*=QDaZ$|4Pf@6Jlt{`Lx5KIVftzW>9Vq3f^tq-MBs?VhT(RXmLzA)J^m~mC!(9w$oxr7;FNd5 zO+=o+PP0d0_`Jam-CfYQG91tBb+beQ&QiF6#W^ui(Y2R+f>5FL$xClqLVOXSjXCA6 zp+`Sn2BH|}nt)fJuBnXaqHjpv6KjQ07QAJSoirmtm|{&1X;+q0x{AXH^O*y6av1u#!oazcMJ9GR~8WNHsGLraj4#ebX< zL~yiO(>B!AsGIavJ=@UvY&t@xNkQrT*JxN9POqCX#h%fulx^LZxa1bE6^M5D`&TZS zXI?r!)+4DGjhacEe(&=+td%12S#{Dr#9>K)pC7m<#`PQXs(rt#yk$F4mCbjooZean z^+j++yT{Vbz^+z0$1dPPQ;`7}x_~-_47iEY{`}ocAJx+!Luof*_l6V9xQ)nEx4mSB z{gtb)_!~DMo*7|$P-PufN18|l&ij2y(V^sUK6zbtkq_5rXc>3zxrDeuYPM??AeqC55r) z{cyO3QP`L~KkE}E{)IPQ>+^ho3m03ZFNiHR8GQ|9a&Rn~|B|6~TMkM@d3t^8*azAP zm~nYP%SblX;vdD5|2fsNRV`0A?99^SgK@XwFzwR$^3jaDWy^`7R=JNcn?(M&x!wKe zmi=^K`Qm=Ue7&WK2TYnBTNS0uGvKf3dZhTxJF$O71=}1So0KOutbzT(^mPE7Qe|tK z2!5zjqDM>uawd`(9+y>d0D8C3;tB#h^HXFz7V%X=5Mpw^+ zOJXh(Y!UqE@X2SFlku&uEY+RQM+<2%@@s3G1U>w479KPetP&aPu~$hg+k@{c)#N*s z)na>MTtQvcV|p&jhW+vr0?}?Rm+Ed;4zZgdA3?l)hf6hBSi*0x#?_{NjolBG7MHh` z?pN(s<>coFe_Y9bGziHlh2x=5Vd=b|2|IWGMghE@ldvVbisPrM> zPzisY+OY(Bp0Tu2Ib8vulU7|y8Zoa}g&2}EfNW9waiK0D(Ntfb1Ss?o{HH1P$j`1{ z@g5-gROP%?A-9VkBHW}id0b(2!s&f=cT~fdC9TdT7l~tid)}NTeyhURRKiI0MH)sR zs59qU2!CA{O<3loQMnsVURGPaNx_b*UyGOQyJXl8dM37j?Bi`2!XN>;^r{AWL~isJ z$4GIDKpT@h{zMKq>SQHkcMx$q&{sAH>-vANV&XZ0PCX&(h+ZrAj61xKNuB zfki{}xu!~aO{}s5pqaXx)J$-35*|isCKLNh6OR6sTekfke*L(FkNb?fJN~Nh#VU$N z)MVkJX1Ui&2A+!)A&+xz;SKS)qPTgrms12?&L#bkKR4LQx|u#d1+`)8dgge5h4JBM zz9~XoiJ=z$sc7)O{?9ja#}ls>g}Y?^i6)S~)8g-E6ZDlyGa2IM26cU*1t-EaELh6c z@?=8)0_MIcAi)~J4Ln8GuQrDZ)E&g^QqHl z#b1H}rp(q*d}JEAS1-AS#dKhIE>KQ#b9a~1i`W`8mDM9w8ut{k_>RMXq6ujJ@TZ!E z&Ns=E!bg8-FJcD3NlR&cEym&}%e3yMY3I?2Jme>ke9A@cbUU2P+BkDMJPb?^Zcn}5 zlIaCmHeNtp@H9~?8Gesca}I4h&<<9kvR2ZmLqT_?`qk7*lMXdbu&UUHCc!3+Xo{Sw z{VTi})xPBv`(C~=WcPjhpI+Wq3MwmyXEvNxoj2kUYX1o=@`xqa?)b}-e?m`B_Y?8; zK#roDab}A;9McdY{$|w-IkR}nxm#Ao_Kyhw&>?qOgse$fZSw$9AqVmAtcFgVjaX?q zX>DzIdG>c$gSf&Y-)1i+^IIb$SjYR(LLKYn@Zp$CoO3q-Siv|G$91;~mSCJ6(9D5+Is%y;~KhKSv-nYeRBfsIe<-fOIy?!(5t$$i^ufGP5kUwg!d-K zPlrcy$)&xJzzcmqJ?gNbA3RnFqm@89dshs~N5=JCmVoNij*W*ED&j`Q8R~v*8Ix$R z3@8hDRy%522`4MYM#J@yvXBpo^{HRwM05+SS)7tR;lW2PSiMDySR6en-0fk*Vw&l)bw(8rrZ|ipY_$J!d@K%F+13hkujJyBW0>FQR zXx1eA-8-?)GH&wxj6isDcX)Nlk9bB5CqE?XdwIeFZ?HC91B1!B0(P;jr@&^HAD}`{ z!a9d%QC>6%hn;iV{`TxBH62>}AOm%S_(Dv4>%0;#Z0xHKtx*L9G`l(l=}I4O$Qi?G zNR{(U!x>-C#9dKZz51jp_UnyDbt`ItL;(|LLv6)O;5B{JjL~9Cl^j+Pfh`6diWeF$ zc@<*Pl7qn57s=Gtnvs{s6LH+bbkzQ8_$8n55|jDT2@d0OBXU5UzFvgqu)Cmid)z?8 zR==@;KK;$6*kT}Cf^AW-S?A(Hor3r-q;B|ea-cx^QO)1(2D+>#kALZ;wP=z z_pi-}T#mZuLT$hPUWl?>^F(Np3plW>Jswkq^e}9P>#B=<`D%qy_p(}sZOwnT0Ib+B zC6xDLd=XAsh1>grER-y!5E@xlb|((q2qZkq1Ji*&(r(gUBL-asEItLo$co#iTM-Lk zF@O2f?@r)8aT7GP5Gp4InzlPX7s2$x1HI_=l2qjPar!^m5IJt!UJzbcxd9YQkHExV*Ok(bMPBSZD8@mR#~8 zO@CfJeet5jVaRFbF-P(>li6!1IU*W*=lLI-ti|s&6v=}8jd=P{y8-pM=8s|vT0c9I zsDT~7wO3|wh?-_c@FsDz7^^#xULeNOL_5l6JWWPb75irO9uIgmpMxRF$kUi=$r_5N zZ5T_pyLu4I1Ys8x3pFCjuP8i3Uv_;*j+=TvqNKlZz&m*iaH`;T7;4uRTSGLuvJlIBqZ7_K{r$MsaFK2AmJ^yV zQcTS1>w|~x_p;H|FCpsWKZ`>}2J}UyBT=(Tm}jXMOeG=82d{3_@T(~|@pW318cSgR z57Wpn2hnNfTb=&-GNwi_wzHV<=dheW%fE-J>$Kx(`R2%$3xZ+nivw?)vOl)SoE%_% zeFp_)*mbX+6jxSq>Ou%u^IX6a?#7VW*;xXDTLag_tSl>zO)Rf3 z^31E#B`ssh-@kvyQdm#wu&}WFnpD@@7I4@JV((3xj|(&Lbwuo#H$FGpBhGYs$??pr z>iAW;Sc!YR7`;!4`|G)6aW6i1ZE7j6buGFAuajr~wO`Ot#^Dc1G2C>HHV{VwYnWFtjH`m!Gr|7AcvmmFVkCq|Qg0-L@lFe2=Y&XqkNJAROqQJ0egMkzSAu ztQbHQbB%gb>{W&qwzj5AJ??2P`|M<+Q}Zn-i6B#}6B>t;pI= z77s^|3{&C>J@=C$)Ycb=fY2=O2!zXXF4K?9C2gLes#IKYh}Go0^3g{E6N4zUP-*FT zN9o~L=;T))<#zpMI^FIQhn)`cvqQ&eh2wTdgih3}(&gYX433%Op(Chv7m_J{jlz-( zpT#9*!*h^2payI_Vy3TMKJHQC7oT4%hFaLNAR&McIF4ruWBJStYMG z?kl2p&xRwLt!FKSbxqtU8|QUn@Ump!4VMqU0QfY%~^ zi^9*A{XzU4i z|Cg=}tC!y6D>RLNcljdmKB48#rLlJW$7GOHZw*E4@-3>Q@UhlJU;AO6=LuXf0OKR>xbr=K=(3EUSbs3xPpg& zvq?A+zmzWdWpM&(WiR>B(Gd|b@xry?k#9>`KNVUy|KWEsg4z)HEr7x$5!kK+5RSm@ zLFQLw`R3_{<;xe~*KIF6QbOTE)Y7R%x(j}<8NQ|=MF)D3v1;taFsA0v`Hr6p9JQ-p zw&9<3Nh!**tHj^3^;13sL3^C#u&%Hdy`N)=tdr^X{eDcdljG8X)yQ}CzxSiGjmbdvYBcox@)RQ35IeP@mo4Z*r~iLuFi(>A7hv5)^*lD8=J^X7r41f`^;_(kRgrf2SGGSGQN zqM|5oR#sOvoSZm;g|@P>sq?>e172QS5x1}KtSmJYoI|x5GmiO_HZcvJglmuD0XCFpN-*cwwsRTF&s+v z6NfQ&R`vc7cabLoG+2aX+Vzt~ly*CcnFgnPStm-SQCHB{bfx`fZZJ2VEQ=htKrt|C z--o3%gSSIWKD6?zF4oCk@wJvD&#jUt0-v3BEx-p>S zcn!9@;_Ludc6h_jX0 z{`#-Ui5n#Rev-aq6P*?rQX!RZ%*zPnn1J2mFx2jK7v4PFOX|3KW9u}nV6$S}zS#To zF9mYL3r&TQVf+?QmG$U{_UP(X#P~YJevHeJxTjyWMDmTg<6PvygomwqeI>J@+svj& z`b-VAZP&XOi6EN|+;z)t9q#|S$lMvsXkdNf z7|{Dj`jaMKjW%|h<^^1{5)@9#SUi$CA^;b=v+!fXL7*f7m?96>s38`AH2iWtcG)ss zFvsgM%@@h7yZHjJJS9fbWb9CQFWBSu*lFb}=IckNQgia3|LcnYes?|~s|kzGR{Ls& zC8~oe3O}qS7vN42R2286#cp$3!OV(QpA%h7l?mWYGKkzfG9=z$d3_w>-2nd11iZwnqfd)&d z}DfkGP{5rFQ+6Ve&kcycWrTO0lQMDAuWbDDB5Zsy6f zD8PJRMnZouerIo9l{IGAHI0otHqHJ zWkoTL=g8r5F2}U~s?kwr@aCi=O>hUVeYDC|{5#txd9oAIfhcb}R)LDqkrX+EDZkJmGI@(<=62CpEag zWiP%@Kp#I0Uyf1}55>b+d%?@cURqv`bFU8uqm&y||H*K4UZ3Y7e%5<9mOYwpUF5+j z5JEZyDfe4A9kP}BNN)BB(GyP4SiR6z|2QS1j}|aPm~@}uSGkzU>bt`wP6OG7-!A(e zaDLeG8Vik^qh3A=DBXFQGcf-h^t2NMq*{3f)n@M?8{rS9ky88t-d-oG6F>?(3d~+m zZxklVfo!6K<7pwvxf!XgunOb{+zy%&Jqfva(8YoWv80By&%~{=9IO<~+c_ zz_8#|iB+Hqmj2F2xf;=t7Ed2xFP6xmt2{{MG1;gyDi%xDpd2Q|<|NlK^cxD#j=kg& zk4=t|ifU*slfzmd>L0F_jUB@8m(vv;NPG!H2qOcb?9)RF20an|$fyaPixKSPHDd#z zbnJn+(*dWF@V0gTyfwY+nf0#e1PtcwN4I){z38L2b>@Mo9=GpX{$jiAeFmVQ05M4;oV;*LLn=tC<@n53Az!21Kwy|a70)xePko0s^m zG##G9$(QVGKzdyT*$@#CDN;@j3F#V6{ELA0ZzIr()6zErtNf->Eo=~IDOIBw~+*k?v@iQR0CpXWE+N_N}s{Yp>afBtZD4wW!zSd#q% zQUPY26or^TP1EdI_v1JXy2Q;^e4R?5bRwR zcKgazH7UqO^^NJZ>CbL_<@aX6(}&KplfNayE=-`G(#>VCj>w^@QdFJpBQF`R#TK@Q zWvmz=$jZ$%M}#%? zIqK7tSQxN`+M@Qn$E!v-+E2rjzXRJ;i>&wfsZym6baQY9`L{t)mJdJZ(j=~(*q(3O z>*@s%E5CUpFgo_dm$M}5cTw!9e}X>sfp#B^X%iVA$f9g>;?WLRIDb$y)bwM&*jZ|GYc`M~<~IX@lJ?%pJx9ME6;x|e#WMCf&}#32$HER5gx=-i zxO6NnJQs;S#gBTXQr~Fl|G8fi0V0$^E|YQA&_Gd)7mSP}V(l#y7eV9J(GFK@LsS&n zPUT=x=ni%TMECwewfDZF4%@52Ed7)f1G$rcyItb%T4+-%*;!E%$UXD%=CdF@vp1Xw zF(e`QzW+BE$2qujF~DC0;Ckr;qK8|m(^D&%xGc|7Y8B0SKm4}7e|&nw%$AXQRD^nD zW&Xd^9v&=sP02q9*;2k!V9-HcoP-qgaCuJtH4Pt5%QuC3e~GliJ&_eR)6=Qw&xix% ze?Hp%y*b4|{7FhCR+lgl>WdQv3#8?7sJ2(U%V`2;TLlU^KQ(5OFHnG80%B($ODT4=2?KS0EG#}sX|Um!BXd%NPngU}xb^u7(yVdm z$T$$`{FGMY^Mwc1|8RM;nrUsjc6O<(`+*3W^Nv5z8Jt~@`TOi$z?IPLMJ{E+%iMjM z0HRJg2ri?B|Eo<_sYpm}D-_(P(mxzXtW4u9Q-&O>#Boj7uN9o#dP)V@R0S%Gl#_LSsAm5JYD zF;>(fpFNr2g9mJmI_lx*E-z1U`XbpMaj8c?H8wX#l9vM~b?S}dZ);$_M%WC`oVVE6 zm|&(B>`N~ELph{lNtdTZ1D?oZC+1XL2MkNWycGMbAtWf6n3QDcxYqN2S{so9Oc2sC z@~MbzIHz!TcP|{wD1-r?D{7!06Fo;lbSZ;Kdm^?-o|6oP@<@b{72(2b;l7=ff$(XzZC^`})0AQZZ^`@FGX)(KGwByg86`rALCKBSN&!+;&xo(`=F1~l$D)Rh|cQ0jwLN#gcW>1f~SnLP1VU#wg+`JDWN56gZp z1gpGbLx0=O{rQ|I+n0u!m+B?1B!u>6?b#fP6o|#cuaZoANfTrXA_By3UpE~XSC>QH zPz6S4?UB&s2M41~wW3u;){YflbM2txL+vo3#z+O~jfV z`u_`xtx?aBvH0OlpPwMb5H+y@MH&1gFW@Zlhdxg9=w9+Y2Vh#aAV7S4U@LO$xY6GT zHdGcaKS;9USW?%VYgw#6uLG6;M|f5TiqFPSQbu<2bdSwK^!Cacq`H7-zK)NpA?`{P zbapBlszA-!Qs5%A{_B=lEYy1UzjM`rz@A41Ql?6@9~<|(vXV0W2NEH6Zy(4%aE;4h zAu5&p?*bV21kulur4$TtCBs(eCmfj7S`vaD`qDpsn>lD0i{%L&+S)kXc)Z+u8x0vW zfg9dkEc)cegxL>*3KU-{@m^F9V=!on-GnPyG@_gK3dk*0kr?*XS^M?IRT9{F4bqC# zroG-^IB89K6oXk0Q3kq3+ff^jTPbCeE$i}3CKRA7VyVnxMHkqV`Br=bhKqX`RN!J-q9h>#*R4uQmr;E zxe!~Gaf4LH`}alu@)vqj^J8hOH^cq>#8DEYpm6%uVYW@5_fodmo`JhGrPrFZ+qQ-D zDXEodVnB3v{SErbQ_*p#q}k4Hu>eo{g5lHD)U>!Cy6fe}oBr=nb)14V+&_HSK$n;% z2+n4FFAi16u3rR`@7VE(@WqeqTx?wV%5f(~_ksONNjMgBx44=s zT8xx^n}3ZqS4E^uK_eHf|MWS`KBA!fkeHr5<_MZ<8mWbeX;{1I@>wJIZC#ynZ^?QQ z+q=pV=QA=UU{kkA4a!%L;~K+?`8qysSrKEQ$bGv6gK_b$seHr>Aod0oQW;tV{~NRh zjI00(p;bAVvMLmIZ+7@Y#TrDu+{XRlWj8xv)SeS59@3Sd(mPY-r*YjLyRUQ!i23u! zg~#t&pyOs0qksB(Ht;;*bpX50YGqs4PYTHsu(mdmYHC+6FnO*Z?uP^G8c+f{$Ix?R z6f%DTioG=-`V8Bz-wwhki?c16be>E@rnjHu#Ja~4yEA6 z>xe;2Ec5Qp*Y|4Qy0v?y-P`?ep68#K8Gw5X0;t&D(GDQHIrZ(lJV0>U?<6OpEbd&) z{QT+hqL`dOXc`RLv->GhjD`}LAIIU<>DvB^*+y&54jw^I5kJ5GI{@&p{5KCE$e-`3 zr3yPbFuKhfHD7!aHZYuzznIJ%zv&L*1YD^%gek#S_m2?q9Id$`Vo$Esukc!yZD0mS zK2XyQe2kc0_J5f_DFB+yl!GT8>nUN0BA=BgGQ`6E>-AfWXt=&h{5LRaLIAOTI^A)v8T?wvkdsciuHwX-`@IG!s-8$YzA z#8L{h@ON_j;{Jh507&_3rw3O1JU!t)&aKi8{oj=XvuSY^*D>Khwhjz%=Jbk~8G{wC zzeGE80F}>F;IZR>Gwb*_WCS=<*AkI7Nq*LXutVqi3pTI84*C^3LG>e;>to#B7XVB6 z^dff+7Ut^95Y;V39|0kG^m+r^4e~ZB(Ov41`7gm0m(Zi8a zX)z9_a0I4vF5d`eq*fjzcsp~50oQooSS-@aodf`%y-(Kwe?085DlGBm^Osr_7H;lh zU;%+9coL^cQ;o--&Ofy}Vkk3F+-^%}0_*HFBe-Ugbp}|qyLyv({3JMBM zX5ic@(*4auDB=)BQ?G}N&d z&C;*h?9B$jX|}`n25*gIc(^ONXqB@a?d@v?XbKf~b^XB-mnuplPV&AJ@Nn8F-*D$Q zR2A3dgZ5U!!sI*A#O!GT2NWG<3VgcRwS50c1>BQd{K>5T=L8y*Xe~KYXg}GfnKvaL z;EOP%CluRi2&v7ISZue+B8uHmVdpmKbn&Xb2j-Uzw76~w-yhan2DQe(6X|h;XfzbY z-kK_99rEc1e>PGrx1+}gd2;ClPp~b&Cqm`LL~VIh&X~MX1EqFRbxQge5!!~ZsYTdr z0XX!6U#ino4sOa;3iz!~+gG{|_lJjvU&(CS=?Tk25)Bb=!!-`oxZpYN6^2be4rb=( z?b=uL3_!23Js94oz8Ku3QS9ZwPv_WwXH}sU&|O#v;#f|=5{?ouf$>q8FdCWFhfqhz zHMlPN+$JgHzD4}lI-6cyrJ?9H($-=4;_20C%q~xoT*5<9U2L)1e{h@WGdajWaYa)S zWI0erNlD4d#pNX)SM}ZAG7o6qx7TIsLm#@R_uqX*r8fGsxjj#ZYv=6jT=L%l*|n=2 zWN^GjM24dYN~v68r!^EM(AwYU5d5hx1?!F~&9AXG@vD~sXF^9ply-h~W23s@CS`Hjyd4|Mo&>R$A`kh<44CgdjUkl;>ew9FB>D*I+G^ z@h1zR<@eW{gS=tRkcry&^k!c1rrc1nwUa(Meg!5f>cFFa~Zie#EuKe$X7@qKfs`z?e^4tBo^q~Z^v&rVF9fJ=7X z&Fn0*APe(TQB$59j}W^gza>hb*SiQTixM zIQD1j^768lZ0{t5mzP&IADx-z123j*a&SvWX3qa>0rC3ye=@{DkQ38z^(- zJmgw!I5V3m@DX0-a-?R3JO+G6&txU?mXM{h!{NElDlFv(KNc4q{0J_6J1gF`Cc;6A z|H|vH#hWJOM}4Q^1&@P_dFO)Jc5Lh}OZz8-5taKNw0rq&YE)hEw@F|$X|(Z#2PB02rWTQV9WMbS;1n;I^mG99?*$I8uJxVBc~C+GFmsT%~nHn zE4O)97SJ|iV`CHE$#S3X4*HiZ>5qm966q|4>gRCoDxl>xch-p&qK3{QYek()d+cW| z+|2EqY3)c0iH<^BOwBNlDK+@wr%!H@va?sLbGpg1AYoDGAxCtJBJ%qC19ItyvF>&* zF1VHYNJH!uhDzI105;38(^mlan^waK^#82M=jU#K_2{@E_?QFIY1Uy@ZBL*TE_KL6 z5F*p*k_W&2z<;N|X3$Mp>aKy$uHkq!yLe?R%D%jQb1%1n0_vQKDheT978JEX=mzA( zMuVZ;9@g)Ams*QPxKIsR$+p2CSDWs~)*761n)#RT_}`{3*^lHHFE)Ke%$%@aT&lv_l~V}CZ1og#DI zjsDg~oW-#r1~PK^BJZBT5))y?JfoXr<+-+!^X_~0Y1)E-!0I!^Knb|#SD3b^0FjhQ zEe+2vl&BX7{m4jrnj|o5q>Z1g@81C0oVZQda)P(a^`3PDFlk&ND=VuOnjsW#>|)|_ zh0gJj*({Wuh#&SPtP94V_s+sAS?>}hlqo!|V+SO?m{WmTQXMcm7x?<`--D4&Y0V9` zlKH3gl{owXiA&WiwU&&v~x;-T?IVSkvYyDC;g9ysyjCXXc zZ3{hlmzyy}m2U2CNZuGG6^Zft8D(VNby?`l%H~^-Yh3|ikvP;ZY6|vH!2F>kg;7fB&dQh=!6CWoMK^)MH$RWb9 zGm|Zk%0Z-5zx(q%-{1B7bhLl$@z@;ec!M9b&r>))OuEG7%D~nz1atwf?7E^Jf?ZX4^mnb-~z^ium74|^Jt00cQDjO2gPRHN)^%L|3+W&S2&~X zN0!75S`9vCg79@&o!311s%&J%`k%X>?P0V^H(P!TOYUd)YHbH=YX-6(Spy%q_hmU& za)9C^k=fc|YpDlB@;Zi;6%GlyYc%wI-23r?Gx3FtFvKReiyL;Nnt>>=Y|?_gh5@rGt-Wy3O=lJ(-PI zPU*V_WI+v~!NHz8|6I#Xl>U`fz3^d*WwEMPLyG#%|@Iw_$HSTeuU<2a2ZL$ zWgAfndgKu@UMG$saw|1eQ%FS1_(Z3sY@D6%W&5mzFG=v@lB3?!m4EXPd zJd(QAdzJIGE5)@5G^suAnod;+?R(^BjnV0peA7j=0j(1D%rNm#g zFj%#7>`cuU3k>b^{VNvR?r=RkTqzMBiD!)0sJ?7A#b83-FvYqH)XqDgQr0tAOJnoq z*k!Mmom!T$SUwUPBsjL((`y|XAgZch@8Rrh^LeUqWBOTqwt~Mz@B#kE?&o*VNNdMA z60LgE$mk6$6FzPJJlp-Wyn>d?@fZ2|Tq!iscUfAkyjTuhj*LM`*d1V!ZXKWkdaq=;!)3^4__LNe zr_uNw>l3u?dLV8UTz(SOiV+aQ%%Sb1p>k;aZRM%J;}R#&Cm+seU)$clE1r3bm&*Ra z>va~)j^ftRb%2jIu(a%lL@!-A z=uSsIhcKOGu5u85IrXM0Q*8{hC~8WF&rFVwp+Q0i0wNZBfA&OS)L+ZAYZa+Kqd9+_@1zckFIVUNbN_8MwP) zyFOZK;5Riw(=BM5x2Q)|rlwc-EpT?LuhOovw}f}vjru322lw^80Wj7(;jY}iJaLr? z6*tn&A~7Z=CJ%pqk{RVa6_3-=)I5?K^7}GqciMtJ>i1v4&>PV1_ui7($3i&R$I+2b zU+LRvR#w)fm6feNNm8euRcUAn6r3vi%06}u>}p^O@k#cz34JDos&n`7aQwDDmh$KkaC&$D8dUGwcq=wBFKZq2 zZR~&?9}+vsAc#&h7wo4&kPT?u(;Yo3C@7e3-4@&w`@mr4?OQAXeU)aH0q11A?&#of zFG+fxMdL-51=dzpamw_)Nn{DI^f1qooEv_V@O3pP;#y zakx1huWS5t3}q1gLot2hT+47aE?__LRHFO?yg<5**jw0hj^Jj7ib6i{yk1OIJgTBWU|SF9a@WgQu9x;GNYREj&+jUy%{?` z(QBww+kKi9P7<01B z!5)YmmN8O)niQ+*o=LAAgdpvU@=*dBe;fZSkmOHywmqe=aa3U~cA{1Oer?ISo_Y%? z&Q3MDJzpz`VG*-YG3>(2FQmv0g50XzpWnZ9(_+46BN=Qr71mp&% z0K0Ly^G?9b9qr^vg85ZF2_aj$FMOG&XNpK>l{82`xSH6f-R06O!ijw{a3Q14+;JiR z7twxR!T*Z`bg1_|7UwDLnH@P<+#1$&D3{a?K5Fv0kARt(+0ydzP_h0wKn4YD8+f~Z z)P6^Hz5)krM)%AQsP#aVWy{(4PsVCfD$`ITS<(QCvctJ!cLtboO6q9{Bg-^OZ5VPE)vwUY6e zLg2!J-CpN{tbVq9s{e<8ZgQ}`dXZFrReeHYVtcw%PR}WQ#rBC>`~9K#;7*uYF>S$J zY|oRTzi{c&(}sqWg@uKC^i4voJtR{1BKaPDp7|Hm1)`)U-TLDpQc9wM<7m%LMm9f{ zJvp;~p(C4k*uY+FJxp_WnehpVzmAILFqN^fF))6h#fh6C1&9Ruu*nZoPf-R#BYdRd zecLzp#oZ^wRIK3nvVqZ_E4aYK%xq+00(~Y*?&Wt&O?@25x<$4RpW&$)R}XcEcQ4W! z^cxpvYQ3sqU*7R#q9QKHmQ!UwUAu@mXCj?qW3#rhs=DA4apXk7e*h%_H3U%N19=L_ znIkls@|}r?p=R#t=2lu(Rsb|C6f2^mqc2>%_ypQt1X#M%lAUnIoANx9G`k%X0)fFYm)ID4wQj~#k(Kh{(im?E$n`iQX_(?EdU%m2upGx zQovBQ$`1VcGA-=T5&pZa#u`&9{#4=Z=bV*TLGIgY?p4)G8W>F)A0Ho#aqgz*I`r(54<<^S=!&$Jt<#;)@xf-`b=5cqXe)${ zf8MMGq62YAAW>uw8K5@1J=+krw=>TlI)Q zQl0e+9_q&WL6MbC-u1+5k~|!GIW(-1C=gPRyhZkNMfa<=#{T;lP$3F^Gr> zblDuHL#tF_6whXE9|ea2ffDwz1$3`U`G2|P*-F;XAg~b|-&?)=8uI6-kvXq67ciWD ze$tQ&T1+^BE*HIY&RGcunsawIudaE4l%(R`j1RPwg@T%QwNbb0eR2CPhlYV=Lh;vT}7 z7{)NeQ+g65$v9#PcXxT0g!e}k5NKpoz=^<&-bZ+sAEAKY1RHk5_qMy(y~H5!p9u2& z`r&03wu|*HD3F0zWctC6_iZcl56-PD#$Q=spZk8laQg>QG`Rg>e>)v=$v5{I6fVX} z7L|l?%KNU-D)vDA$V&SatWE4*R8BL zff=GV+EBo4u*|;j^KqB@eB*g6SE9qa5>?+qR>IV3O0=_vhYx%7^oL4|XM@rWh0$xL zG3ER%oBMYWi~vT5A>}ZvxhCRl)pN^BOKHWW@6)q(ybAhx_A@h$(rVj&zt_UnYa=Eg zNBYJ7uK{KNIbTp1gA;{JEP)92HY|eY2!(IS+3A|3helglQC&ATVL0W896OY6VYn5- zknKz}mNwAQgt`Xi9zhca_gUI{yS_2mqB0S6jCwP#718)d?(|{8by_U`R>m zdjvi_;^n|albTF?tU!^i8XU#xCJ*}`JD+IlwJX+qKtqn{3W&$O?eBjAcX8=B9#9#g zY^C`7`(pl&h!IMcLmXl7Mz_}r#l&o^u;DnEOfPNJ}BR(m4mFqc_#)ua4kpj!!uJe=rG9z@+OL0 z1gI6h@cSD=rEZSm<%_<)Ned~o*v>=6JsKy5e=F_Hynzr6Ck|Ci7^5@cs{_N!g4l!n z4aFx|G`Y_sTg=hIyPv1iVCv)s(VBO|aDdx>bRCZ?n~sfo?ag-rVFbJ2HR#&f% zTX=mAH{g$hcxt#|#9@&o{3&=a&bt0~5^G?;%4(G;NP zke6D2Rk1LsoEx9%^(r0Ccih>_9Sg*mHyLg?8E`>kV`B$!Qjyu+ZPJx1qn&vrC2#$R z%H>};1PVRtr_#_fmaMA|`ckMjDz0enrMD-Q_m@s>ZEcmn%>=@#eL#O$0+)I43Q|i1 zNi%lf8@!(^quE>zWgAZroUZ)Z8KGyFi3K{&xBY@2Z1sP4cYitUU@NVS7t(tGGj+Ca zs1Jfe9^2(wQeKcW<)g|@37o!K_f0-lC<69zNsT*rXL8lvvj^)bH>oUulR7#&S_3p0 z!gRoUU39*J%UOlhX$j?3FN8euEIV8{B5Cd4QEc9iUn2y?N;Q_XT=+YUSOSBQTurVoYOQN%4Ex`ZwPSj;u^_X^v?K!_lH*oK$*A=MXt)XuK$*TbWR=P_uzU_Heqa zLlMj#|MCNvmWzuZ+_G~1&ZG-#V`{sJC8pxXqTl_lNT$Yq5d&A5o(T>M?!HI=qIo%h z%7Er^8)MzuipBHF!(4H^Z-jD`AWYmp#?jlCF4@!5GygQx(+<3drP53@VD8JemuMht z6_=Jq!P^Fa4`oeugv|rKsOr)$9{Y}U0a;nuPqdnnl-vs51>tGHazXPX!n4#n>A%kv zEME-(CJTutKs+YG1KS?S{Vk>Y;ymFyU{S$)#yW$(zQ{(q3|LOJvF?0jJH$7%CyO3z1p$QlXQ^WuI%gJd8a$avEhHSwP$&BPT@u?OYaQV!!+I7w~t<>FGVdbtcIVNXaw1913Dw@YNnX zefso0+SP`J2Bf?K)U5uE4Wn99Vs6v0d{g*{Yxe-7Q8X^gG`SC!Dr#Y|;((@Z`3|Ym zvr6!~Eb11zCB$7VK7o36SkJzcjo6VR?pBR~n)jh+* zI~05mEb}gqjL2EXE@~)p9*5a@--jqC=2AmlKY4DCJbfPC`bBk#`R)Dh0N$^|Z=UYf;tqBM`fN|IxjfECd%tBcS$;qd>PE-1qbDa8A$pT=jEm#Ra0|>${=WoM3#}RW3 zD<7}>!YjeDaiw#JCPl&VgIc<3#)oM6mF@irXdI@cy5Z>P7{}0LR&upg$c~95Cq5B% zA7yTCZrbR|#G?^wv-CF6HY7$m&L|)GT~jK#8d{j{7zgOBP;kg1yzT9!b33=P_Y=uC zbAx}llw8oDRastL?RKAI5*|%(E*JYYi=V#7A?F8<6t%<%DsyIuxTMsOx5XAzeo ze(()a3*l%*1tx%gELK2)+$d2YT0+JO={^|1(9{&0;fCT=0RmqT6r}779UmJrFfh0Z zGf4p8eZPLlMl65llReu}nK~&ugS9?-+<#^R0Kh6h==t??Z+rG@aGG+^4>@I$v1@SF z{mcJ*y@7D||4(Xy`{r$VRg9(R)vFw!|Km6m&M<}w{hJj0E3^z50zo+O{ovs13~9IK z&5nPVoytlAGSOkzNdvsXM`!EO@dG&qpCSfdHp^q>_w4^=po~lMH$XmSgvg_G5<~$J z012Lb=w+Zkh{J$r+4WCzZEb~vosj)b07DDSr`|I;@1jADFR>jg!SwlUJ_LE29$~WI zUfi1t$qf&u((KkP^a1ba;(OVS?<{52s~@hvftK$g{?U^G*wKyTw4g$B4Yt^omXzc{ zArYK2L{WI4`5}LPKaCWGoeRJlQnk&ImU@yc?US-D^~dY`71{siSI!0PS!%?c4KQSL z3O&M!KX2R#7ra+fz*ha99nJ-{1+5Bnej*(5fYRQr`wZ+W>yII`?9Vt?jJ?hM`D*dd zG9F?V5-*^s-Dx1}^1*c5FN+f91(0n)lD6YYJ2#l)hdGe-?q+5^NOXh<51&1r%%aD` zz=NEIOApW;wg1KNKvMo?eg0{p21rWejnoj3M-qSK_v1{@7g5l`QU^UZ@5{f=grbLQ z;ea6;#?U{6!SEktzO=EB2>;JFOHnClSndbq|{v43NMQ3g95o4IQ8>@pV zk7_*9kU(CGTnF3`^lvDa5;6_4_4zoMU`Hj-tDm!94R77bx?O8+Z`}(Br3SW<)U@41 zqam`5^xNrk6_r})M^jKx+G(p@HHKr zEKa)?u?FuL&gnF zj8L-U?x)e_5d?2HqQyg3!59a?$`M$zTeo}oEHAf8p|FVExTuApJIJ&Cb5EL@9wBMX zq*E>w3O=O1(Oo>P4nbUl&ESNd2~(%@6#)DI8uDT^GrCG2HiV&g2vWmx+KI=oMere) ytfGIeL5qiwd>V0sY%WFEB#2`0KpVd|6F;GcR0RX^IcrT*?0HBT6!v-1d^-jyd0xkf+0qV+Hvago_ z0Dc7k-~j*;42Fk;LxhJ%LPSIa0Av6_1%r{n;MaW60RR&WMu&sLgoj5)L_~v!$3XrM zRAgifboAG!*Z}aFD?R`aA|m1-A-$du0RRaAkO2S%3JMMy8UZ>w4h9AR1_m(}78wo> z0X{wf0RiDFI5@;OIOO>F#Ds+8#KgoTB&5W|5Shl7KUlT(n3i|g&% zw>&(&uki8lzrxKe$j>j#&o3q@C@3T({ECo}n5d|zxcEB&kN^NF0C;WH_W+;>04e~W z4gjyMuMGhD0ALIN5CAX-080S)2mm$!UEh#B0^B?kZa&oe=it_TR@87>y{0~)SWn~o=HC0tr6_pR_|DmbzA6l>I z=;-MEhoOOif$@Ks{11q!sU-vgF*CC;H#fJmwEXz-qm7M?ot>S%y~8U`PEIZ^E^cma z9v&WEUS2*vKEA&Getv!d0RcflK>z@KB_t&Dm9Vgg@bK`6h)=IXMMXtNN5{r~iHnO% zNJvOZN=ivdNlQ!1$jJEm^=o!^c5ZI&w{PDH3JQvfib_gK%F4gpOA8k(A#T3T9u{`}eA-rm{S34_6UdU|?$d;9zQ2L}g-hlfW;N5{vAFU zXMg|xy|A#bw6wIcva+_ewz09Xxw*N$y}i4;dw6(we0+R%c6M=bad&rjb8~ZYa`N=_ z^ziVozrTNVb@lJxKW1s9o7cVxQIL_;@-aD{Geqr?z?PM~?b-+$rTdWbUL5{~XLeC) zs+n@47!Lviv;BX9BKYJ0Xtn>J;0Ph0|I!BW;<8Z+@>$h*6t^5%BReB}%g>{z(I+N( zOLmV@X!A)8o}k)JlzknD6Xp*x9p=7ClvjZ@?}>#3FHKl<9#|lX9*sRB33eun_5Woc zJ&Q4JTXrS*6apOI2Cw#yW7&@Xzh$7@lFTjmsowI(j1-$Wc zuz)cA&T6F(UB7MxYhQXeWS>8>-XOr#z1F!i$~D(CAfk*(jF*w#%!hAZGz(3HbFN|`nxNM*!6(Kh{1sv>?UU1?AVmpFYtq}0cE+Qlvu6pBD z4if!r>cj4WpQ_(Z=Y_f>1;K9fe4cB3>zPzdsDeh;QPT3es475HmF60DfBl+W@_svl zs7UTq7-fnxn+Dd3e|mvxLA@R%k*hvSfRLQ#kSl{VB!9&?y?~-_4$(#Nkk)S6G&D(% zV3+`=o>ub{{~DIX7yKyaoqSh>B#nK31h}}V-A5SzW$p%YbxdAM6wY{uz<*weNwj|X zPeCS&vkLehJLZwFEE%|CJ zQ#7)4hn-vK3=h?Z$ZjL>C}}DDqp=+jnfeCSu2Et6s0jIPzNrg3JLlko2Agv@s16^(6|UA5hz zOBHA;g@+TobOJaR5BXpv+?94oc+mCNR{HBM4A_tBd`Cn?Vcpb4sRiz;jiZ;g5V)S& z?Ra;;!{2;Xc@jol>DA}w8;G$WQy0GBFv4=!dP7nA7UIF&Y00H2ZD@@a+o2tRSNp}Z z5w>rZo{g(6VrcCj8`d$G2RnHSc{H@6T=)yR++yzD1BcB67S06M9WZ{&^p~nyX)@!( zOX!QH`%Bdacrzl{oA!@L97Q;B;YM!SGH7ksII{SpjX10#l58?Ec<6MJbks&koDgaq z8e~cQS*a9z6>8K_2o5+&5&K2L%ZtDMqmFZZKjVPNki!kX`;nGHqS5*CvG{57ttw)# zfd0CKvlaOQS%FL${-&-Rgw=jgwX~k#AEOL(%8)pO))ZL;cK0{RG=C7@!HRwPz{3O6 z1NMjHpqh!xEgfGG&*NDPk>Pv<-68gmdR*pZMsc>Pb*1C$yO|+dr1x?LA$9B!lCFgO zI?z@)OME<g*7j z*jDsCl&B4X>O&muBn5bDz;ig;eTv_O;=eljS4IKy*_s|=TqR5u^nyD4RUkMS>L4qG zn~*R1cD{|1u#IAUq7Ll@Tq4aB{0ICnI~r#QeJnMx(xL$*k^W;$`Z_8Gx(RCR8jB(V znQunHwkk8?uue!LXI$Z15Yj|D4IHiE6kF_?A{LvWZ}E3gh!QPM3OgwBq4?M<Wro2!K&JY8-5 zq{!gJN6z@21jsq3;$}Sh-0dQ@|9rZc+iIKVK)>~sMA{?$>yukF|N3pD_?pC%kw#!Tvw+59Vrz()q&Bi z)ZUzTwR2!|rI|5vUEs!#!f>4Js&|l{&H;oCRtu=xNN7Bc;YB6KGOuoh`IPV-m z>GfmBk@VW%?cc0b2rxV>4juIVxZjyNZ&jJQKR2+2v>2GhrjAsNDvoF76%CHV3G#x_ zWVhxcV{R#`;j4SP=D!CtVYTP#X+RQ+;nHX_1IA#xV;_Uhk0}q=5C&6^*^cL*ii`dQ z_SoV%4<-P&J4GeQ*?Q5Mip8oPV*lLTpPuWZERz7i{}s+8eVTe&2L zL+g9-e=Gv_(&m_Ju$uZ;)wDnqOW9`feg3HNGo}Ca+33mW7+H?^-l&I?aA*DP;ga!A zWv)(T^KjR{we@>d^j$D`Q(8c7H{s20TW8}6S|5W zHv>#<_8v~GIylwl^pwVq&M%W@Mh6!o0}8f#sgxHrgk6$Pc_PR@I~egnxXmKQI;roT z=GTA75{wz|@j=xBd@E->p{)#W;KcLH(Hbevp1l9rQ89tU5>+Bo9NyBv(Hkg-Jw=%{ zj700aD^VA58LwbERoNC{2yg{67?#+ zRg^qPPx#=MG4}b|L#^lGp5izkK%!S?H-P)RuU{PtyF$htA_gj$;)wU13P)oXE`9SEr zCLvtsYt6aZP$5k%z89b3oiMn%kC2olv40T4<>qjX*PgGUXE4DPTP7=?JAZNq^r1nY%WX!*HT?>ag`2c{yvwep$TfBS3wP0j)gPH8|)N^oMYSu@326v zPagm1cG;8iXONn9g>3t>DjG|iu+EgL&F+=5g7VtDYgkBES`y0DKFvB|%+?w>LB$3l z5EMl@hC}cNUG5uMqctcvoXMC8da>{@?YOS^C0R20G*Cf#CTM3_&697~dq(<3(whxX z?mUdQ+K9OLXO24d4-SskL;3}Ag?cv3mnI3;GyGt+d=Rf#23d8gV=O$XUtMIZe7*6X z97NcO0-h=r>DODR59jZM;YQCN3Mq4&zvjjOcppr0gxPffhQ60;=sSYwFDiO{$B0~~ zsE&I{cX71B-9m~MIe|nGjY9;QY|S|DyetG=($?s}%AM9G>z*JqVXr2njJENhy#>6# zkWsLk(w099plsZP_iq3d_72m4iSw2_i-e1rzI7mKPIA>uvKE(bLtJtqK8T7yd$lT! z1ZA5%9PvU))is3$e5~dg4T^DI=!AElU&LBSZ~n>-5B;n_0F>lU;jcFmo^4TEI2@oj z80Una$h9Q9IP~-h1oVDbVpcdb2{~%mLX8c*NlcU=suLV23&iV3rVx1)&! zpxyXY;BE*wE+m{9fs^y3bRixaK_Xj8{D}TzXl`aZR6B)MB__K^=~f>SDkoc;g`BI> zXB++HNU4uqY#GdUQ*GmMbeR+2%ZfRUl_6o_9Qnc@KhlJ9_JSCGQC>u$wzqv-qlU19v5s9Mv0)oZ%w?t@fwI-NX$fjLwvwY!{Cxia4$43aQXLt z-xl9SAM<&2FSK;!E*fwb8_(Nam$*Ad3Zo`JE-RK>=H+n-=EMt;P_jd`Sc+U}lN#{z z=q=nl6GUJk+>9*DBs)V+5+z!>AF>DSrs!U;#(-Y>mYb0>9(86?p%X36ecjdY%4x3t zed|~mN9SLllvwX;C;7~k%Gp_aJL=lrjLxB-f6LO+PJTx+PM_Vap1ENaSzf5nW-w|M zGj~HLWuq25Njnc7y{~@n8lAkT)?qIK6*39-P!fttB55BBlfVPz)=?d@l7e0y;w*)m za`iPvY%O#dSqTP+bv!eKCE6~ik-q9+;=Fq>5X5N`DKu4d;sbjw*C6y>)=G%o4 zme=9+BkJ-}oe&)KH020l@Clq^_YNhj*Ea7!JtOAt41Gj~ObfOVpN+t60Qz>WLjnXp zjQG%xYT~MysclEKYrZxKDzm~F4#wdNt$LB|FB(N(iigu{cQa4Oaq$NJnfdhm`FITa z?&2Ri@UefM^2voUif`d__p0EVReAVT0UzrVB+*A!kqex<6tRYFQqNz7AP4TmFLXRq zJ2WRoA0`+^H@g@buw4^Ci2s7<++mn4ExCyF1Bemnqs~~6U>bk+4Xhv^5oq2oAAvDU zdJL~n$nTtdH-?|_*mR+t#Aja;isFdY8~$97gOgFp)4V5(z?%!a^Jb@R2pRu80ru?O zNdkjx^~mg6zXpd3=*PO;JAvjmTv)}wIXDAP2b$sactujKOdX>=?-busLpb)qFjHja z!|?O5stu73!3@CvLGJq*g)syt?EF~W}YD}NOBagnmA77uc(NSkgRo3U!&TO@C- zs;yzb++U8>xgog-LWY5PdXsylx}bM~YImXEtCMI<(=Noe0|s72SQ8n5SL1uTs6rGB z9E$bEd!^)AuQKYP3<)QRGqMmEb)5$8TuiUuucT$ObxpLn{-sQpc8*X0GP?K~rC@5S zcpV!@`!XxhW=mh-mXw43h|7PS!L2%h(9dyO`;etm{|q3j7$or+Yg!~$|0o=_=0lZ(MR!P@`HNiMOHgxb$vtkNXq3S8U@_bfO z5owpHM@H{8KZOY=lDNWSyKtZ2e^}a6TTM=uM=my3(qa#Y z!%{MnvLDL+N*-fTq$5Ync4C|I z2-+U#y4<6DD6@{6<_tIvIPkGc4ccM5{PDKN`7jhs5{A=~Ufq|qv`)EFe8mQ;b|BX8w7Ryt$jv z{kIf7;_;AN^@xZ0R}aY-KNt_ouIXKx7~3au7iAA_6?@A~<>vl6%=rlgjyhR-iu^z_ zV?DE5sFR)}PsIvIv#jhFFdiNX9Kq;G zigrXYSBpVZiM3^kyYnS-|873!RO)e*#7M40?S-z*D|GDb7xp9BhHEy_YTbkFEUR*p z_?$_}bt0!_?L}rI^0e*L!W)(AHslHVOl;S)c`V~XTRc=Lm)%Oqg>(6WJwY-ig$`)X z(0*XoqZIDNy(LDE*yBYti@fZ+4wUl2RjoS`6EeHz+s(;L#D-Q-{MJSe{qVIz^(4M_ zNigN#MFk3}In2;z$S)t7nc@mNT6T{*&p`z=8=;p;VAyD@|C?DrIfYg(m`iZ~B*e`C z$&GG)SGWnQg;HCym93OE#a2HxMmZp6Qf3ew?lc3K6-z7%#C`i(Y#viDqh)X52yf-W(?k>P&Z=k%JU{zl^ zTeSLtNa$~C`f$1o7aK;-VFZRb5Z_)I!DqA!SWA5;v$rNw4pe4=gpjs9EPM-cVcsPB zZ9!#}D@Zb7;nB^#m9-b(|BZZ&wv>ItKtIawlTm*a5~_Puk4zO>ZK3CxK7I6I2n-cJ zw8;jv;VJi&xxPlY@qH2!DKA56Cnma0}y2)WuJak7_-5lM*Tr#h51|K z@K}{XkxsA#^j7SKx4#+grwu&f-9PZr4U~Iv?mWWSpZ6Fm&LNBbyFYfzlxJZ|T4zgW zd>Q+L0W|OF*%2hJpB+V0a9<9giab^MmY)c4`D1Zyys;ixNcgCU*?lOiNoYxDZ-5z| z702lOnZ!spYXeX!4Nb7>b+`oym8)W7+ED}e>{XQzq;15SyGq4=NBXG7>%4XN1=E){ z@s0Q{YeQ%r9)d94#5U#N4?VaH5l4Gap6U7g`~HVuTVu8ga;X>#jQI@Dh=D==LM|RT zRkldMS)6oJDRvWa9WUb`m5kK0y>1Szoif>@;hg9K_VvV{X7URBZ1om=p)|0dVRINO z68!U&F}PiCwId|+v&i~h>A(K{14LLrW$HmGEOF%z#KMZ?rVc#~Lm|go3p^DYLgeKF z8-4qwB#wTAZK_LqscL#PF_}?LZ4Qpmu-k5rhGw zyc?XyhiDvJE*iehgcesGf#TusF1Ht*xwa6pT^S&^o>@&|^|kX0#nxVLdxf**C%38b zQy)~hB>n57kuwv8dCzo*3RvfcV*T^7wQu!LbwJx6j?Hi=Uj#1*a#GA;r@ju4&}X+H z7_1`2zBsr^XzDJO^s#pGp_Cdt`K#FH}v}Vr%q^W{S(< zn27qJf$b)aj6Va6lsxm)E#vEe+MzZxYBcnanJ1C?439zH8+LS8-@9^0P$x6&6DUS_ zy|qkJmIF989=?ej<|d-yb?ZLljma3UC!=j{;J>LS*PMGCR4Bbuj9|b>_=8P<*iua1 z{D4lYF(26DB1%S~U)F<1XVvkZ?9vUNK$GbMrP4gP)j>WRI95Pq2^Rw5kKH`3JPt5b z{-JbSdZ6ZGTumFSPa;>#Nx+a+uRMa{7*XJY-x(y5PL*!pR#o*@f3}RN`45iHhz(Qv z*?anonX|E9!pLA*O#-5n3f|C~iZ?Ck+-d9rArZpD8sf|j3k$vRx1DGdXQTp67z^=V zh=<*is<6T0)Xz!gF!%WWV0}kQ`!T--?a7A7ut&AUt73FQ_crrs4l%y6s;msoI!Pn} z7jir95u1WvxLRIrzc2b5)>OM~u%tOH4m{m{>`c&A5pD?Hem#P_`R<~Jb;r)QjCg#S z^M^Dv_jRCs&JrWoDIN0Pn7TVfS96IbehG_r3A>(6P$Lw@d5UbWuOrvRjM9?Ne5NO- z0{mxHYU70!8CmxH6f>U;3<+tzDmqwND~t{4BbxVN>)=Fai1>)Xj+pXH0#Fa1mk~rS zcau8)*8UUmwejEpl;`V=8{tfo;tKS-NMwe7(`ZTZ7~0mC5p6+A6H#>r5sp1sF~_uU zH~7kkWMTuWo*~3XRockNEfklB0M5`i$beS1UL$au|j|W*ZbE=DAs-$TD*I9Ra4P z7GH_YWxCG_jB14rXa~#G{{b%P^qInj^(tTlyzc%;ke(W;7-YAwFWW?ILVI=Qf9ZEn z-ENA=OLaA*)Opa!q3Wy8*L1YE)i$3hz(Rr&WpRPcdWH{Ij!4SCqcw*Ok)GO{<>O+u zOGsw!XLTV4R#n6*xqp75`Y>SdEJzOTezyG3)iN?#^Xt*sp|Q_!{qd@RE)o4xv2N5; z1^+N&*1kHHf-e)oa7G}CWtA$frTkqgR0xSWi~p1l8l4Y^ly5*Ly18>HwOpXzk)e45 z`Zwp?<*fw0C$o{9Cr{MT;ll+=>{cjap2Ie=V>YQ9kVh%L#0Zaujk{E5Ll6Z!MIh8*lgZ!H;4La-=Xn*)KqB3<`BX#*HevKbShV!8))FEY08yonjLi-Qf zz)ryBYMDxC80Xu_ov%<{bGxuXGLn+~@i7pbijo!Hi88Rjn8v4o`u?+`36dQR^AUsX z2@AN8G@d&-cW6#B$K!88-!Jm=OR;8TAC}`*!+P6k_fi@d*3-$@GFAqQ#s{3lE>tkA z)JWtJ)J6YI5s+d6ecL||HOQytQsUONh{_=mm8$qrTYhMgh*;FZE5RVB2oLTC5+tf* zdP?F5g-Cw$3JLUMSs$g3s{6={0+>@N7KS=EUA7$i(F6ctljXTeGCcI%8AAw0r(ENo z?fyk6>=8c`1a3%4&NGxU-D``uDwAhW{G>4{33b-y$B{?ox&!wIS^1T)HLUvDp$I2d zMa&T%-VaFVA{taY4FlRLPC`Y$*AwV7PG*X6JaM(?cf##)g@sFuR7ywMIDf7*P$LHT zvBT85G|}&PZgbakU@}{4f^V9%A50@uPHxQmw^#Dumxl}?vCOza8YFx2gxLz zlCqsX5FrQY?O_;AXy0!a6sfR8$sQsq7i2_mRDn6-bU1s6NWLxO-Bq zGb9D0ix|N*&|bJzN&gPis)|t8uT$5JykzamfAw_m33~-(F?8IYKEJF#gFt0(;4@x* zwygjc+Lv+o%8as8QRBS+AO%lia`IMcTylkd*bZ-x4kC-j=hiN_ynLPeUWJN|A2gh0 zKY0>KONx@{qf*4K=P`}Cz5RSY*YKUNZr7)Ip+<*K<`;mcAkPqXX3Uo z<*#tGwTx6UW=f=`Yl#QLK>!e_wZN5uWUa~ue@ zOYTLpExh*Uq;}b=lo;~&b6(iPt-sCE<;I3WA2-BjEs2afxm=gbjG0>R$59nLfh|1N zg6#WjOv(;19s`O%bAPBa?$*c?v!rdp>WJCH7IO_=0QA22mR0pmBaSwGfO3WA(jjt& zRRTM+)**6CZvV_oVF88JJKrsQYiZ$M8Hui>`jFP)m`)o)w11ldxs({8u-vF4gZvwj zZE5K~_MCgMaEYs}qio1YKnDbd73dWMT^vSqa(brOQ~NN%587o;xI7Ss<=<#P&)?~_ z+r(M)TF&iKYrNSGG5KYgRiO-8z5yXMkqL?qyVZHmr@1T!#}Be|WC5I_D0Y;Ppo73@ zJSSJsxCSUrS0DE$%w5lM+hc}mNs>Ls#9c*zrEfL1gdT}TL^eQyI00$W%4Ss3UOuCs zG`EE$WFff{AKPvKN8<_u`keB8k?18)Mr*=)4IQ7Jkh zQyl?g)a)7ebW8KYA~$-rc8B7|xK-X|6io-xM*-DcbJ1Zo-tT0gq{!Ev;k@@)&Q_tn zo|L)R{el?w7Z&+977z3F=y-9#x$b}4>%s>!BbTJfD$oPL4m_YHpAm%3H-FJWJtI@I z4sn_}Jdl5C1Pd7yQzB8sb7o5Vg4tW&Hk!&Rax{P4nJoZ?vF`75=99@b-(<$N?dB4-WAhtD$a+PzLzB z`1jW3S;pt+rvf93GP6+|pik$PLEnuB&w&iG1Xl`H3qGI*Z9&bKedFF3{`ili6s_c* z$Ot5N)1C$v6C>QGq{M%d=s5o&4Jn1*Gn>%}dYb`U!qa+<#c9^&H!9FV3+JJW>6>`? z&WRmK$nEIW5)uBR$d|1}+b&1ML|h3YXR^)6dR%ONH+<+B{=K2&-d=lB@EJvlK z4OMV>u05J>YrdMNa8zS&*yL!?xFA|K8B5DwyYTWJ$a+cAP8n6Wtj?WWg$yBPK@P|> ziz(Wme~pJ^HnQ3QS!kcX;rO2Sn=@gG6?I2mF4rt z7G^nqA|8?uPZ2DZOcY5OL9mQ88i!X(o_!FvEWa3E1N-eSsEeuFg?3n%D-LXhCMC$uU|9;`<<2&g{b{I9?LonvN42Cd>$ug&^CuDi=YAik6Whct`hZ@ z@2QNA&8SHWDvLq0bJ$`Y$+^WWMo?eS;?y!B^1s5qJjARIC?bOE(c z!HqhHM9djtJ(6lcpx1Si-gC{zD;>LQV=T!DsE;WVKM32)U2*$adMxdt4Py|0yBxrGPBy= zJR+NFO8euCt~H7U!L$D|5c?wseG;8!rd<0l zh0emz1JOBAc~8N-UublJ6s;8tto()FG=Molk`KL+{InO+lD6jlm+wmzDRHmwMJ&<_ zo0d!Y54LAkwWjlagq=4n_f;=GXCt{>2aVr-?{>)%Mx_hyjS{?fQ3kY+^q(+hO_2U# zc@%m2PF7yoUM%W!a4QUnP_4Tnqx&fTVB(dDn=hBa^E7_Ha(NtZ{=QbBF0sw3>YJtp zRcn0Y=X9GH)zrl#)CTg8hKpu@X8gPZ3a;=Z`U&0Ts+GvU>dO9N(UE z86ab258-L^XM{clwt{F8k2|U|r@j9UvN~QP_uF5CN4k`)ifw6BhkocuUB3AUFG7M+ zByqU~js*j4U&2v)B3!!p_6~#tfyQC6+dt$5eW@QwV7bH(y{M!kqZfa{0Wa>$h@&Fw zZ&*R!7s}PqiPraSVa4YftfduncZ-D|4$kUgcF6drQK3_DG4DFn0;@x_BE;#$y|~Z6 z2smYrci{+P!uD5m~!&i`G0c?^~W|{Bpic8_tdI^UiGh zdE+$*ce^^EcWRY#;l&Utq|48xSs*;hWu!YX8EH*vrBoMFtIE53kG2hslp{Reg7GvA zHYw}R%sR~y7xQY)*?|lRM*Tuj>brh=LULZ#oaA#7*mgfgPfWx(vvRCNvoQpYQl;L~ zAV37QPe+gml$dZH|E7i|8RAs@xF;+_{!ij zvkB-AZE#g2rsWsh7?m|J^lw8`E zsE{qQGKT4hj*shHv zSU^C(5LoCzh*?!o)P?I&L+qqPp4v)uJc$Z~JIWuL490403xe zpz0}WvCx_sRV;UqnaXG%$V$oX$(*;~xpnzwJ^*67aooW6;pihWE>Bv%lv9vLy+6jG zPG@Y%wB~fFtdzFqiPtBzg|=bcIq*S(KRtNxK{WUckn|4uEcDva=56+C##x^OVt-k^ z897V=Q?svnrG!yyQ4T=6Ooe6S9$UUefUCJ<0=W1enq-96WUc=anG>`1bQnqWD>)OK zUCmm*_JwsoS>Lx%vQY}+*0QSYCEhTxKfcy6{J>PNe!g;6VCtn{JyV*Gmi#WldQCHWtErW;NXo5ar7|Z@jaV`fch7Ey39r%aF!;;EdTN z?SW8FH=NgSu_*tV;@uy#ceK06hm?KjK^(I(spxZ)v#{i)N=vAv5YOyOz=<=pGp7I<^_cCzz%=$-|3Z28jy-^*8vC0@*4kdAiccmGbJ3_;P0g#kBa#)(WZ={*C5?NCND-eR+$uLM1xVl$Y$@cBI&3)jxd4T z$I*+(^}Fccn>eiCp~=pIFR(d=`Yna$DcM8VV(VpeW)<&W7n>fX3N>x&?y`o#x;v>Bsss*61pR!2cP#= z85TtMgKy=g$w*`W=9gEZPHv_)eV6S8H*wMv=FiDT`*Vg}omzaF<_wufYWJNf%IkbX z8QiG?yA-muzBO9mYL$l1V17(*BvoLuK%6}q=!LJ3iYSe1?bz{zD9TMtVtg)9wL;CTv1FO`iTtpG2$MijQ7e$v?RHZy+yaNedd! z$?5XrCvdyL&qkBC&9oz0@ikxx)=%wm*M+(!elBR|Z;}`b~c~%6!QwD3{);{_L;@ew&f5`}LSHyUn%sef`WL4`}WOENTLg z=rT1rj%P(pihg_-y6keCYb#b!S1zgZiS|HE2h=V(fBm1nUjRc!Ex7R-wCjlM2sX;! zO$<5t2d^agMpcrxyd_aMP><6crOhp!MKm!M`_%AL-s7){#!u`Y^Gf6?EGAzs6UuxD ztI2hx)Kz)erpx5xLX-_%aA=5+Lwe9Kf+UR09zklaKX_k85%S=^XHZc_1s)Lto;3Pm z&v}t*IfaCESe==Ya5-P&5^8975BbXorDJ;E2+7|)|WX5 zY{4<pi(hy0xfycVR z;B7}v>I??dx2z8o_Ss9qP+woDcAx@P*glG17~gZ!K`W-!<1I-vi5zb(y3DP?FAL(i zWC(XK_x>FCkDh2glHD#*{h{BUIGNPrFchjFf*^5tromEQY0m1P)WreMmK#c6I~6cf zR1LSgDW`R4;BqC`J&ky5bnl!@W-|qe@@(R0|GV!yJUs>- zNwtnKi*+Gtg4s7!^I~J24z9y1Hhn8YzZ5N@#qQo!fBelBd6m4>+w{qGjtjlA9sTu;&j#zQH_0l+Mz9mHQ8oEDYvcbc%&Y@I-Zsv zmU~wcVucc7GvXhmn|`ADYul{->Zbp*=P5-wNIw;sN$n6B9#q6{4Ku`i2DicosgBHW5& zy?K!2YhU?NO5)jGFHSaQi8feW0xG7kBVoeRGkQ}y@#`jlbB)*b5jvp@Vp!5gzC-L7 zi_Ki+}&_ssIIIW>-7cc6X$(_4%JXWC|ak3+G41NV^vOz90RehM|7 z8uIc=uF;1ekd*`ApbRYcA)f+VNw6YsI#dh3;z_amh@3~m6U6wKF2kx}fqKF8bN`|_? zRy$IXk_TT*0lST`8`_b@9K%H6#A1x#sBu${2}L+*971F+b2=VZez2UubB->14TfIr z-I-Rq?er>AKoGx3=BtiC_Wk+lSN~GZpR60eI-1@0tKBMdX0Iai(`2S_QX97H<(=+X z)QWA3-XmMSW49HTLzZhdWSE#{Hf3<9D8IlD^~mJtJfo)Mty z_>W$;z6)-ZOh#h$%TtjKA*>|yWe_QR#uwxH8CCI-CK%-jN8AOc1kJleW6*807u)S)?)qZI z^P5w{zc>WLH1vvz)g+R|&aKsWPlhoo{6=B~DGsMj>w*Pc5-b0QJ|-N|hT+gOk5(Le zULwMxUc}$OlE&X<=02Fl&FwdWV!HNnrew?>MZ72i#_WTd_35zU?pd_vmxo!B z*sc7AzB&p^`|6P6jA=YA*a;Qn#H{PSGx$Da!oQzM?BB}Rx}S?7R7jx0ijwU$LFp(8-MPSCbzjSC>pPa6?|_pBoO{!nXW5r3xp zpTHAFQ)wlDr+p>{2fYE2U5FxGmw*0n)3`RLsoP;oihWG0qA=foL4l**@4dd7K1qyU zgniwZZJD-s6I1U>@!O$QN#Jpxxr|+`i6*>58tN-OQ2wSnOS}aGmQ5i-t)*5$UGuw=F{F@nh)|6FovhooJPdAGXAL3jPG=) zxT>#VT}@7&i5!5x->|*C?=|BPsP zzQbh2Jp9o!4S!P>i7<`%uU6eqer&Rof7XAmUIaCRGWd#9wF&9YQgK+W$5>%vVw^B2 zu-!|fA$S2qN8p&As{R0TEq#+%m4;_~*SpC$)5; zNA^^IRQVweb>=5}hIQ2f3A_wXR>oWM!nh7L(tpn?*CsT>-V|p8CL$fJvM>ReFrqsN zv-7%_#T3++cduJ&Vy;?HXT~f{Flu%?*z3btn&hD#5ZEb}Pk?Hc_t;cghc%Q{j&Znr@?$~^s4CSJ{43v=&Q8ib8Aow47CA9-m zZ62w>XeWF+G8gw0{@F7hYK9e)V+Qhwgqrr(sfsaC779+G-?jCoTy)=xv-p=-Th>c& z?L$PFW->j6lJdw-z*|(6Z?*px;UWuRsJl$cIJM1R37?l~PFdE4yNQ_oS6nW^3K(h* zQxo&>2Hc&%uvjugHlv$~h?nMQd@jnue9Rv|9Pl@B;Q^LEdT-h3-CK?Lb7Bv0;nn#x zc0O&&Dbc@iDOp1Wf9tOtkXbd*vOE$Mmr`X=#P=~ckXJnEQ=BF69f%9h4!L6Gp@wvw z_EDSKZ@g^9J5ipgnD7^fiTrC)Chnh>}KP22VqMI_`v0zPQ-(O8xMbez)EpC_HBQ>0FfkwMN*Ssj=g z@BYoSfii6NzNqM%xCMRK+Z?{6Bs(LkfJ;i+-hudT+AoCI>&AWLf*4hoS^49#S$QNJ z2n{aj&IML^?Z9()d7<#K(RSbQTaA=&z<0}&!>v(0YUjv6P$GC9$r5Yr(XY6I%d9owa+TaO;|pzH2Z12 zhZ#b;q%zOpMV!yOkJ|yRYAte;7jl@s?G&}d=fJpb`qjlgHl1C!#99;Sq@dSmN|nIh zI9%_>5UX(cGR`-~omzYibQ)qc2MddJjx9V{xw*-3n!5$ZoVTB~xv&bup_BoyG3nO` zE*Y0+4S)25P@d8V$fA8O=CZ5U+P2xa<;bD8bZl6q=p70^JZY34ZT>LJOq+57u2#l*JFJ#youZv(>%kXqW2;bdGnq9!5*__ z!!BI)v-`i)UR4L{tsG2HL4gD^icFXdKwZ*?%7jf7q~M|Te*p>{_2UTXxKnJ0EM&uu zch-~JiFYrN@ZD=Sij;hM^WOXBd-J*UfBB_!jVU^26s0oYHCO=H2m!oVV%LQzVPjAd zE6~{QAFR&2XuC=g_c8&4ZLex}4fSW2kPWg2U1Fg&!TA!|k#CP(A$TfZ31H!3dE`Wz zCM_Od&L@iwb(1-I$$0wm)b&$kETMpj%-2K9!Foq*vW!r-RbTN9Ih2j1Hp%G;sfg+n ze>C&wUjn&~2@7n35;W%4@GpvRE`HYa*R5?5a6>X4zMG(PJB2L z&hj|=^5oQEXJJwf_{O!(P~z&l^SkIh?Yu~S52|z}u?4AQQcY3ZtjrQc6*mo3Km!tl zWoSxJmUX`(mtmRy0*e%qU%O(6seI%_10404TXsA(0E=`ZSHzAZgwav-<;jUxo5mxr zFuCq4sa5RTfL7N-UJAxG2ROXfFi_43rrVIfwEwbY6(u$m+1SOzUe!C*YmjZ(q=9-t z-lt7c(XfB5$TYb%xjp5jo6>FfF@jzug{tx;-=cLUTqE)n0nBfXIB79Hjslp?Mk2r` zE(>~U-AhXA{xVX%5LzBxZV49beKlly)K|!`gt~@rxL-`}${OyV6ap`#Qp?ZQmS1-5 zGF3?9;u1Zdwd;g^Yb!XrPWtjQ4?JvQ@f|rrRlejyhk|PMNdf$iz($1?vCeUn^4%R> zL|^*N6_2AtQCkH(6L9bqC4TcywBAgE5t8BA&_XMisz4}*-Ij1&+9mtYBN*edj)QTC zE;;S8p$Qu%8K@?sJfTN2OIc#8iJx3{tVE#fiQa{(eASmM`55Jhf{rh4y>#dPLIEBL ziOI87DAw!@=i`)sFOTO`oP*9x^31(YJk$hhG7O<@v>i$7u#F{ijcg@qq>}29rb55s z;xb{erS?~5JcotqsK!b;m3LHSo0L*-ojX?MrSjN`Xs|B}Fef)}z4q$QMd(TOjRINFbSd>h&p-{^c?`wke)veDNAJ`eJ68UFPh3%f2#D4H$J^{Gc2LJ z_t_!=J{j?;C;Bl%hy9d5UovVV9QWmIvSt=wW2p2A+XD~lXGl6m9n)s3Z?>(Kcs1?JH?N~WL zD}A>v0`jEFXA8KAQ-YSM+MEn=hO zn<1=(R37o;Ic|!g2evbbQ|!c>COx?EKLdO=|3QujU*Y2mbhBeO0$+Cm1Ckz0S*c!0 zg&dbaf=xn}u(+~R&VW|xkdDr+SeS{&o?NBB1v1kP%^!mXFw^c(J)V_Mcl{qf+<}(Q zX5oD5#kldv!;GbJ+!QqttFj1iylK*3UX%6iVgcrDQpPgQ8FD&gxsDAJ(N8rP5Z}v`0nMVN+gq@X z3EK{b4xNU}gp{S!!&ul@Dvw6vs42?fQxrp)f*cRc<0v5w~rPAu=pTHgsI zxtiK%r^ohn(8tUwvXQ6yw!Uc_pGa%sE}bB(VCL>hxW0N0Fv8iDQ>fYpcZ;pEo#{@Kpi>q+PMNji@P8_v}s7%rI&?z^~q(Bfu89 zViOB4FllTwX5c;U+lJGGgSR<_%tNt>6IM<(%u45&W)4ns!*s^X4dDZgq;-itq7(v1 z-Y7R3^`b8=p%9rl8Y>Z7FBtTy6hak4~7Qwu*z-AV7 zQ0{SmkNZnL!yrpBxArX4d!t>K;o390y9HFx6f<{3G6!o(!}8;Jtu7u5ojcVSid`TX z=4~O`H9Dc8;<>wC`GtNTU{@dwqs0TT!@qN>Hy?l>KLP=!#?hy=r1kF`fGc;-dL#bFW$7^rr$yCY6=zxps=Lk(xYT z@!b6xq^S>aCfJWtQ=YkSc;oe>$(#U&5(`fbd((tx4}1Y)`rL{?ysstlrjIV$)~7hL zzL^Y88Lv7oM6ufj`to1m>$)?&e6P+sKd&8;1vp0F3;rSkZSPY_U3wK=%S>9qivo%AsQ{*LI(@r;8L2)I} z4n`C@Ma|V@ODS?e!^kDWlMPfQA*L{EyNGXAwJb6bpT2;U@x#7Ugrxj^SP)3cU-GY3m6OBE+Suk&-H306R>p?P^lHET zPC{vlY8HR)gyP+JaNiE4Y*gvEezhJ%b*+IvZI)yJ>4!==|1NL2{0gFa>YdQaG@#Xj?eJeQD^S7z6AIuu9kFg~O=BtG6bTg#h@- ztx%cCVYWfi6Ucm_E$`k4cl_xlX_L}MRty}l<*Wll8$bsgWvl2Dx*C0yyq}ZOg-W%$ zRa^`Da42Uu>0QpZbj1U-{+OB|yL>oVNPx$Eovpfq%omp7*^MyviJRC! za#J*cYdS5w3n2%}gvpuJsu13f2M;K~9igo5?Udu9#YlX~5{JgvG;f8qS+em*yVo!E#i%Fi{;sW^wU$mSp$ue0pk<{j91cl0KN@!{HX+%(%%P2Oy?I?;MalIbn zz5{V$@9Y8;^T38qKT!Xg$B#o6gmALj-HmY156;GkS-uY(H;|!| z5A@cbuKm^f{=$K;VL+fDtc7Q#j?I z+L8Ghoq}X?GG%9bx7pw~G^HHizCQ%9fO+v4I&WYeHoWzhpPqmICr48a>DiCGJedo? zw(v|HFf_&JrLTn4_9eVqZK?t#lX82;Tx1*Wl^;j z=bpaqatR<7o^--PpE*4b8~o#q$CJsx%MlOeQNF zQ$$cs2{x%JqAYdIJ2Hw@R#1Vmt2*zOIRCHvj7xZboS4yP&g>E%UIDI`{pQuVNR=bq zrCP!G4*PvZkOa&Z1bO_6bN*$tX|~)*+EbsSxa2^r))0q~oX(Xir zU5KD;%Rsl0Mqm&zObPWMz_Dc%mwmIwY`Fp)@{LdQsC1OBxX9cCGVBOobNM#9?TZc`e)(9 zkkksh=ke8>6ZlyWfOQ>ECOr)YTnRV5xcAZr5GTkEC=e7e66KZDY9bay z9P<62yL0(%ql&_Km#}2Pf*pG{pweBD5Qrs}@FsOuK@}US{wb(JYE~5jB!m&!@BA`+Qzy0!X?Qymn%QJaITcAASy>?w7 zH(!$S(g5~3g9SQz#p;L?Ncr@5o=Lc1-rCQk}fj}97wirl06>=KL^CqHL z2&Xb`NdJo?QvfTz8aA%Wu*9Hg5GN@G2?Qh`+F~W-e4EgDy1o5ktS9qb(ICLeToE?$ zPylsJexq15?2sds0Z2V%)I&bnKT42 zNnavRiVY`yDH{d~B>a5Oq!|+;d2U+cnOJ^Wm8Jj=@WP@-A+z&GI7l3EO(jLy6>&;B z`^%$hG1i{F7+b!wxU^)3$;##{-I;SiS^+`&A|VIjh-)fkqfqdDwl5-Uf|J|FU`)wi zIXukR`AWwJ^@mhIX8QWTUUO<{WKHm+X#$Yv6RRtY0*udTZ3_9I?E`28G9!KU_L{E= zBmtp3V@=R&ekDDW<^jfMbo4TvE4O_DZMLK_(wEal#6}kjzZ*TMUK8A%qLhXL#^QFc5xwY|K%ntQ z3+f0)Q2G#6v%=d`EAJ&Q4dCO&zei{7yp=7r1iOpKpDcJl69_a=LYSIQcjLWaF*dK+ zyeii(4`A;<4eA!G?5idL177?_U1AK)hfQY$jTB{u<`Z>kJxRT=wf3rGBw&1c#VO>5 z3oH|Y%+OpF31%9GJRYk^{O72K^?W7yb;VS`{4;oEWs64+OdtJ%vd8h+G!p-xhD2h# zsaDDa@;nwM1LhxR*Lt~A9}^P56vrhYw2?rjL1-!xkY~6UYdT>55qnd}wf+=9cdSSt zm%|`|*K&o+tEn2xk866%>dQF*jE}dU&J+sId(pdy3Jt^EP&9}H&U9+|u{@X2V(gFa zzj5^+qX6?y>zm)FkgL6D)emG1F4PnaDG#`&V>hTq8~nxC`2ajv3Vh_SYTNlr7q)(Z zG_ov{qtM{d5jF}y6v$%iyaSeJ|4%4_G1Nf^jDvtQLd!&NCH5MK0){+O6sp!l&MRPi zMs+oz(4;T!6f-neV~Ux*N!QVSfKjNAZpIcV<-jx>`LYLID($3mc&q zx?jq(8^U0~zLZ-(bBpsy-pU0Nie4FIkIFxX-zBEy9o!0(oQQ(An+=~WoPORP-KvLP zx?3-PZ@5H1Sv!vd>l9L{?E^RZV4bmT1C&3WXA>GxC>9PghZ+F;%Thjl9C|79@Q0$& zfY+}k&ZjVg<(1}+noxk8q#}#m6&3}|7b|&xZ|n9~tJzQh=?7BUzlIx<#0@h|*4<=q z3PA?1oGZ|?ybMmi7(jMVPBJ#|u#y8&Kt@|F#=3>SLIK!%!1BSLrn~5F;-VsrcY`q$ z`a~#!3=$11dDQNiBlMSM{u*65kmzvsa_Piqz#S>KF4QErrCgI0^Qc z{Mca?iUYDdGxE(e_})62Vm{AEH|LzWasXcKTK&KLqvOkR{jA(4n)pu3J(=pANsz&r zq*?F)S#6<>-A)cbg~SH8{0*LcZMKg4ldRv#fzTPpM7Q9HMB@Spo58xufbTlQpp5<6 z@w$ATB7#lKy2+~vA@fKk>B zYA)yPC>NAb_sM8{2+|RjEnI4bX>`>D?A4;}2W5m_pA5*Qq$4a_tXqD!<8`$GU%kAy z_hh^FfnXy;E%e+NfLu~K!m@c=ZX%gR_r7-iw5obAeIF2%kuak_f`?l_@7p=Up30`t z{Rs9MFl`?uuHF7LjP;lSGMEuOc*&w$ciwzn$pA25>;p9@6IeLRO2}zUdAb*EbP)5xRJrqGuj{i)f8_$dS!>R!`xDV6)r}OAchC)zA%?yys3Cd$9 zH2dYE_yiBUzZHs~n#=%M`+8PQqhrA4_d#qFikVc-RX55w>gA2G?MoLf-e$li_95#b zD5GwE0lDa?6O6G87^U`_>KB4Cn$i&_-{}~QbN)wr){5LJ!8Q7UQ<#~1`35b%mm130%I)Add7gwdrhYZ z%IJle65+nI4|*%5^^5`2i~imtp7I(*x-Nj&Z8aY5+?*S-o-tq}?>$Bc$|w}&usPZ& z&<3n$4A|sbQwYk8JRvj2W(4aQtr#%9MAtrQ^k#+Hg6A%Ow|D<44MTAh#~pnGUqcXI zL(s*VsZcWdEaD&zA}WgDB8pVdK|uv^5Q8?T6%78xcJL3uwlQsvOP7-24hO>Jaz3x{ zUGM!J;^`SnfqCuK^{d&QD(qL@CQpNFuy3%dJIz(K!JeTTS)~br-GL?bMyhZ*u(bTh>A-S)>TF>7Z6lej vI~!OUWpC;T3M?)M76lds76lds76q1nD?cI#+AhQ&00000NkvXXu0mjfKo)~# diff --git a/dev-py3k/_images/vec_add.svg b/dev-py3k/_images/vec_add.svg deleted file mode 100644 index 6579815903c..00000000000 --- a/dev-py3k/_images/vec_add.svg +++ /dev/null @@ -1,195 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - a - b - - b - a - a+b - - - - - - - - diff --git a/dev-py3k/_images/vec_cross.svg b/dev-py3k/_images/vec_cross.svg deleted file mode 100644 index d1360f54c39..00000000000 --- a/dev-py3k/_images/vec_cross.svg +++ /dev/null @@ -1,240 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - a x a = 0 - a - a - b - d - c - c / sqrt(2) - 45° - - - - - - - - - - diff --git a/dev-py3k/_images/vec_dot.svg b/dev-py3k/_images/vec_dot.svg deleted file mode 100644 index 153ba397aa9..00000000000 --- a/dev-py3k/_images/vec_dot.svg +++ /dev/null @@ -1,258 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - a - b - a - a - a - c - 45° - a ⋅ b = 0 - a ⋅ a = 1 - a ⋅ c = 1/sqrt(2) - - - - - - - - - - - - - - diff --git a/dev-py3k/_images/vec_fix_notfix.svg b/dev-py3k/_images/vec_fix_notfix.svg deleted file mode 100644 index 02f9a376b56..00000000000 --- a/dev-py3k/_images/vec_fix_notfix.svg +++ /dev/null @@ -1,385 +0,0 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - A - B - f - - e - - d - c - c xd xe f - - - - - - - Fixed in: - xx - A - B - - diff --git a/dev-py3k/_images/vec_mul.svg b/dev-py3k/_images/vec_mul.svg deleted file mode 100644 index ac1f196f25a..00000000000 --- a/dev-py3k/_images/vec_mul.svg +++ /dev/null @@ -1,132 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - a - 2a - -a - - - - - diff --git a/dev-py3k/_images/vec_rep.svg b/dev-py3k/_images/vec_rep.svg deleted file mode 100644 index 3955b573b9d..00000000000 --- a/dev-py3k/_images/vec_rep.svg +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - Vector on page - Vector outof page - - Vectorinto page - - diff --git a/dev-py3k/_images/vec_simp_der.svg b/dev-py3k/_images/vec_simp_der.svg deleted file mode 100644 index cb1795da3bf..00000000000 --- a/dev-py3k/_images/vec_simp_der.svg +++ /dev/null @@ -1,353 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - A - B - l - - x - ax - ay - bx - by - c - - - - - - θ - - diff --git a/dev-py3k/_images/winpdb1.png b/dev-py3k/_images/winpdb1.png deleted file mode 100644 index 9f626015316997f632192350b3d859e7937f8c0c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 66153 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 N!)ixV)0Is<{tI6nAsGMw diff --git a/dev-py3k/_images/winpdb2.png b/dev-py3k/_images/winpdb2.png deleted file mode 100644 index 4a4e9248cc05dc875d3c8381072aa52c9ac1d405..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93795 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 diff --git a/dev-py3k/_modules/index.html b/dev-py3k/_modules/index.html deleted file mode 100644 index 4cdee61cf5e..00000000000 --- a/dev-py3k/_modules/index.html +++ /dev/null @@ -1,331 +0,0 @@ - - - - - - - - - - Overview: module code — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - -
    - -
    -
    -
    -
    - -

    All modules for which code is available

    - - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/mpmath.html b/dev-py3k/_modules/mpmath.html deleted file mode 100644 index 8d044808449..00000000000 --- a/dev-py3k/_modules/mpmath.html +++ /dev/null @@ -1,560 +0,0 @@ - - - - - - - - - - mpmath — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for mpmath

    -__version__ = '0.17'
    -
    -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
    -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
    -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
    -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
    -
    -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
    -
    -# 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():
    -    try:
    -        import psyco
    -        psyco.full()
    -    except ImportError:
    -        pass
    -    import sys
    -    from timeit import default_timer as clock
    -    filter = []
    -    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()
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/mpmath/calculus/optimization.html b/dev-py3k/_modules/mpmath/calculus/optimization.html deleted file mode 100644 index 77f854c7458..00000000000 --- a/dev-py3k/_modules/mpmath/calculus/optimization.html +++ /dev/null @@ -1,1206 +0,0 @@ - - - - - - - - - - mpmath.calculus.optimization — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for mpmath.calculus.optimization

    -from copy import copy
    -
    -from ..libmp.backend import xrange
    -
    -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) - if fm * fb < 0: - a = m - else: - b = m - fb = fm - 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: - '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 - """ - 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 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 - 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 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) - - Examples - -------- - - 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 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() -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/mpmath/calculus/quadrature.html b/dev-py3k/_modules/mpmath/calculus/quadrature.html deleted file mode 100644 index 2b54c716df8..00000000000 --- a/dev-py3k/_modules/mpmath/calculus/quadrature.html +++ /dev/null @@ -1,1126 +0,0 @@ - - - - - - - - - - mpmath.calculus.quadrature — SymPy 0.7.2-git 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.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: - - 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 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 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() -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy.html b/dev-py3k/_modules/sympy.html deleted file mode 100644 index 01252b83f8a..00000000000 --- a/dev-py3k/_modules/sympy.html +++ /dev/null @@ -1,196 +0,0 @@ - - - - - - - - - - sympy — SymPy 0.7.2-git 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://code.google.com/p/sympy/"""
    -
    -__version__ = "0.7.2-git"
    -
    -# Try to determine if 2to3 has been run. To do this, we look at long.__name__.
    -# If 2to3 has been run, it should convert long to int.
    -import sys
    -if sys.version_info[0] == 3:
    -    try:
    -        HAS_2TO3 = int.__name__ == "int"
    -    except NameError:  # it tries to see long but long doesn't exist in Python 3
    -        HAS_2TO3 = False
    -else:
    -    HAS_2TO3 = int.__name__ == "int"
    -
    -if sys.version_info[0] == 2:
    -    if HAS_2TO3:
    -        raise ImportError("It appears 2to3 has been run on the codebase. Use "
    -                          "Python 3 or get the original source code.")
    -    else:
    -        if sys.version_info[1] < 5:
    -            raise ImportError(
    -                "Python Version 2.5 or above is required for SymPy.")
    -else:  # Python 3
    -    if not HAS_2TO3:
    -        raise ImportError("This is the Python 2 version of SymPy. To use SymPy "
    -    "with Python 3, please obtain a Python 3 version from http://sympy.org, "
    -    "or use the bin/use2to3 script if you are using the git version.")
    -    # Here we can also check for specific Python 3 versions, if needed
    -
    -del sys
    -del HAS_2TO3
    -
    -
    -def __sympy_debug():
    -    # helper function so we don't import os globally
    -    import os
    -    return eval(os.getenv('SYMPY_DEBUG', 'False'))
    -SYMPY_DEBUG = __sympy_debug()
    -
    -from sympy.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 *
    -# 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, 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, 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
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/assumptions/ask.html b/dev-py3k/_modules/sympy/assumptions/ask.html deleted file mode 100644 index 3cd347d90bb..00000000000 --- a/dev-py3k/_modules/sympy/assumptions/ask.html +++ /dev/null @@ -1,420 +0,0 @@ - - - - - - - - - - sympy.assumptions.ask — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.assumptions.ask

    -"""Module for querying SymPy objects about assumptions."""
    -from sympy.core import sympify
    -from sympy.logic.boolalg import to_cnf, And, Not, Or, Implies, Equivalent
    -from sympy.logic.inference import satisfiable
    -from sympy.assumptions.assume import (global_assumptions, Predicate,
    -        AppliedPredicate)
    -
    -
    -
    [docs]class Q: - """Supported ask keys.""" - antihermitian = Predicate('antihermitian') - bounded = Predicate('bounded') - commutative = Predicate('commutative') - complex = Predicate('complex') - composite = Predicate('composite') - even = Predicate('even') - extended_real = Predicate('extended_real') - hermitian = Predicate('hermitian') - imaginary = Predicate('imaginary') - infinitesimal = Predicate('infinitesimal') - infinity = Predicate('infinity') - integer = Predicate('integer') - irrational = Predicate('irrational') - rational = Predicate('rational') - negative = Predicate('negative') - nonzero = Predicate('nonzero') - positive = Predicate('positive') - prime = Predicate('prime') - real = Predicate('real') - odd = Predicate('odd') - is_true = Predicate('is_true') - symmetric = Predicate('symmetric') - invertible = Predicate('invertible') - orthogonal = Predicate('orthogonal') - positive_definite = Predicate('positive_definite') - upper_triangular = Predicate('upper_triangular') - lower_triangular = Predicate('lower_triangular') - diagonal = Predicate('diagonal') - triangular = Predicate('triangular') - unit_triangular = Predicate('unit_triangular') - -
    -def _extract_facts(expr, symbol): - """ - Helper for ask(). - - Extracts the facts relevant to the symbol from an assumption. - Returns None if there is nothing to extract. - """ - if not expr.has(symbol): - return None - if isinstance(expr, AppliedPredicate): - return expr.func - return expr.func(*[x for x in [_extract_facts(arg, symbol) for arg in expr.args] if x is not None]) - - -
    [docs]def ask(proposition, assumptions=True, context=global_assumptions): - """ - Method for inferring properties about objects. - - **Syntax** - - * ask(proposition) - - * ask(proposition, assumptions) - - where ``proposition`` is any boolean expression - - Examples - ======== - - >>> from sympy import ask, Q, pi - >>> from sympy.abc import x, y - >>> ask(Q.rational(pi)) - False - >>> ask(Q.even(x*y), Q.even(x) & Q.integer(y)) - True - >>> ask(Q.prime(x*y), Q.integer(x) & Q.integer(y)) - False - - **Remarks** - Relations in assumptions are not implemented (yet), so the following - will not give a meaningful result. - - >>> ask(Q.positive(x), Q.is_true(x > 0)) # doctest: +SKIP - - It is however a work in progress. - - """ - assumptions = And(assumptions, And(*context)) - if isinstance(proposition, AppliedPredicate): - key, expr = proposition.func, sympify(proposition.arg) - else: - key, expr = Q.is_true, sympify(proposition) - - # direct resolution method, no logic - res = key(expr)._eval_ask(assumptions) - if res is not None: - return res - - if assumptions is True: - return - - local_facts = _extract_facts(assumptions, expr) - if local_facts is None or local_facts is True: - return - - # See if there's a straight-forward conclusion we can make for the inference - if local_facts.is_Atom: - if key in known_facts_dict[local_facts]: - return True - if Not(key) in known_facts_dict[local_facts]: - return False - elif local_facts.func is And and all(k in known_facts_dict for k in local_facts.args): - for assum in local_facts.args: - if assum.is_Atom: - if key in known_facts_dict[assum]: - return True - if Not(key) in known_facts_dict[assum]: - return False - elif assum.func is Not and assum.args[0].is_Atom: - if key in known_facts_dict[assum]: - return False - if Not(key) in known_facts_dict[assum]: - return True - elif (isinstance(key, Predicate) and - local_facts.func is Not and local_facts.args[0].is_Atom): - if local_facts.args[0] in known_facts_dict[key]: - return False - - # Failing all else, we do a full logical inference - return ask_full_inference(key, local_facts, known_facts_cnf) - -
    -
    [docs]def ask_full_inference(proposition, assumptions, known_facts_cnf): - """ - Method for inferring properties about objects. - - """ - if not satisfiable(And(known_facts_cnf, assumptions, proposition)): - return False - if not satisfiable(And(known_facts_cnf, assumptions, Not(proposition))): - return True - return None - -
    -
    [docs]def register_handler(key, handler): - """ - Register a handler in the ask system. key must be a string and handler a - class inheriting from AskHandler:: - - >>> from sympy.assumptions import register_handler, ask, Q - >>> from sympy.assumptions.handlers import AskHandler - >>> class MersenneHandler(AskHandler): - ... # Mersenne numbers are in the form 2**n + 1, n integer - ... @staticmethod - ... def Integer(expr, assumptions): - ... import math - ... return ask(Q.integer(math.log(expr + 1, 2))) - >>> register_handler('mersenne', MersenneHandler) - >>> ask(Q.mersenne(7)) - True - - """ - if type(key) is Predicate: - key = key.name - try: - getattr(Q, key).add_handler(handler) - except AttributeError: - setattr(Q, key, Predicate(key, handlers=[handler])) - -
    -
    [docs]def remove_handler(key, handler): - """Removes a handler from the ask system. Same syntax as register_handler""" - if type(key) is Predicate: - key = key.name - getattr(Q, key).remove_handler(handler) - -
    -def single_fact_lookup(known_facts_keys, known_facts_cnf): - # Compute the quick lookup for single facts - mapping = {} - for key in known_facts_keys: - mapping[key] = set([key]) - for other_key in known_facts_keys: - if other_key != key: - if ask_full_inference(other_key, key, known_facts_cnf): - mapping[key].add(other_key) - return mapping - - -
    [docs]def compute_known_facts(known_facts, known_facts_keys): - """Compute the various forms of knowledge compilation used by the - assumptions system. - - This function is typically applied to the variables - ``known_facts`` and ``known_facts_keys`` defined at the bottom of - this file. - """ - from textwrap import dedent, wrap - - fact_string = dedent('''\ - """ - The contents of this file are the return value of - ``sympy.assumptions.ask.compute_known_facts``. Do NOT manually - edit this file. - """ - - from sympy.logic.boolalg import And, Not, Or - from sympy.assumptions.ask import Q - - # -{ Known facts in CNF }- - known_facts_cnf = And( - %s - ) - - # -{ Known facts in compressed sets }- - known_facts_dict = { - %s - } - ''') - # Compute the known facts in CNF form for logical inference - LINE = ",\n " - HANG = ' '*8 - cnf = to_cnf(known_facts) - c = LINE.join([str(a) for a in cnf.args]) - mapping = single_fact_lookup(known_facts_keys, cnf) - m = LINE.join(['\n'.join( - wrap("%s: %s" % item, - subsequent_indent=HANG, - break_long_words=False)) - for item in list(mapping.items())]) + ',' - return fact_string % (c, m) - -# handlers_dict tells us what ask handler we should use -# for a particular key
    -_val_template = 'sympy.assumptions.handlers.%s' -_handlers = [ - ("antihermitian", "sets.AskAntiHermitianHandler"), - ("bounded", "calculus.AskBoundedHandler"), - ("commutative", "AskCommutativeHandler"), - ("complex", "sets.AskComplexHandler"), - ("composite", "ntheory.AskCompositeHandler"), - ("even", "ntheory.AskEvenHandler"), - ("extended_real", "sets.AskExtendedRealHandler"), - ("hermitian", "sets.AskHermitianHandler"), - ("imaginary", "sets.AskImaginaryHandler"), - ("infinitesimal", "calculus.AskInfinitesimalHandler"), - ("integer", "sets.AskIntegerHandler"), - ("irrational", "sets.AskIrrationalHandler"), - ("rational", "sets.AskRationalHandler"), - ("negative", "order.AskNegativeHandler"), - ("nonzero", "order.AskNonZeroHandler"), - ("positive", "order.AskPositiveHandler"), - ("prime", "ntheory.AskPrimeHandler"), - ("real", "sets.AskRealHandler"), - ("odd", "ntheory.AskOddHandler"), - ("algebraic", "sets.AskAlgebraicHandler"), - ("is_true", "TautologicalHandler"), - ("symmetric", "matrices.AskSymmetricHandler"), - ("invertible", "matrices.AskInvertibleHandler"), - ("orthogonal", "matrices.AskOrthogonalHandler"), - ("positive_definite", "matrices.AskPositiveDefiniteHandler"), - ("upper_triangular", "matrices.AskUpperTriangularHandler"), - ("lower_triangular", "matrices.AskLowerTriangularHandler"), - ("diagonal", "matrices.AskDiagonalHandler"), -] -for name, value in _handlers: - register_handler(name, _val_template % value) - - -known_facts_keys = [getattr(Q, attr) for attr in Q.__dict__ - if not attr.startswith('__')] -known_facts = And( - Implies(Q.real, Q.complex), - Implies(Q.real, Q.hermitian), - Equivalent(Q.even, Q.integer & ~Q.odd), - Equivalent(Q.extended_real, Q.real | Q.infinity), - Equivalent(Q.odd, Q.integer & ~Q.even), - Equivalent(Q.prime, Q.integer & Q.positive & ~Q.composite), - Implies(Q.integer, Q.rational), - Implies(Q.imaginary, Q.complex & ~Q.real), - Implies(Q.imaginary, Q.antihermitian), - Implies(Q.antihermitian, ~Q.hermitian), - Equivalent(Q.negative, Q.nonzero & ~Q.positive), - Equivalent(Q.positive, Q.nonzero & ~Q.negative), - Equivalent(Q.rational, Q.real & ~Q.irrational), - Equivalent(Q.real, Q.rational | Q.irrational), - Implies(Q.nonzero, Q.real), - Equivalent(Q.nonzero, Q.positive | Q.negative), - Implies(Q.orthogonal, Q.positive_definite), - Implies(Q.positive_definite, Q.invertible), - Implies(Q.diagonal, Q.upper_triangular), - Implies(Q.diagonal, Q.lower_triangular), - Implies(Q.lower_triangular, Q.triangular), - Implies(Q.upper_triangular, Q.triangular), - Implies(Q.triangular, Q.upper_triangular | Q.lower_triangular), - Implies(Q.upper_triangular & Q.lower_triangular, Q.diagonal), - Implies(Q.diagonal, Q.symmetric), - Implies(Q.unit_triangular, Q.triangular), -) - -from sympy.assumptions.ask_generated import known_facts_dict, known_facts_cnf -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/assumptions/assume.html b/dev-py3k/_modules/sympy/assumptions/assume.html deleted file mode 100644 index 6dd97c59945..00000000000 --- a/dev-py3k/_modules/sympy/assumptions/assume.html +++ /dev/null @@ -1,294 +0,0 @@ - - - - - - - - - - sympy.assumptions.assume — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.assumptions.assume

    -import inspect
    -from sympy.core.cache import cacheit
    -from sympy.core.singleton import S
    -from sympy.logic.boolalg import Boolean
    -from sympy.utilities.source import get_class
    -
    -
    -
    [docs]class AssumptionsContext(set): - """Set representing assumptions. - - This is used to represent global assumptions, but you can also use this - class to create your own local assumptions contexts. It is basically a thin - wrapper to Python's set, so see its documentation for advanced usage. - - Examples - ======== - - >>> from sympy import global_assumptions, AppliedPredicate, Q - >>> global_assumptions - AssumptionsContext() - >>> from sympy.abc import x - >>> global_assumptions.add(Q.real(x)) - >>> global_assumptions - AssumptionsContext([Q.real(x)]) - >>> global_assumptions.remove(Q.real(x)) - >>> global_assumptions - AssumptionsContext() - >>> global_assumptions.clear() - - """ - -
    [docs] def add(self, *assumptions): - """Add an assumption.""" - for a in assumptions: - super(AssumptionsContext, self).add(a) -
    -global_assumptions = AssumptionsContext() - - -
    [docs]class AppliedPredicate(Boolean): - """The class of expressions resulting from applying a Predicate. - - >>> from sympy import Q, Symbol - >>> x = Symbol('x') - >>> Q.integer(x) - Q.integer(x) - >>> type(Q.integer(x)) - <class 'sympy.assumptions.assume.AppliedPredicate'> - - """ - __slots__ = [] - - def __new__(cls, predicate, arg): - return Boolean.__new__(cls, predicate, arg) - - is_Atom = True # do not attempt to decompose this - - @property -
    [docs] def arg(self): - """ - Return the expression used by this assumption. - - Examples - ======== - - >>> from sympy import Q, Symbol - >>> x = Symbol('x') - >>> a = Q.integer(x + 1) - >>> a.arg - x + 1 - - """ - return self._args[1] -
    - @property - def args(self): - return self._args[1:] - - @property - def func(self): - return self._args[0] - - @cacheit - def sort_key(self, order=None): - return self.class_key(), (2, (self.func.name, self.arg.sort_key())), S.One.sort_key(), S.One - - def __eq__(self, other): - if type(other) is AppliedPredicate: - return self._args == other._args - return False - - def __hash__(self): - return super(AppliedPredicate, self).__hash__() - - def _eval_ask(self, assumptions): - return self.func.eval(self.arg, assumptions) - -
    -
    [docs]class Predicate(Boolean): - """A predicate is a function that returns a boolean value. - - Predicates merely wrap their argument and remain unevaluated: - - >>> from sympy import Q, ask, Symbol, S - >>> x = Symbol('x') - >>> Q.prime(7) - Q.prime(7) - - To obtain the truth value of an expression containing predicates, use - the function `ask`: - - >>> ask(Q.prime(7)) - True - - The tautological predicate `Q.is_true` can be used to wrap other objects: - - >>> Q.is_true(x > 1) - Q.is_true(x > 1) - >>> Q.is_true(S(1) < x) - Q.is_true(1 < x) - - """ - - is_Atom = True - - def __new__(cls, name, handlers=None): - obj = Boolean.__new__(cls) - obj.name = name - obj.handlers = handlers or [] - return obj - - def _hashable_content(self): - return (self.name,) - - def __getnewargs__(self): - return (self.name,) - - def __call__(self, expr): - return AppliedPredicate(self, expr) - - def add_handler(self, handler): - self.handlers.append(handler) - - def remove_handler(self, handler): - self.handlers.remove(handler) - - @cacheit - def sort_key(self, order=None): - return self.class_key(), (1, (self.name,)), S.One.sort_key(), S.One - -
    [docs] def eval(self, expr, assumptions=True): - """ - Evaluate self(expr) under the given assumptions. - - This uses only direct resolution methods, not logical inference. - """ - res, _res = None, None - mro = inspect.getmro(type(expr)) - for handler in self.handlers: - cls = get_class(handler) - for subclass in mro: - try: - eval = getattr(cls, subclass.__name__) - except AttributeError: - continue - res = eval(expr, assumptions) - if _res is None: - _res = res - elif res is None: - # since first resolutor was conclusive, we keep that value - res = _res - else: - # only check consistency if both resolutors have concluded - if _res != res: - raise ValueError('incompatible resolutors') - break - return res
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/assumptions/handlers/calculus.html b/dev-py3k/_modules/sympy/assumptions/handlers/calculus.html deleted file mode 100644 index 876a5065d0b..00000000000 --- a/dev-py3k/_modules/sympy/assumptions/handlers/calculus.html +++ /dev/null @@ -1,420 +0,0 @@ - - - - - - - - - - sympy.assumptions.handlers.calculus — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.assumptions.handlers.calculus

    -"""
    -This module contains query handlers responsible for calculus queries:
    -infinitesimal, bounded, etc.
    -"""
    -from sympy.logic.boolalg import conjuncts
    -from sympy.assumptions import Q, ask
    -from sympy.assumptions.handlers import CommonHandler
    -
    -
    -
    [docs]class AskInfinitesimalHandler(CommonHandler): - """ - Handler for key 'infinitesimal' - Test that a given expression is equivalent to an infinitesimal - number - """ - - @staticmethod - def _number(expr, assumptions): - # helper method - return expr.evalf() == 0 - - @staticmethod - def Basic(expr, assumptions): - if expr.is_number: - return AskInfinitesimalHandler._number(expr, assumptions) - - @staticmethod -
    [docs] def Mul(expr, assumptions): - """ - Infinitesimal*Bounded -> Infinitesimal - """ - if expr.is_number: - return AskInfinitesimalHandler._number(expr, assumptions) - result = False - for arg in expr.args: - if ask(Q.infinitesimal(arg), assumptions): - result = True - elif ask(Q.bounded(arg), assumptions): - continue - else: - break - else: - return result -
    - Add, Pow = Mul, Mul - - @staticmethod - def Number(expr, assumptions): - return expr == 0 - - NumberSymbol = Number - - @staticmethod - def ImaginaryUnit(expr, assumptions): - return False - -
    -
    [docs]class AskBoundedHandler(CommonHandler): - """ - Handler for key 'bounded'. - - Test that an expression is bounded respect to all its variables. - - Examples of usage: - - >>> from sympy import Symbol, Q - >>> from sympy.assumptions.handlers.calculus import AskBoundedHandler - >>> from sympy.abc import x - >>> a = AskBoundedHandler() - >>> a.Symbol(x, Q.positive(x)) == None - True - >>> a.Symbol(x, Q.bounded(x)) - True - - """ - - @staticmethod -
    [docs] def Symbol(expr, assumptions): - """ - Handles Symbol. - - Examples: - - >>> from sympy import Symbol, Q - >>> from sympy.assumptions.handlers.calculus import AskBoundedHandler - >>> from sympy.abc import x - >>> a = AskBoundedHandler() - >>> a.Symbol(x, Q.positive(x)) == None - True - >>> a.Symbol(x, Q.bounded(x)) - True - - """ - if Q.bounded(expr) in conjuncts(assumptions): - return True - return None -
    - @staticmethod -
    [docs] def Add(expr, assumptions): - """ - Return True if expr is bounded, False if not and None if unknown. - - Truth Table: - - +-------+-----+-----------+-----------+ - | | | | | - | | B | U | ? | - | | | | | - +-------+-----+---+---+---+---+---+---+ - | | | | | | | | | - | | |'+'|'-'|'x'|'+'|'-'|'x'| - | | | | | | | | | - +-------+-----+---+---+---+---+---+---+ - | | | | | - | B | B | U | ? | - | | | | | - +---+---+-----+---+---+---+---+---+---+ - | | | | | | | | | | - | |'+'| | U | ? | ? | U | ? | ? | - | | | | | | | | | | - | +---+-----+---+---+---+---+---+---+ - | | | | | | | | | | - | U |'-'| | ? | U | ? | ? | U | ? | - | | | | | | | | | | - | +---+-----+---+---+---+---+---+---+ - | | | | | | - | |'x'| | ? | ? | - | | | | | | - +---+---+-----+---+---+---+---+---+---+ - | | | | | - | ? | | | ? | - | | | | | - +-------+-----+-----------+---+---+---+ - - * 'B' = Bounded - - * 'U' = Unbounded - - * '?' = unknown boundedness - - * '+' = positive sign - - * '-' = negative sign - - * 'x' = sign unknown - -| - - * All Bounded -> True - - * 1 Unbounded and the rest Bounded -> False - - * >1 Unbounded, all with same known sign -> False - - * Any Unknown and unknown sign -> None - - * Else -> None - - When the signs are not the same you can have an undefined - result as in oo - oo, hence 'bounded' is also undefined. - - """ - - sign = -1 # sign of unknown or unbounded - result = True - for arg in expr.args: - _bounded = ask(Q.bounded(arg), assumptions) - if _bounded: - continue - s = ask(Q.positive(arg), assumptions) - # if there has been more than one sign or if the sign of this arg - # is None and Bounded is None or there was already - # an unknown sign, return None - if sign != -1 and s != sign or \ - s is None and (s == _bounded or s == sign): - return None - else: - sign = s - # once False, do not change - if result is not False: - result = _bounded - return result -
    - @staticmethod -
    [docs] def Mul(expr, assumptions): - """ - Return True if expr is bounded, False if not and None if unknown. - - Truth Table: - - +---+---+---+--------+ - | | | | | - | | B | U | ? | - | | | | | - +---+---+---+---+----+ - | | | | | | - | | | | s | /s | - | | | | | | - +---+---+---+---+----+ - | | | | | - | B | B | U | ? | - | | | | | - +---+---+---+---+----+ - | | | | | | - | U | | U | U | ? | - | | | | | | - +---+---+---+---+----+ - | | | | | - | ? | | | ? | - | | | | | - +---+---+---+---+----+ - - * B = Bounded - - * U = Unbounded - - * ? = unknown boundedness - - * s = signed (hence nonzero) - - * /s = not signed - - """ - result = True - for arg in expr.args: - _bounded = ask(Q.bounded(arg), assumptions) - if _bounded: - continue - elif _bounded is None: - if result is None: - return None - if ask(Q.nonzero(arg), assumptions) is None: - return None - if result is not False: - result = None - else: - result = False - return result -
    - @staticmethod -
    [docs] def Pow(expr, assumptions): - """ - Unbounded ** NonZero -> Unbounded - Bounded ** Bounded -> Bounded - Abs()<=1 ** Positive -> Bounded - Abs()>=1 ** Negative -> Bounded - Otherwise unknown - """ - base_bounded = ask(Q.bounded(expr.base), assumptions) - exp_bounded = ask(Q.bounded(expr.exp), assumptions) - if base_bounded is None and exp_bounded is None: # Common Case - return None - if base_bounded is False and ask(Q.nonzero(expr.exp), assumptions): - return False - if base_bounded and exp_bounded: - return True - if abs(expr.base) <= 1 and ask(Q.positive(expr.exp), assumptions): - return True - if abs(expr.base) >= 1 and ask(Q.negative(expr.exp), assumptions): - return True - if abs(expr.base) >= 1 and exp_bounded is False: - return False - return None -
    - @staticmethod - def log(expr, assumptions): - return ask(Q.bounded(expr.args[0]), assumptions) - - exp = log - - @staticmethod - def sin(expr, assumptions): - return True - - cos = sin - - @staticmethod - def Number(expr, assumptions): - return True - - @staticmethod - def Infinity(expr, assumptions): - return False - - @staticmethod - def NegativeInfinity(expr, assumptions): - return False - - @staticmethod - def Pi(expr, assumptions): - return True - - @staticmethod - def Exp1(expr, assumptions): - return True - - @staticmethod - def ImaginaryUnit(expr, assumptions): - return True - - @staticmethod - def sign(expr, assumptions): - return True
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/assumptions/handlers/ntheory.html b/dev-py3k/_modules/sympy/assumptions/handlers/ntheory.html deleted file mode 100644 index a1ecbdb17eb..00000000000 --- a/dev-py3k/_modules/sympy/assumptions/handlers/ntheory.html +++ /dev/null @@ -1,355 +0,0 @@ - - - - - - - - - - sympy.assumptions.handlers.ntheory — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.assumptions.handlers.ntheory

    -"""
    -Handlers for keys related to number theory: prime, even, odd, etc.
    -"""
    -from sympy.assumptions import Q, ask
    -from sympy.assumptions.handlers import CommonHandler
    -from sympy.ntheory import isprime
    -
    -
    -
    [docs]class AskPrimeHandler(CommonHandler): - """ - Handler for key 'prime' - Test that an expression represents a prime number. When the - expression is a number the result, when True, is subject to - the limitations of isprime() which is used to return the result. - """ - - @staticmethod - def _number(expr, assumptions): - # helper method - try: - i = int(expr.round()) - if not (expr - i).equals(0): - raise TypeError - except TypeError: - return False - return isprime(i) - - @staticmethod - def Basic(expr, assumptions): - # Just use int(expr) once - # http://code.google.com/p/sympy/issues/detail?id=1462 - # is solved - if expr.is_number: - return AskPrimeHandler._number(expr, assumptions) - - @staticmethod - def Mul(expr, assumptions): - if expr.is_number: - return AskPrimeHandler._number(expr, assumptions) - for arg in expr.args: - if ask(Q.integer(arg), assumptions): - pass - else: - break - else: - # a product of integers can't be a prime - return False - - @staticmethod -
    [docs] def Pow(expr, assumptions): - """ - Integer**Integer -> !Prime - """ - if expr.is_number: - return AskPrimeHandler._number(expr, assumptions) - if ask(Q.integer(expr.exp), assumptions) and \ - ask(Q.integer(expr.base), assumptions): - return False -
    - @staticmethod - def Integer(expr, assumptions): - return isprime(expr) - - @staticmethod - def Rational(expr, assumptions): - return False - - @staticmethod - def Float(expr, assumptions): - return AskPrimeHandler._number(expr, assumptions) - - @staticmethod - def Infinity(expr, assumptions): - return False - - @staticmethod - def NegativeInfinity(expr, assumptions): - return False - - @staticmethod - def ImaginaryUnit(expr, assumptions): - return False - - @staticmethod - def NumberSymbol(expr, assumptions): - return AskPrimeHandler._number(expr, assumptions) - -
    -class AskCompositeHandler(CommonHandler): - - @staticmethod - def Basic(expr, assumptions): - _positive = ask(Q.positive(expr), assumptions) - if _positive: - _integer = ask(Q.integer(expr), assumptions) - if _integer: - _prime = ask(Q.prime(expr), assumptions) - if _prime is None: - return - return not _prime - else: - return _integer - else: - return _positive - - -class AskEvenHandler(CommonHandler): - - @staticmethod - def _number(expr, assumptions): - # helper method - try: - i = int(expr.round()) - if not (expr - i).equals(0): - raise TypeError - except TypeError: - return False - return i % 2 == 0 - - @staticmethod - def Basic(expr, assumptions): - if expr.is_number: - return AskEvenHandler._number(expr, assumptions) - - @staticmethod - def Mul(expr, assumptions): - """ - Even * Integer -> Even - Even * Odd -> Even - Integer * Odd -> ? - Odd * Odd -> Odd - """ - if expr.is_number: - return AskEvenHandler._number(expr, assumptions) - even, odd, irrational = False, 0, False - for arg in expr.args: - # check for all integers and at least one even - if ask(Q.integer(arg), assumptions): - if ask(Q.even(arg), assumptions): - even = True - elif ask(Q.odd(arg), assumptions): - odd += 1 - elif ask(Q.irrational(arg), assumptions): - # one irrational makes the result False - # two makes it undefined - if irrational: - break - irrational = True - else: - break - else: - if irrational: - return False - if even: - return True - if odd == len(expr.args): - return False - - @staticmethod - def Add(expr, assumptions): - """ - Even + Odd -> Odd - Even + Even -> Even - Odd + Odd -> Even - - """ - if expr.is_number: - return AskEvenHandler._number(expr, assumptions) - _result = True - for arg in expr.args: - if ask(Q.even(arg), assumptions): - pass - elif ask(Q.odd(arg), assumptions): - _result = not _result - else: - break - else: - return _result - - @staticmethod - def Integer(expr, assumptions): - return not bool(expr.p & 1) - - @staticmethod - def Rational(expr, assumptions): - return False - - @staticmethod - def Float(expr, assumptions): - return expr % 2 == 0 - - @staticmethod - def Infinity(expr, assumptions): - return False - - @staticmethod - def NegativeInfinity(expr, assumptions): - return False - - @staticmethod - def NumberSymbol(expr, assumptions): - return AskEvenHandler._number(expr, assumptions) - - @staticmethod - def ImaginaryUnit(expr, assumptions): - return False - - @staticmethod - def Abs(expr, assumptions): - if ask(Q.real(expr.args[0]), assumptions): - return ask(Q.even(expr.args[0]), assumptions) - - @staticmethod - def re(expr, assumptions): - if ask(Q.real(expr.args[0]), assumptions): - return ask(Q.even(expr.args[0]), assumptions) - - @staticmethod - def im(expr, assumptions): - if ask(Q.real(expr.args[0]), assumptions): - return True - - -
    [docs]class AskOddHandler(CommonHandler): - """ - Handler for key 'odd' - Test that an expression represents an odd number - """ - - @staticmethod - def Basic(expr, assumptions): - _integer = ask(Q.integer(expr), assumptions) - if _integer: - _even = ask(Q.even(expr), assumptions) - if _even is None: - return None - return not _even - return _integer
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/assumptions/handlers/order.html b/dev-py3k/_modules/sympy/assumptions/handlers/order.html deleted file mode 100644 index 8cf309536ca..00000000000 --- a/dev-py3k/_modules/sympy/assumptions/handlers/order.html +++ /dev/null @@ -1,316 +0,0 @@ - - - - - - - - - - sympy.assumptions.handlers.order — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.assumptions.handlers.order

    -"""
    -AskHandlers related to order relations: positive, negative, etc.
    -"""
    -from sympy.assumptions import Q, ask
    -from sympy.assumptions.handlers import CommonHandler
    -
    -
    -
    [docs]class AskNegativeHandler(CommonHandler): - """ - This is called by ask() when key='negative' - - Test that an expression is less (strict) than zero. - - Examples: - - >>> from sympy import ask, Q, pi - >>> ask(Q.negative(pi+1)) # this calls AskNegativeHandler.Add - False - >>> ask(Q.negative(pi**2)) # this calls AskNegativeHandler.Pow - False - - """ - - @staticmethod - def _number(expr, assumptions): - if not expr.as_real_imag()[1]: - return expr.evalf() < 0 - else: - return False - - @staticmethod - def Basic(expr, assumptions): - if expr.is_number: - return AskNegativeHandler._number(expr, assumptions) - - @staticmethod -
    [docs] def Add(expr, assumptions): - """ - Positive + Positive -> Positive, - Negative + Negative -> Negative - """ - if expr.is_number: - return AskNegativeHandler._number(expr, assumptions) - for arg in expr.args: - if not ask(Q.negative(arg), assumptions): - break - else: - # if all argument's are negative - return True -
    - @staticmethod - def Mul(expr, assumptions): - if expr.is_number: - return AskNegativeHandler._number(expr, assumptions) - result = None - for arg in expr.args: - if result is None: - result = False - if ask(Q.negative(arg), assumptions): - result = not result - elif ask(Q.positive(arg), assumptions): - pass - else: - return - return result - - @staticmethod -
    [docs] def Pow(expr, assumptions): - """ - Real ** Even -> NonNegative - Real ** Odd -> same_as_base - NonNegative ** Positive -> NonNegative - """ - if expr.is_number: - return AskNegativeHandler._number(expr, assumptions) - if ask(Q.real(expr.base), assumptions): - if ask(Q.positive(expr.base), assumptions): - return False - if ask(Q.even(expr.exp), assumptions): - return False - if ask(Q.odd(expr.exp), assumptions): - return ask(Q.negative(expr.base), assumptions) -
    - @staticmethod - def ImaginaryUnit(expr, assumptions): - return False - - @staticmethod - def Abs(expr, assumptions): - return False - -
    -
    [docs]class AskNonZeroHandler(CommonHandler): - """ - Handler for key 'zero' - Test that an expression is not identically zero - """ - - @staticmethod - def Basic(expr, assumptions): - if expr.is_number: - # if there are no symbols just evalf - return expr.evalf() != 0 - - @staticmethod - def Add(expr, assumptions): - if all(ask(Q.positive(x), assumptions) for x in expr.args) \ - or all(ask(Q.negative(x), assumptions) for x in expr.args): - return True - - @staticmethod - def Mul(expr, assumptions): - for arg in expr.args: - result = ask(Q.nonzero(arg), assumptions) - if result: - continue - return result - return True - - @staticmethod - def Pow(expr, assumptions): - return ask(Q.nonzero(expr.base), assumptions) - - @staticmethod - def NaN(expr, assumptions): - return True - - @staticmethod - def Abs(expr, assumptions): - return ask(Q.nonzero(expr.args[0]), assumptions) - -
    -
    [docs]class AskPositiveHandler(CommonHandler): - """ - Handler for key 'positive' - Test that an expression is greater (strict) than zero - """ - - @staticmethod - def _number(expr, assumptions): - if not expr.as_real_imag()[1]: - return expr.evalf() > 0 - else: - return False - - @staticmethod - def Basic(expr, assumptions): - if expr.is_number: - return AskPositiveHandler._number(expr, assumptions) - - @staticmethod - def Mul(expr, assumptions): - if expr.is_number: - return AskPositiveHandler._number(expr, assumptions) - result = True - for arg in expr.args: - if ask(Q.positive(arg), assumptions): - continue - elif ask(Q.negative(arg), assumptions): - result = result ^ True - else: - return - return result - - @staticmethod - def Add(expr, assumptions): - if expr.is_number: - return AskPositiveHandler._number(expr, assumptions) - for arg in expr.args: - if ask(Q.positive(arg), assumptions) is not True: - break - else: - # if all argument's are positive - return True - - @staticmethod - def Pow(expr, assumptions): - if expr.is_number: - return expr.evalf() > 0 - if ask(Q.positive(expr.base), assumptions): - return True - if ask(Q.negative(expr.base), assumptions): - if ask(Q.even(expr.exp), assumptions): - return True - if ask(Q.even(expr.exp), assumptions): - return False - - @staticmethod - def exp(expr, assumptions): - if ask(Q.real(expr.args[0]), assumptions): - return True - - @staticmethod - def ImaginaryUnit(expr, assumptions): - return False - - @staticmethod - def Abs(expr, assumptions): - return ask(Q.nonzero(expr), assumptions)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/assumptions/handlers/sets.html b/dev-py3k/_modules/sympy/assumptions/handlers/sets.html deleted file mode 100644 index a8a49bec97e..00000000000 --- a/dev-py3k/_modules/sympy/assumptions/handlers/sets.html +++ /dev/null @@ -1,755 +0,0 @@ - - - - - - - - - - sympy.assumptions.handlers.sets — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.assumptions.handlers.sets

    -"""
    -Handlers for predicates related to set membership: integer, rational, etc.
    -"""
    -from sympy.assumptions import Q, ask
    -from sympy.assumptions.handlers import CommonHandler
    -from sympy import I
    -
    -
    -
    [docs]class AskIntegerHandler(CommonHandler): - """ - Handler for Q.integer - Test that an expression belongs to the field of integer numbers - """ - - @staticmethod - def _number(expr, assumptions): - # helper method - try: - i = int(expr.round()) - if not (expr - i).equals(0): - raise TypeError - return True - except TypeError: - return False - - @staticmethod -
    [docs] def Add(expr, assumptions): - """ - Integer + Integer -> Integer - Integer + !Integer -> !Integer - !Integer + !Integer -> ? - """ - if expr.is_number: - return AskIntegerHandler._number(expr, assumptions) - return test_closed_group(expr, assumptions, Q.integer) -
    - @staticmethod -
    [docs] def Mul(expr, assumptions): - """ - Integer*Integer -> Integer - Integer*Irrational -> !Integer - Odd/Even -> !Integer - Integer*Rational -> ? - """ - if expr.is_number: - return AskIntegerHandler._number(expr, assumptions) - _output = True - for arg in expr.args: - if not ask(Q.integer(arg), assumptions): - if arg.is_Rational: - if arg.q == 2: - return ask(Q.even(2*expr), assumptions) - if ~(arg.q & 1): - return None - elif ask(Q.irrational(arg), assumptions): - if _output: - _output = False - else: - return - else: - return - else: - return _output -
    - Pow = Add - - @staticmethod - def int(expr, assumptions): - return True - - @staticmethod - def Integer(expr, assumptions): - return True - - @staticmethod - def Rational(expr, assumptions): - # rationals with denominator one get - # evaluated to Integers - return False - - @staticmethod - def Float(expr, assumptions): - return int(expr) == expr - - @staticmethod - def Pi(expr, assumptions): - return False - - @staticmethod - def Exp1(expr, assumptions): - return False - - @staticmethod - def Infinity(expr, assumptions): - return False - - @staticmethod - def NegativeInfinity(expr, assumptions): - return False - - @staticmethod - def ImaginaryUnit(expr, assumptions): - return False - - @staticmethod - def Abs(expr, assumptions): - return ask(Q.integer(expr.args[0]), assumptions) - -
    -
    [docs]class AskRationalHandler(CommonHandler): - """ - Handler for Q.rational - Test that an expression belongs to the field of rational numbers - """ - - @staticmethod -
    [docs] def Add(expr, assumptions): - """ - Rational + Rational -> Rational - Rational + !Rational -> !Rational - !Rational + !Rational -> ? - """ - if expr.is_number: - if expr.as_real_imag()[1]: - return False - return test_closed_group(expr, assumptions, Q.rational) -
    - Mul = Add - - @staticmethod -
    [docs] def Pow(expr, assumptions): - """ - Rational ** Integer -> Rational - Irrational ** Rational -> Irrational - Rational ** Irrational -> ? - """ - if ask(Q.integer(expr.exp), assumptions): - return ask(Q.rational(expr.base), assumptions) - elif ask(Q.rational(expr.exp), assumptions): - if ask(Q.prime(expr.base), assumptions): - return False -
    - @staticmethod - def Rational(expr, assumptions): - return True - - @staticmethod - def Float(expr, assumptions): - # it's finite-precision - return True - - @staticmethod - def ImaginaryUnit(expr, assumptions): - return False - - @staticmethod - def Infinity(expr, assumptions): - return False - - @staticmethod - def NegativeInfinity(expr, assumptions): - return False - - @staticmethod - def Pi(expr, assumptions): - return False - - @staticmethod - def Exp1(expr, assumptions): - return False - -
    -class AskIrrationalHandler(CommonHandler): - - @staticmethod - def Basic(expr, assumptions): - _real = ask(Q.real(expr), assumptions) - if _real: - _rational = ask(Q.rational(expr), assumptions) - if _rational is None: - return None - return not _rational - else: - return _real - - -
    [docs]class AskRealHandler(CommonHandler): - """ - Handler for Q.real - Test that an expression belongs to the field of real numbers - """ - - @staticmethod - def _number(expr, assumptions): - # let as_real_imag() work first since the expression may - # be simpler to evaluate - i = expr.as_real_imag()[1].evalf(2) - if i._prec != 1: - return not i - # allow None to be returned if we couldn't show for sure - # that i was 0 - - @staticmethod -
    [docs] def Add(expr, assumptions): - """ - Real + Real -> Real - Real + (Complex & !Real) -> !Real - """ - if expr.is_number: - return AskRealHandler._number(expr, assumptions) - return test_closed_group(expr, assumptions, Q.real) -
    - @staticmethod -
    [docs] def Mul(expr, assumptions): - """ - Real*Real -> Real - Real*Imaginary -> !Real - Imaginary*Imaginary -> Real - """ - if expr.is_number: - return AskRealHandler._number(expr, assumptions) - result = True - for arg in expr.args: - if ask(Q.real(arg), assumptions): - pass - elif ask(Q.imaginary(arg), assumptions): - result = result ^ True - else: - break - else: - return result -
    - @staticmethod -
    [docs] def Pow(expr, assumptions): - """ - Real**Integer -> Real - Positive**Real -> Real - Real**(Integer/Even) -> Real if base is nonnegative - Real**(Integer/Odd) -> Real - Real**Imaginary -> ? - Imaginary**Real -> ? - Real**Real -> ? - """ - if expr.is_number: - return AskRealHandler._number(expr, assumptions) - if ask(Q.imaginary(expr.base), assumptions): - if ask(Q.real(expr.exp), assumptions): - if ask(Q.odd(expr.exp), assumptions): - return False - elif ask(Q.even(expr.exp), assumptions): - return True - elif ask(Q.real(expr.base), assumptions): - if ask(Q.real(expr.exp), assumptions): - if expr.exp.is_Rational and \ - ask(Q.even(expr.exp.q), assumptions): - return ask(Q.positive(expr.base), assumptions) - elif ask(Q.integer(expr.exp), assumptions): - return True - elif ask(Q.positive(expr.base), assumptions): - return True - elif ask(Q.negative(expr.base), assumptions): - return False -
    - @staticmethod - def Rational(expr, assumptions): - return True - - @staticmethod - def Float(expr, assumptions): - return True - - @staticmethod - def Pi(expr, assumptions): - return True - - @staticmethod - def Exp1(expr, assumptions): - return True - - @staticmethod - def Abs(expr, assumptions): - return True - - @staticmethod - def re(expr, assumptions): - return True - - im = re - - @staticmethod - def ImaginaryUnit(expr, assumptions): - return False - - @staticmethod - def Infinity(expr, assumptions): - return False - - @staticmethod - def NegativeInfinity(expr, assumptions): - return False - - @staticmethod - def sin(expr, assumptions): - if ask(Q.real(expr.args[0]), assumptions): - return True - - cos, exp = sin, sin - -
    -
    [docs]class AskExtendedRealHandler(AskRealHandler): - """ - Handler for Q.extended_real - Test that an expression belongs to the field of extended real numbers, - that is real numbers union {Infinity, -Infinity} - """ - - @staticmethod - def Add(expr, assumptions): - return test_closed_group(expr, assumptions, Q.extended_real) - - Mul, Pow = Add, Add - - @staticmethod - def Infinity(expr, assumptions): - return True - - @staticmethod - def NegativeInfinity(expr, assumptions): - return True - -
    -
    [docs]class AskHermitianHandler(AskRealHandler): - """ - Handler for Q.hermitian - Test that an expression belongs to the field of Hermitian operators - """ - - @staticmethod -
    [docs] def Add(expr, assumptions): - """ - Hermitian + Hermitian -> Hermitian - Hermitian + !Hermitian -> !Hermitian - """ - if expr.is_number: - return AskRealHandler._number(expr, assumptions) - return test_closed_group(expr, assumptions, Q.hermitian) -
    - @staticmethod -
    [docs] def Mul(expr, assumptions): - """ - As long as there is at most only one noncommutative term: - Hermitian*Hermitian -> Hermitian - Hermitian*Antihermitian -> !Hermitian - Antihermitian*Antihermitian -> Hermitian - """ - if expr.is_number: - return AskRealHandler._number(expr, assumptions) - nccount = 0 - result = True - for arg in expr.args: - if ask(Q.antihermitian(arg), assumptions): - result = result ^ True - elif not ask(Q.hermitian(arg), assumptions): - break - if ask(~Q.commutative(arg), assumptions): - nccount += 1 - if nccount > 1: - break - else: - return result -
    - @staticmethod -
    [docs] def Pow(expr, assumptions): - """ - Hermitian**Integer -> Hermitian - """ - if expr.is_number: - return AskRealHandler._number(expr, assumptions) - if ask(Q.hermitian(expr.base), assumptions): - if ask(Q.integer(expr.exp), assumptions): - return True -
    - @staticmethod - def sin(expr, assumptions): - if ask(Q.hermitian(expr.args[0]), assumptions): - return True - - cos, exp = sin, sin - -
    -
    [docs]class AskComplexHandler(CommonHandler): - """ - Handler for Q.complex - Test that an expression belongs to the field of complex numbers - """ - - @staticmethod - def Add(expr, assumptions): - return test_closed_group(expr, assumptions, Q.complex) - - Mul, Pow = Add, Add - - @staticmethod - def Number(expr, assumptions): - return True - - @staticmethod - def NumberSymbol(expr, assumptions): - return True - - @staticmethod - def Abs(expr, assumptions): - return True - - @staticmethod - def ImaginaryUnit(expr, assumptions): - return True - - @staticmethod - def Infinity(expr, assumptions): - return False - - @staticmethod - def NegativeInfinity(expr, assumptions): - return False - - sin, cos, exp, re, im = [Abs]*5 # they are all complex functions - -
    -
    [docs]class AskImaginaryHandler(CommonHandler): - """ - Handler for Q.imaginary - Test that an expression belongs to the field of imaginary numbers, - that is, numbers in the form x*I, where x is real - """ - - @staticmethod - def _number(expr, assumptions): - # helper method - return not expr.as_real_imag()[0].evalf() - - @staticmethod -
    [docs] def Add(expr, assumptions): - """ - Imaginary + Imaginary -> Imaginary - Imaginary + Complex -> ? - Imaginary + Real -> !Imaginary - """ - if expr.is_number: - return AskImaginaryHandler._number(expr, assumptions) - reals = 0 - for arg in expr.args: - if ask(Q.imaginary(arg), assumptions): - pass - elif ask(Q.real(arg), assumptions): - reals += 1 - else: - break - else: - if reals == 0: - return True - if reals == 1 or (len(expr.args) == reals): - # two reals could sum 0 thus giving an imaginary - return False -
    - @staticmethod -
    [docs] def Mul(expr, assumptions): - """ - Real*Imaginary -> Imaginary - Imaginary*Imaginary -> Real - """ - if expr.is_number: - return AskImaginaryHandler._number(expr, assumptions) - result = False - reals = 0 - for arg in expr.args: - if ask(Q.imaginary(arg), assumptions): - result = result ^ True - elif not ask(Q.real(arg), assumptions): - break - else: - if reals == len(expr.args): - return False - return result -
    - @staticmethod -
    [docs] def Pow(expr, assumptions): - """ - Imaginary**integer -> Imaginary if integer % 2 == 1 - Imaginary**integer -> real if integer % 2 == 0 - Imaginary**Imaginary -> ? - Imaginary**Real -> ? - """ - if expr.is_number: - return AskImaginaryHandler._number(expr, assumptions) - if ask(Q.imaginary(expr.base), assumptions): - if ask(Q.real(expr.exp), assumptions): - if ask(Q.odd(expr.exp), assumptions): - return True - elif ask(Q.even(expr.exp), assumptions): - return False - elif ask(Q.real(expr.base), assumptions): - if ask(Q.real(expr.exp), assumptions): - if expr.exp.is_Rational and \ - ask(Q.even(expr.exp.q), assumptions): - return ask(Q.negative(expr.base),assumptions) - elif ask(Q.integer(expr.exp), assumptions): - return False - elif ask(Q.positive(expr.base), assumptions): - return False - elif ask(Q.negative(expr.base), assumptions): - return True - - -
    - @staticmethod - def Number(expr, assumptions): - return not (expr.as_real_imag()[1] == 0) - - NumberSymbol = Number - - @staticmethod - def ImaginaryUnit(expr, assumptions): - return True - -
    -
    [docs]class AskAntiHermitianHandler(AskImaginaryHandler): - """ - Handler for Q.antihermitian - Test that an expression belongs to the field of anti-Hermitian operators, - that is, operators in the form x*I, where x is Hermitian - """ - - @staticmethod -
    [docs] def Add(expr, assumptions): - """ - Antihermitian + Antihermitian -> Antihermitian - Antihermitian + !Antihermitian -> !Antihermitian - """ - if expr.is_number: - return AskImaginaryHandler._number(expr, assumptions) - return test_closed_group(expr, assumptions, Q.antihermitian) -
    - @staticmethod -
    [docs] def Mul(expr, assumptions): - """ - As long as there is at most only one noncommutative term: - Hermitian*Hermitian -> !Antihermitian - Hermitian*Antihermitian -> Antihermitian - Antihermitian*Antihermitian -> !Antihermitian - """ - if expr.is_number: - return AskImaginaryHandler._number(expr, assumptions) - nccount = 0 - result = False - for arg in expr.args: - if ask(Q.antihermitian(arg), assumptions): - result = result ^ True - elif not ask(Q.hermitian(arg), assumptions): - break - if ask(~Q.commutative(arg), assumptions): - nccount += 1 - if nccount > 1: - break - else: - return result -
    - @staticmethod -
    [docs] def Pow(expr, assumptions): - """ - Hermitian**Integer -> !Antihermitian - Antihermitian**Even -> !Antihermitian - Antihermitian**Odd -> Antihermitian - """ - if expr.is_number: - return AskImaginaryHandler._number(expr, assumptions) - if ask(Q.hermitian(expr.base), assumptions): - if ask(Q.integer(expr.exp), assumptions): - return False - elif ask(Q.antihermitian(expr.base), assumptions): - if ask(Q.even(expr.exp), assumptions): - return False - elif ask(Q.odd(expr.exp), assumptions): - return True - -
    -
    [docs]class AskAlgebraicHandler(CommonHandler): - """Handler for Q.algebraic key. """ - - @staticmethod - def Add(expr, assumptions): - return test_closed_group(expr, assumptions, Q.algebraic) - - @staticmethod - def Mul(expr, assumptions): - return test_closed_group(expr, assumptions, Q.algebraic) - - @staticmethod - def Pow(expr, assumptions): - return expr.exp.is_Rational and ask( - Q.algebraic(expr.base), assumptions) - - @staticmethod - def Number(expr, assumptions): - return False - - @staticmethod - def Rational(expr, assumptions): - return expr.q != 0 - - @staticmethod - def ImaginaryUnit(expr, assumptions): - return True - - @staticmethod - def AlgebraicNumber(expr, assumptions): - return True - -#### Helper methods - -
    -
    [docs]def test_closed_group(expr, assumptions, key): - """ - Test for membership in a group with respect - to the current operation - """ - result = True - for arg in expr.args: - _out = ask(key(arg), assumptions) - if _out is None: - break - elif _out is False: - if result: - result = False - else: - break - else: - return result
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/assumptions/refine.html b/dev-py3k/_modules/sympy/assumptions/refine.html deleted file mode 100644 index ecb772c7dc7..00000000000 --- a/dev-py3k/_modules/sympy/assumptions/refine.html +++ /dev/null @@ -1,283 +0,0 @@ - - - - - - - - - - sympy.assumptions.refine — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.assumptions.refine

    -from sympy.core import S, Add
    -from sympy.assumptions import Q, ask
    -from sympy.core.logic import fuzzy_not
    -
    -
    -
    [docs]def refine(expr, assumptions=True): - """ - Simplify an expression using assumptions. - - Gives the form of expr that would be obtained if symbols - in it were replaced by explicit numerical expressions satisfying - the assumptions. - - Examples - ======== - - >>> from sympy import refine, sqrt, Q - >>> from sympy.abc import x - >>> refine(sqrt(x**2), Q.real(x)) - Abs(x) - >>> refine(sqrt(x**2), Q.positive(x)) - x - - """ - if not expr.is_Atom: - args = [refine(arg, assumptions) for arg in expr.args] - # TODO: this will probably not work with Integral or Polynomial - expr = expr.func(*args) - name = expr.__class__.__name__ - handler = handlers_dict.get(name, None) - if handler is None: - return expr - new_expr = handler(expr, assumptions) - if (new_expr is None) or (expr == new_expr): - return expr - return refine(new_expr, assumptions) - -
    -
    [docs]def refine_abs(expr, assumptions): - """ - Handler for the absolute value. - - Examples - ======== - - >>> from sympy import Symbol, Q, refine, Abs - >>> from sympy.assumptions.refine import refine_abs - >>> from sympy.abc import x - >>> refine_abs(Abs(x), Q.real(x)) - >>> refine_abs(Abs(x), Q.positive(x)) - x - >>> refine_abs(Abs(x), Q.negative(x)) - -x - - """ - arg = expr.args[0] - if ask(Q.real(arg), assumptions) and \ - fuzzy_not(ask(Q.negative(arg), assumptions)): - # if it's nonnegative - return arg - if ask(Q.negative(arg), assumptions): - return -arg - -
    -
    [docs]def refine_Pow(expr, assumptions): - """ - Handler for instances of Pow. - - >>> from sympy import Symbol, Q - >>> from sympy.assumptions.refine import refine_Pow - >>> from sympy.abc import x,y,z - >>> refine_Pow((-1)**x, Q.real(x)) - >>> refine_Pow((-1)**x, Q.even(x)) - 1 - >>> refine_Pow((-1)**x, Q.odd(x)) - -1 - - For powers of -1, even parts of the exponent can be simplified: - - >>> refine_Pow((-1)**(x+y), Q.even(x)) - (-1)**y - >>> refine_Pow((-1)**(x+y+z), Q.odd(x) & Q.odd(z)) - (-1)**y - >>> refine_Pow((-1)**(x+y+2), Q.odd(x)) - (-1)**(y + 1) - >>> refine_Pow((-1)**(x+3), True) - (-1)**(x + 1) - - """ - from sympy.core import Pow, Rational - from sympy.functions import sign - if ask(Q.real(expr.base), assumptions): - if expr.base.is_number: - if ask(Q.even(expr.exp), assumptions): - return abs(expr.base) ** expr.exp - if ask(Q.odd(expr.exp), assumptions): - return sign(expr.base) * abs(expr.base) ** expr.exp - if isinstance(expr.exp, Rational): - if type(expr.base) is Pow: - return abs(expr.base.base) ** (expr.base.exp * expr.exp) - - if expr.base is S.NegativeOne: - if expr.exp.is_Add: - - # For powers of (-1) we can remove - # - even terms - # - pairs of odd terms - # - a single odd term + 1 - # - A numerical constant N can be replaced with mod(N,2) - - coeff, terms = expr.exp.as_coeff_add() - terms = set(terms) - even_terms = set([]) - odd_terms = set([]) - initial_number_of_terms = len(terms) - - for t in terms: - if ask(Q.even(t), assumptions): - even_terms.add(t) - elif ask(Q.odd(t), assumptions): - odd_terms.add(t) - - terms -= even_terms - if len(odd_terms) % 2: - terms -= odd_terms - new_coeff = (coeff + S.One) % 2 - else: - terms -= odd_terms - new_coeff = coeff % 2 - - if new_coeff != coeff or len(terms) < initial_number_of_terms: - terms.add(new_coeff) - return expr.base**(Add(*terms)) - -
    -
    [docs]def refine_exp(expr, assumptions): - """ - Handler for exponential function. - - >>> from sympy import Symbol, Q, exp, I, pi - >>> from sympy.assumptions.refine import refine_exp - >>> from sympy.abc import x - >>> refine_exp(exp(pi*I*2*x), Q.real(x)) - >>> refine_exp(exp(pi*I*2*x), Q.integer(x)) - 1 - - """ - arg = expr.args[0] - if arg.is_Mul: - coeff = arg.as_coefficient(S.Pi*S.ImaginaryUnit) - if coeff: - if ask(Q.integer(2*coeff), assumptions): - if ask(Q.even(coeff), assumptions): - return S.One - elif ask(Q.odd(coeff), assumptions): - return S.NegativeOne - elif ask(Q.even(coeff + S.Half), assumptions): - return -S.ImaginaryUnit - elif ask(Q.odd(coeff + S.Half), assumptions): - return S.ImaginaryUnit -
    -handlers_dict = { - 'Abs': refine_abs, - 'Pow': refine_Pow, - 'exp': refine_exp, -} -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/categories.html b/dev-py3k/_modules/sympy/categories.html deleted file mode 100644 index 040239d1271..00000000000 --- a/dev-py3k/_modules/sympy/categories.html +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - - - - - sympy.categories — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.categories

    -"""
    -Category Theory module.
    -
    -Provides some of the fundamental category-theory-related classes,
    -including categories, morphisms, diagrams.  Functors are not
    -implemented yet.
    -
    -The general reference work this module tries to follow is
    -
    -  [JoyOfCats] J. Adamek, H. Herrlich. G. E. Strecker: Abstract and
    -              Concrete Categories. The Joy of Cats.
    -
    -The latest version of this book should be available for free download
    -from
    -
    -   katmat.math.uni-bremen.de/acc/acc.pdf
    -
    -"""
    -
    -from .baseclasses import (Object, Morphism, IdentityMorphism,
    -                         NamedMorphism, CompositeMorphism, Category,
    -                         Diagram)
    -
    -from .diagram_drawing import (DiagramGrid, XypicDiagramDrawer,
    -                             xypic_draw_diagram, preview_diagram)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/categories/diagram_drawing.html b/dev-py3k/_modules/sympy/categories/diagram_drawing.html deleted file mode 100644 index 2eedfdd73be..00000000000 --- a/dev-py3k/_modules/sympy/categories/diagram_drawing.html +++ /dev/null @@ -1,2699 +0,0 @@ - - - - - - - - - - sympy.categories.diagram_drawing — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.categories.diagram_drawing

    -r"""
    -This module contains the functionality to arrange the nodes of a
    -diagram on an abstract grid, and then to produce a graphical
    -representation of the grid.
    -
    -The currently supported back-ends are Xy-pic [Xypic].
    -
    -Layout Algorithm
    -================
    -
    -This section provides an overview of the algorithms implemented in
    -:class:`DiagramGrid` to lay out diagrams.
    -
    -The first step of the algorithm is the removal composite and identity
    -morphisms which do not have properties in the supplied diagram.  The
    -premises and conclusions of the diagram are then merged.
    -
    -The generic layout algorithm begins with the construction of the
    -"skeleton" of the diagram.  The skeleton is an undirected graph which
    -has the objects of the diagram as vertices and has an (undirected)
    -edge between each pair of objects between which there exist morphisms.
    -The direction of the morphisms does not matter at this stage.  The
    -skeleton also includes an edge between each pair of vertices `A` and
    -`C` such that there exists an object `B` which is connected via
    -a morphism to `A`, and via a morphism to `C`.
    -
    -The skeleton constructed in this way has the property that every
    -object is a vertex of a triangle formed by three edges of the
    -skeleton.  This property lies at the base of the generic layout
    -algorithm.
    -
    -After the skeleton has been constructed, the algorithm lists all
    -triangles which can be formed.  Note that some triangles will not have
    -all edges corresponding to morphisms which will actually be drawn.
    -Triangles which have only one edge or less which will actually be
    -drawn are immediately discarded.
    -
    -The list of triangles is sorted according to the number of edges which
    -correspond to morphisms, then the triangle with the least number of such
    -edges is selected.  One of such edges is picked and the corresponding
    -objects are placed horizontally, on a grid.  This edge is recorded to
    -be in the fringe.  The algorithm then finds a "welding" of a triangle
    -to the fringe.  A welding is an edge in the fringe where a triangle
    -could be attached.  If the algorithm succeeds in finding such a
    -welding, it adds to the grid that vertex of the triangle which was not
    -yet included in any edge in the fringe and records the two new edges in
    -the fringe.  This process continues iteratively until all objects of
    -the diagram has been placed or until no more weldings can be found.
    -
    -An edge is only removed from the fringe when a welding to this edge
    -has been found, and there is no room around this edge to place
    -another vertex.
    -
    -When no more weldings can be found, but there are still triangles
    -left, the algorithm searches for a possibility of attaching one of the
    -remaining triangles to the existing structure by a vertex.  If such a
    -possibility is found, the corresponding edge of the found triangle is
    -placed in the found space and the iterative process of welding
    -triangles restarts.
    -
    -When logical groups are supplied, each of these groups is laid out
    -independently.  Then a diagram is constructed in which groups are
    -objects and any two logical groups between which there exist morphisms
    -are connected via a morphism.  This diagram is laid out.  Finally,
    -the grid which includes all objects of the initial diagram is
    -constructed by replacing the cells which contain logical groups with
    -the corresponding laid out grids, and by correspondingly expanding the
    -rows and columns.
    -
    -The sequential layout algorithm begins by constructing the
    -underlying undirected graph defined by the morphisms obtained after
    -simplifying premises and conclusions and merging them (see above).
    -The vertex with the minimal degree is then picked up and depth-first
    -search is started from it.  All objects which are located at distance
    -`n` from the root in the depth-first search tree, are positioned in
    -the `n`-th column of the resulting grid.  The sequential layout will
    -therefore attempt to lay the objects out along a line.
    -
    -References
    -==========
    -
    -[Xypic] http://www.tug.org/applications/Xy-pic/
    -"""
    -
    -from sympy.core import Basic, FiniteSet, Dict, Symbol
    -from sympy.categories import (CompositeMorphism, IdentityMorphism,
    -                              NamedMorphism, Diagram)
    -from sympy.utilities import default_sort_key
    -from itertools import chain
    -from sympy.core.compatibility import iterable
    -from sympy.printing import latex
    -
    -
    -class _GrowableGrid(object):
    -    """
    -    Holds a growable grid of objects.
    -
    -    It is possible to append or prepend a row or a column to the grid
    -    using the corresponding methods.  Prepending rows or columns has
    -    the effect of changing the coordinates of the already existing
    -    elements.
    -
    -    This class currently represents a naive implementation of the
    -    functionality with little attempt at optimisation.
    -    """
    -    def __init__(self, width, height):
    -        self._width = width
    -        self._height = height
    -
    -        self._array = [[None for j in range(width)] for i in range(height)]
    -
    -    @property
    -    def width(self):
    -        return self._width
    -
    -    @property
    -    def height(self):
    -        return self._height
    -
    -    def __getitem__(self, xxx_todo_changeme):
    -        """
    -        Returns the element located at in the i-th line and j-th
    -        column.
    -        """
    -        (i, j) = xxx_todo_changeme
    -        return self._array[i][j]
    -
    -    def __setitem__(self, xxx_todo_changeme1, newvalue):
    -        """
    -        Sets the element located at in the i-th line and j-th
    -        column.
    -        """
    -        (i, j) = xxx_todo_changeme1
    -        self._array[i][j] = newvalue
    -
    -    def append_row(self):
    -        """
    -        Appends an empty row to the grid.
    -        """
    -        self._height += 1
    -        self._array.append([None for j in range(self._width)])
    -
    -    def append_column(self):
    -        """
    -        Appends an empty column to the grid.
    -        """
    -        self._width += 1
    -        for i in range(self._height):
    -            self._array[i].append(None)
    -
    -    def prepend_row(self):
    -        """
    -        Prepends the grid with an empty row.
    -        """
    -        self._height += 1
    -        self._array.insert(0, [None for j in range(self._width)])
    -
    -    def prepend_column(self):
    -        """
    -        Prepends the grid with an empty column.
    -        """
    -        self._width += 1
    -        for i in range(self._height):
    -            self._array[i].insert(0, None)
    -
    -
    -
    [docs]class DiagramGrid(object): - r""" - Constructs and holds the fitting of the diagram into a grid. - - The mission of this class is to analyse the structure of the - supplied diagram and to place its objects on a grid such that, - when the objects and the morphisms are actually drawn, the diagram - would be "readable", in the sense that there will not be many - intersections of moprhisms. This class does not perform any - actual drawing. It does strive nevertheless to offer sufficient - metadata to draw a diagram. - - Consider the following simple diagram. - - >>> from sympy.categories import Object, NamedMorphism - >>> from sympy.categories import Diagram, DiagramGrid - >>> from sympy import pprint - >>> A = Object("A") - >>> B = Object("B") - >>> C = Object("C") - >>> f = NamedMorphism(A, B, "f") - >>> g = NamedMorphism(B, C, "g") - >>> diagram = Diagram([f, g]) - - The simplest way to have a diagram laid out is the following: - - >>> grid = DiagramGrid(diagram) - >>> (grid.width, grid.height) - (2, 2) - >>> pprint(grid) - A B - <BLANKLINE> - C - - Sometimes one sees the diagram as consisting of logical groups. - One can advise ``DiagramGrid`` as to such groups by employing the - ``groups`` keyword argument. - - Consider the following diagram: - - >>> D = Object("D") - >>> f = NamedMorphism(A, B, "f") - >>> g = NamedMorphism(B, C, "g") - >>> h = NamedMorphism(D, A, "h") - >>> k = NamedMorphism(D, B, "k") - >>> diagram = Diagram([f, g, h, k]) - - Lay it out with generic layout: - - >>> grid = DiagramGrid(diagram) - >>> pprint(grid) - A B D - <BLANKLINE> - C - - Now, we can group the objects `A` and `D` to have them near one - another: - - >>> grid = DiagramGrid(diagram, groups=[[A, D], B, C]) - >>> pprint(grid) - B C - <BLANKLINE> - A D - - Note how the positioning of the other objects changes. - - Further indications can be supplied to the constructor of - :class:`DiagramGrid` using keyword arguments. The currently - supported hints are explained in the following paragraphs. - - :class:`DiagramGrid` does not automatically guess which layout - would suit the supplied diagram better. Consider, for example, - the following linear diagram: - - >>> E = Object("E") - >>> f = NamedMorphism(A, B, "f") - >>> g = NamedMorphism(B, C, "g") - >>> h = NamedMorphism(C, D, "h") - >>> i = NamedMorphism(D, E, "i") - >>> diagram = Diagram([f, g, h, i]) - - When laid out with the generic layout, it does not get to look - linear: - - >>> grid = DiagramGrid(diagram) - >>> pprint(grid) - A B - <BLANKLINE> - C D - <BLANKLINE> - E - - To get it laid out in a line, use ``layout="sequential"``: - - >>> grid = DiagramGrid(diagram, layout="sequential") - >>> pprint(grid) - A B C D E - - One may sometimes need to transpose the resulting layout. While - this can always be done by hand, :class:`DiagramGrid` provides a - hint for that purpose: - - >>> grid = DiagramGrid(diagram, layout="sequential", transpose=True) - >>> pprint(grid) - A - <BLANKLINE> - B - <BLANKLINE> - C - <BLANKLINE> - D - <BLANKLINE> - E - - Separate hints can also be provided for each group. For an - example, refer to ``tests/test_drawing.py``, and see the different - ways in which the five lemma [FiveLemma] can be laid out. - - See Also - ======== - - Diagram - - References - ========== - - [FiveLemma] http://en.wikipedia.org/wiki/Five_lemma - """ - @staticmethod - def _simplify_morphisms(morphisms): - """ - Given a dictionary mapping morphisms to their properties, - returns a new dictionary in which there are no morphisms which - do not have properties, and which are compositions of other - morphisms included in the dictionary. Identities are dropped - as well. - """ - newmorphisms = {} - for morphism, props in list(morphisms.items()): - if isinstance(morphism, CompositeMorphism) and not props: - continue - elif isinstance(morphism, IdentityMorphism): - continue - else: - newmorphisms[morphism] = props - return newmorphisms - - @staticmethod - def _merge_premises_conclusions(premises, conclusions): - """ - Given two dictionaries of morphisms and their properties, - produces a single dictionary which includes elements from both - dictionaries. If a morphism has some properties in premises - and also in conclusions, the properties in conclusions take - priority. - """ - return dict(chain(list(premises.items()), list(conclusions.items()))) - - @staticmethod - def _juxtapose_edges(edge1, edge2): - """ - If ``edge1`` and ``edge2`` have precisely one common endpoint, - returns an edge which would form a triangle with ``edge1`` and - ``edge2``. - - If ``edge1`` and ``edge2`` don't have a common endpoint, - returns ``None``. - - If ``edge1`` and ``edge`` are the same edge, returns ``None``. - """ - intersection = edge1 & edge2 - if len(intersection) != 1: - # The edges either have no common points or are equal. - return None - - # The edges have a common endpoint. Extract the different - # endpoints and set up the new edge. - return (edge1 - intersection) | (edge2 - intersection) - - @staticmethod - def _add_edge_append(dictionary, edge, elem): - """ - If ``edge`` is not in ``dictionary``, adds ``edge`` to the - dictionary and sets its value to ``[elem]``. Otherwise - appends ``elem`` to the value of existing entry. - - Note that edges are undirected, thus `(A, B) = (B, A)`. - """ - if edge in dictionary: - dictionary[edge].append(elem) - else: - dictionary[edge] = [elem] - - @staticmethod - def _build_skeleton(morphisms): - """ - Creates a dictionary which maps edges to corresponding - morphisms. Thus for a morphism `f:A\rightarrow B`, the edge - `(A, B)` will be associated with `f`. This function also adds - to the list those edges which are formed by juxtaposition of - two edges already in the list. These new edges are not - associated with any morphism and are only added to assure that - the diagram can be decomposed into triangles. - """ - edges = {} - # Create edges for morphisms. - for morphism in morphisms: - DiagramGrid._add_edge_append( - edges, frozenset([morphism.domain, morphism.codomain]), morphism) - - # Create new edges by juxtaposing existing edges. - edges1 = dict(edges) - for w in edges1: - for v in edges1: - wv = DiagramGrid._juxtapose_edges(w, v) - if wv and wv not in edges: - edges[wv] = [] - - return edges - - @staticmethod - def _list_triangles(edges): - """ - Builds the set of triangles formed by the supplied edges. The - triangles are arbitrary and need not be commutative. A - triangle is a set that contains all three of its sides. - """ - triangles = set() - - for w in edges: - for v in edges: - wv = DiagramGrid._juxtapose_edges(w, v) - if wv and wv in edges: - triangles.add(frozenset([w, v, wv])) - - return triangles - - @staticmethod - def _drop_redundant_triangles(triangles, skeleton): - """ - Returns a list which contains only those triangles who have - morphisms associated with at least two edges. - """ - return [tri for tri in triangles - if len([e for e in tri if skeleton[e]]) >= 2] - - @staticmethod - def _morphism_length(morphism): - """ - Returns the length of a morphism. The length of a morphism is - the number of components it consists of. A non-composite - morphism is of length 1. - """ - if isinstance(morphism, CompositeMorphism): - return len(morphism.components) - else: - return 1 - - @staticmethod - def _compute_triangle_min_sizes(triangles, edges): - r""" - Returns a dictionary mapping triangles to their minimal sizes. - The minimal size of a triangle is the sum of maximal lengths - of morphisms associated to the sides of the triangle. The - length of a morphism is the number of components it consists - of. A non-composite morphism is of length 1. - - Sorting triangles by this metric attempts to address two - aspects of layout. For triangles with only simple morphisms - in the edge, this assures that triangles with all three edges - visible will get typeset after triangles with less visible - edges, which sometimes minimises the necessity in diagonal - arrows. For triangles with composite morphisms in the edges, - this assures that objects connected with shorter morphisms - will be laid out first, resulting the visual proximity of - those objects which are connected by shorter morphisms. - """ - triangle_sizes = {} - for triangle in triangles: - size = 0 - for e in triangle: - morphisms = edges[e] - if morphisms: - size += max(DiagramGrid._morphism_length(m) - for m in morphisms) - triangle_sizes[triangle] = size - return triangle_sizes - - @staticmethod - def _triangle_objects(triangle): - """ - Given a triangle, returns the objects included in it. - """ - # A triangle is a frozenset of three two-element frozensets - # (the edges). This chains the three edges together and - # creates a frozenset from the iterator, thus producing a - # frozenset of objects of the triangle. - return frozenset(chain(*tuple(triangle))) - - @staticmethod - def _other_vertex(triangle, edge): - """ - Given a triangle and an edge of it, returns the vertex which - opposes the edge. - """ - # This gets the set of objects of the triangle and then - # subtracts the set of objects employed in ``edge`` to get the - # vertex opposite to ``edge``. - return list(DiagramGrid._triangle_objects(triangle) - set(edge))[0] - - @staticmethod - def _empty_point(pt, grid): - """ - Checks if the cell at coordinates ``pt`` is either empty or - out of the bounds of the grid. - """ - if (pt[0] < 0) or (pt[1] < 0) or \ - (pt[0] >= grid.height) or (pt[1] >= grid.width): - return True - return grid[pt] is None - - @staticmethod - def _put_object(coords, obj, grid, fringe): - """ - Places an object at the coordinate ``cords`` in ``grid``, - growing the grid and updating ``fringe``, if necessary. - Returns (0, 0) if no row or column has been prepended, (1, 0) - if a row was prepended, (0, 1) if a column was prepended and - (1, 1) if both a column and a row were prepended. - """ - (i, j) = coords - offset = (0, 0) - if i == -1: - grid.prepend_row() - i = 0 - offset = (1, 0) - for k in range(len(fringe)): - ((i1, j1), (i2, j2)) = fringe[k] - fringe[k] = ((i1 + 1, j1), (i2 + 1, j2)) - elif i == grid.height: - grid.append_row() - - if j == -1: - j = 0 - offset = (offset[0], 1) - grid.prepend_column() - for k in range(len(fringe)): - ((i1, j1), (i2, j2)) = fringe[k] - fringe[k] = ((i1, j1 + 1), (i2, j2 + 1)) - elif j == grid.width: - grid.append_column() - - grid[i, j] = obj - return offset - - @staticmethod - def _choose_target_cell(pt1, pt2, edge, obj, skeleton, grid): - """ - Given two points, ``pt1`` and ``pt2``, and the welding edge - ``edge``, chooses one of the two points to place the opposing - vertex ``obj`` of the triangle. If neither of this points - fits, returns ``None``. - """ - pt1_empty = DiagramGrid._empty_point(pt1, grid) - pt2_empty = DiagramGrid._empty_point(pt2, grid) - - if pt1_empty and pt2_empty: - # Both cells are empty. Of these two, choose that cell - # which will assure that a visible edge of the triangle - # will be drawn perpendicularly to the current welding - # edge. - - A = grid[edge[0]] - B = grid[edge[1]] - - if skeleton.get(frozenset([A, obj])): - return pt1 - else: - return pt2 - if pt1_empty: - return pt1 - elif pt2_empty: - return pt2 - else: - return None - - @staticmethod - def _find_triangle_to_weld(triangles, fringe, grid): - """ - Finds, if possible, a triangle and an edge in the fringe to - which the triangle could be attached. Returns the tuple - containing the triangle and the index of the corresponding - edge in the fringe. - - This function relies on the fact that objects are unique in - the diagram. - """ - for triangle in triangles: - for (a, b) in fringe: - if frozenset([grid[a], grid[b]]) in triangle: - return (triangle, (a, b)) - return None - - @staticmethod - def _weld_triangle(tri, welding_edge, fringe, grid, skeleton): - """ - If possible, welds the triangle ``tri`` to ``fringe`` and - returns ``False``. If this method encounters a degenerate - situation in the fringe and corrects it such that a restart of - the search is required, it returns ``True`` (which means that - a restart in finding triangle weldings is required). - - A degenerate situation is a situation when an edge listed in - the fringe does not belong to the visual boundary of the - diagram. - """ - a, b = welding_edge - target_cell = None - - obj = DiagramGrid._other_vertex(tri, (grid[a], grid[b])) - - # We now have a triangle and an edge where it can be welded to - # the fringe. Decide where to place the other vertex of the - # triangle and check for degenerate situations en route. - - if (abs(a[0] - b[0]) == 1) and (abs(a[1] - b[1]) == 1): - # A diagonal edge. - target_cell = (a[0], b[1]) - if grid[target_cell]: - # That cell is already occupied. - target_cell = (b[0], a[1]) - - if grid[target_cell]: - # Degenerate situation, this edge is not - # on the actual fringe. Correct the - # fringe and go on. - fringe.remove((a, b)) - return True - elif a[0] == b[0]: - # A horizontal edge. We first attempt to build the - # triangle in the downward direction. - - down_left = a[0] + 1, a[1] - down_right = a[0] + 1, b[1] - - target_cell = DiagramGrid._choose_target_cell( - down_left, down_right, (a, b), obj, skeleton, grid) - - if not target_cell: - # No room below this edge. Check above. - up_left = a[0] - 1, a[1] - up_right = a[0] - 1, b[1] - - target_cell = DiagramGrid._choose_target_cell( - up_left, up_right, (a, b), obj, skeleton, grid) - - if not target_cell: - # This edge is not in the fringe, remove it - # and restart. - fringe.remove((a, b)) - return True - elif a[1] == b[1]: - # A vertical edge. We will attempt to place the other - # vertex of the triangle to the right of this edge. - right_up = a[0], a[1] + 1 - right_down = b[0], a[1] + 1 - - target_cell = DiagramGrid._choose_target_cell( - right_up, right_down, (a, b), obj, skeleton, grid) - - if not target_cell: - # No room to the left. See what's to the right. - left_up = a[0], a[1] - 1 - left_down = b[0], a[1] - 1 - - target_cell = DiagramGrid._choose_target_cell( - left_up, left_down, (a, b), obj, skeleton, grid) - - if not target_cell: - # This edge is not in the fringe, remove it - # and restart. - fringe.remove((a, b)) - return True - - # We now know where to place the other vertex of the - # triangle. - offset = DiagramGrid._put_object(target_cell, obj, grid, fringe) - - # Take care of the displacement of coordinates if a row or - # a column was prepended. - target_cell = (target_cell[0] + offset[0], - target_cell[1] + offset[1]) - a = (a[0] + offset[0], a[1] + offset[1]) - b = (b[0] + offset[0], b[1] + offset[1]) - - fringe.extend([(a, target_cell), (b, target_cell)]) - - # No restart is required. - return False - - @staticmethod - def _triangle_key(tri, triangle_sizes): - """ - Returns a key for the supplied triangle. It should be the - same independently of the hash randomisation. - """ - objects = sorted( - DiagramGrid._triangle_objects(tri), key=default_sort_key) - return (triangle_sizes[tri], default_sort_key(objects)) - - @staticmethod - def _pick_root_edge(tri, skeleton): - """ - For a given triangle always picks the same root edge. The - root edge is the edge that will be placed first on the grid. - """ - candidates = [sorted(e, key=default_sort_key) - for e in tri if skeleton[e]] - sorted_candidates = sorted(candidates, key=default_sort_key) - # Don't forget to assure the proper ordering of the vertices - # in this edge. - return tuple(sorted(sorted_candidates[0], key=default_sort_key)) - - @staticmethod - def _drop_irrelevant_triangles(triangles, placed_objects): - """ - Returns only those triangles whose set of objects is not - completely included in ``placed_objects``. - """ - return [tri for tri in triangles if not placed_objects.issuperset( - DiagramGrid._triangle_objects(tri))] - - @staticmethod - def _grow_pseudopod(triangles, fringe, grid, skeleton, placed_objects): - """ - Starting from an object in the existing structure on the grid, - adds an edge to which a triangle from ``triangles`` could be - welded. If this method has found a way to do so, it returns - the object it has just added. - - This method should be applied when ``_weld_triangle`` cannot - find weldings any more. - """ - for i in range(grid.height): - for j in range(grid.width): - obj = grid[i, j] - if not obj: - continue - - # Here we need to choose a triangle which has only - # ``obj`` in common with the existing structure. The - # situations when this is not possible should be - # handled elsewhere. - - def good_triangle(tri): - objs = DiagramGrid._triangle_objects(tri) - return obj in objs and \ - placed_objects & (objs - set([obj])) == set() - - tris = [tri for tri in triangles if good_triangle(tri)] - if not tris: - # This object is not interesting. - continue - - # Pick the "simplest" of the triangles which could be - # attached. Remember that the list of triangles is - # sorted according to their "simplicity" (see - # _compute_triangle_min_sizes for the metric). - # - # Note that ``tris`` are sequentially built from - # ``triangles``, so we don't have to worry about hash - # randomisation. - tri = tris[0] - - # We have found a triangle which could be attached to - # the existing structure by a vertex. - - candidates = sorted([e for e in tri if skeleton[e]], - key=lambda e: FiniteSet(e).sort_key()) - edges = [e for e in candidates if obj in e] - - # Note that a meaningful edge (i.e., and edge that is - # associated with a morphism) containing ``obj`` - # always exists. That's because all triangles are - # guaranteed to have at least two meaningful edges. - # See _drop_redundant_triangles. - - # Get the object at the other end of the edge. - edge = edges[0] - other_obj = tuple(edge - frozenset([obj]))[0] - - # Now check for free directions. When checking for - # free directions, prefer the horizontal and vertical - # directions. - neighbours = [(i - 1, j), (i, j + 1), (i + 1, j), (i, j - 1), - (i - 1, j - 1), (i - 1, j + 1), (i + 1, j - 1), (i + 1, j + 1)] - - for pt in neighbours: - if DiagramGrid._empty_point(pt, grid): - # We have a found a place to grow the - # pseudopod into. - offset = DiagramGrid._put_object( - pt, other_obj, grid, fringe) - - i += offset[0] - j += offset[1] - pt = (pt[0] + offset[0], pt[1] + offset[1]) - fringe.append(((i, j), pt)) - - return other_obj - - # This diagram is actually cooler that I can handle. Fail cowardly. - return None - - @staticmethod - def _handle_groups(diagram, groups, merged_morphisms, hints): - """ - Given the slightly preprocessed morphisms of the diagram, - produces a grid laid out according to ``groups``. - - If a group has hints, it is laid out with those hints only, - without any influence from ``hints``. Otherwise, it is laid - out with ``hints``. - """ - def lay_out_group(group, local_hints): - """ - If ``group`` is a set of objects, uses a ``DiagramGrid`` - to lay it out and returns the grid. Otherwise returns the - object (i.e., ``group``). If ``local_hints`` is not - empty, it is supplied to ``DiagramGrid`` as the dictionary - of hints. Otherwise, the ``hints`` argument of - ``_handle_groups`` is used. - """ - if isinstance(group, FiniteSet): - # Set up the corresponding object-to-group - # mappings. - for obj in group: - obj_groups[obj] = group - - # Lay out the current group. - if local_hints: - groups_grids[group] = DiagramGrid( - diagram.subdiagram_from_objects(group), **local_hints) - else: - groups_grids[group] = DiagramGrid( - diagram.subdiagram_from_objects(group), **hints) - else: - obj_groups[group] = group - - def group_to_finiteset(group): - """ - Converts ``group`` to a :class:``FiniteSet`` if it is an - iterable. - """ - if iterable(group): - return FiniteSet(group) - else: - return group - - obj_groups = {} - groups_grids = {} - - # We would like to support various containers to represent - # groups. To achieve that, before laying each group out, it - # should be converted to a FiniteSet, because that is what the - # following code expects. - - if isinstance(groups, dict) or isinstance(groups, Dict): - finiteset_groups = {} - for group, local_hints in list(groups.items()): - finiteset_group = group_to_finiteset(group) - finiteset_groups[finiteset_group] = local_hints - lay_out_group(group, local_hints) - groups = finiteset_groups - else: - finiteset_groups = [] - for group in groups: - finiteset_group = group_to_finiteset(group) - finiteset_groups.append(finiteset_group) - lay_out_group(finiteset_group, None) - groups = finiteset_groups - - new_morphisms = [] - for morphism in merged_morphisms: - dom = obj_groups[morphism.domain] - cod = obj_groups[morphism.codomain] - # Note that we are not really interested in morphisms - # which do not employ two different groups, because - # these do not influence the layout. - if dom != cod: - # These are essentially unnamed morphisms; they are - # not going to mess in the final layout. By giving - # them the same names, we avoid unnecessary - # duplicates. - new_morphisms.append(NamedMorphism(dom, cod, "dummy")) - - # Lay out the new diagram. Since these are dummy morphisms, - # properties and conclusions are irrelevant. - top_grid = DiagramGrid(Diagram(new_morphisms)) - - # We now have to substitute the groups with the corresponding - # grids, laid out at the beginning of this function. Compute - # the size of each row and column in the grid, so that all - # nested grids fit. - - def group_size(group): - """ - For the supplied group (or object, eventually), returns - the size of the cell that will hold this group (object). - """ - if group in groups_grids: - grid = groups_grids[group] - return (grid.height, grid.width) - else: - return (1, 1) - - row_heights = [max(group_size(top_grid[i, j])[0] - for j in range(top_grid.width)) - for i in range(top_grid.height)] - - column_widths = [max(group_size(top_grid[i, j])[1] - for i in range(top_grid.height)) - for j in range(top_grid.width)] - - grid = _GrowableGrid(sum(column_widths), sum(row_heights)) - - real_row = 0 - real_column = 0 - for logical_row in range(top_grid.height): - for logical_column in range(top_grid.width): - obj = top_grid[logical_row, logical_column] - - if obj in groups_grids: - # This is a group. Copy the corresponding grid in - # place. - local_grid = groups_grids[obj] - for i in range(local_grid.height): - for j in range(local_grid.width): - grid[real_row + i, - real_column + j] = local_grid[i, j] - else: - # This is an object. Just put it there. - grid[real_row, real_column] = obj - - real_column += column_widths[logical_column] - real_column = 0 - real_row += row_heights[logical_row] - - return grid - - @staticmethod - def _generic_layout(diagram, merged_morphisms): - """ - Produces the generic layout for the supplied diagram. - """ - all_objects = set(diagram.objects) - if len(all_objects) == 1: - # There only one object in the diagram, just put in on 1x1 - # grid. - grid = _GrowableGrid(1, 1) - grid[0, 0] = tuple(all_objects)[0] - return grid - - skeleton = DiagramGrid._build_skeleton(merged_morphisms) - - grid = _GrowableGrid(2, 1) - - if len(skeleton) == 1: - # This diagram contains only one morphism. Draw it - # horizontally. - objects = sorted(all_objects, key=default_sort_key) - grid[0, 0] = objects[0] - grid[0, 1] = objects[1] - - return grid - - triangles = DiagramGrid._list_triangles(skeleton) - triangles = DiagramGrid._drop_redundant_triangles(triangles, skeleton) - triangle_sizes = DiagramGrid._compute_triangle_min_sizes( - triangles, skeleton) - - triangles = sorted(triangles, key=lambda tri: - DiagramGrid._triangle_key(tri, triangle_sizes)) - - # Place the first edge on the grid. - root_edge = DiagramGrid._pick_root_edge(triangles[0], skeleton) - grid[0, 0], grid[0, 1] = root_edge - fringe = [((0, 0), (0, 1))] - - # Record which objects we now have on the grid. - placed_objects = set(root_edge) - - while placed_objects != all_objects: - welding = DiagramGrid._find_triangle_to_weld( - triangles, fringe, grid) - - if welding: - (triangle, welding_edge) = welding - - restart_required = DiagramGrid._weld_triangle( - triangle, welding_edge, fringe, grid, skeleton) - if restart_required: - continue - - placed_objects.update( - DiagramGrid._triangle_objects(triangle)) - else: - # No more weldings found. Try to attach triangles by - # vertices. - new_obj = DiagramGrid._grow_pseudopod( - triangles, fringe, grid, skeleton, placed_objects) - - if not new_obj: - # No more triangles can be attached, not even by - # the edge. We will set up a new diagram out of - # what has been left, laid it out independently, - # and then attach it to this one. - - remaining_objects = all_objects - placed_objects - - remaining_diagram = diagram.subdiagram_from_objects( - FiniteSet(remaining_objects)) - remaining_grid = DiagramGrid(remaining_diagram) - - # Now, let's glue ``remaining_grid`` to ``grid``. - final_width = grid.width + remaining_grid.width - final_height = max(grid.height, remaining_grid.height) - final_grid = _GrowableGrid(final_width, final_height) - - for i in range(grid.width): - for j in range(grid.height): - final_grid[i, j] = grid[i, j] - - start_j = grid.width - for i in range(remaining_grid.height): - for j in range(remaining_grid.width): - final_grid[i, start_j + j] = remaining_grid[i, j] - - return final_grid - - placed_objects.add(new_obj) - - triangles = DiagramGrid._drop_irrelevant_triangles( - triangles, placed_objects) - - return grid - - @staticmethod - def _get_undirected_graph(objects, merged_morphisms): - """ - Given the objects and the relevant morphisms of a diagram, - returns the adjacency lists of the underlying undirected - graph. - """ - adjlists = {} - for obj in objects: - adjlists[obj] = [] - - for morphism in merged_morphisms: - adjlists[morphism.domain].append(morphism.codomain) - adjlists[morphism.codomain].append(morphism.domain) - - # Assure that the objects in the adjacency list are always in - # the same order. - for obj in list(adjlists.keys()): - adjlists[obj].sort(key=default_sort_key) - - return adjlists - - @staticmethod - def _sequential_layout(diagram, merged_morphisms): - r""" - Lays out the diagram in "sequential" layout. This method - will attempt to produce a result as close to a line as - possible. For linear diagrams, the result will actually be a - line. - """ - objects = diagram.objects - sorted_objects = sorted(objects, key=default_sort_key) - - # Set up the adjacency lists of the underlying undirected - # graph of ``merged_morphisms``. - adjlists = DiagramGrid._get_undirected_graph(objects, merged_morphisms) - - # Find an object with the minimal degree. This is going to be - # the root. - root = sorted_objects[0] - mindegree = len(adjlists[root]) - for obj in sorted_objects: - current_degree = len(adjlists[obj]) - if current_degree < mindegree: - root = obj - mindegree = current_degree - - grid = _GrowableGrid(1, 1) - grid[0, 0] = root - - placed_objects = set([root]) - - def place_objects(pt, placed_objects): - """ - Does depth-first search in the underlying graph of the - diagram and places the objects en route. - """ - # We will start placing new objects from here. - new_pt = (pt[0], pt[1] + 1) - - for adjacent_obj in adjlists[grid[pt]]: - if adjacent_obj in placed_objects: - # This object has already been placed. - continue - - DiagramGrid._put_object(new_pt, adjacent_obj, grid, []) - placed_objects.add(adjacent_obj) - placed_objects.update(place_objects(new_pt, placed_objects)) - - new_pt = (new_pt[0] + 1, new_pt[1]) - - return placed_objects - - place_objects((0, 0), placed_objects) - - return grid - - @staticmethod - def _drop_inessential_morphisms(merged_morphisms): - r""" - Removes those morphisms which should appear in the diagram, - but which have no relevance to object layout. - - Currently this removes "loop" morphisms: the non-identity - morphisms with the same domains and codomains. - """ - morphisms = [m for m in merged_morphisms if m.domain != m.codomain] - return morphisms - - @staticmethod - def _get_connected_components(objects, merged_morphisms): - """ - Given a container of morphisms, returns a list of connected - components formed by these morphisms. A connected component - is represented by a diagram consisting of the corresponding - morphisms. - """ - component_index = {} - for o in objects: - component_index[o] = None - - # Get the underlying undirected graph of the diagram. - adjlist = DiagramGrid._get_undirected_graph(objects, merged_morphisms) - - def traverse_component(object, current_index): - """ - Does a depth-first search traversal of the component - containing ``object``. - """ - component_index[object] = current_index - for o in adjlist[object]: - if component_index[o] is None: - traverse_component(o, current_index) - - # Traverse all components. - current_index = 0 - for o in adjlist: - if component_index[o] is None: - traverse_component(o, current_index) - current_index += 1 - - # List the objects of the components. - component_objects = [[] for i in range(current_index)] - for o, idx in list(component_index.items()): - component_objects[idx].append(o) - - # Finally, list the morphisms belonging to each component. - # - # Note: If some objects are isolated, they will not get any - # morphisms at this stage, and since the layout algorithm - # relies, we are essentially going to lose this object. - # Therefore, check if there are isolated objects and, for each - # of them, provide the trivial identity morphism. It will get - # discarded later, but the object will be there. - - component_morphisms = [] - for component in component_objects: - current_morphisms = {} - for m in merged_morphisms: - if (m.domain in component) and (m.codomain in component): - current_morphisms[m] = merged_morphisms[m] - - if len(component) == 1: - # Let's add an identity morphism, for the sake of - # surely having morphisms in this component. - current_morphisms[IdentityMorphism(component[0])] = FiniteSet() - - component_morphisms.append(Diagram(current_morphisms)) - - return component_morphisms - - def __init__(self, diagram, groups=None, **hints): - premises = DiagramGrid._simplify_morphisms(diagram.premises) - conclusions = DiagramGrid._simplify_morphisms(diagram.conclusions) - all_merged_morphisms = DiagramGrid._merge_premises_conclusions( - premises, conclusions) - merged_morphisms = DiagramGrid._drop_inessential_morphisms( - all_merged_morphisms) - - # Store the merged morphisms for later use. - self._morphisms = all_merged_morphisms - - components = DiagramGrid._get_connected_components( - diagram.objects, all_merged_morphisms) - - if groups and (groups != diagram.objects): - # Lay out the diagram according to the groups. - self._grid = DiagramGrid._handle_groups( - diagram, groups, merged_morphisms, hints) - elif len(components) > 1: - # Note that we check for connectedness _before_ checking - # the layout hints because the layout strategies don't - # know how to deal with disconnected diagrams. - - # The diagram is disconnected. Lay out the components - # independently. - grids = [] - - # Sort the components to eventually get the grids arranged - # in a fixed, hash-independent order. - components = sorted(components, key=default_sort_key) - - for component in components: - grid = DiagramGrid(component, **hints) - grids.append(grid) - - # Throw the grids together, in a line. - total_width = sum(g.width for g in grids) - total_height = max(g.height for g in grids) - - grid = _GrowableGrid(total_width, total_height) - start_j = 0 - for g in grids: - for i in range(g.height): - for j in range(g.width): - grid[i, start_j + j] = g[i, j] - - start_j += g.width - - self._grid = grid - elif "layout" in hints: - if hints["layout"] == "sequential": - self._grid = DiagramGrid._sequential_layout( - diagram, merged_morphisms) - else: - self._grid = DiagramGrid._generic_layout(diagram, merged_morphisms) - - if hints.get("transpose"): - # Transpose the resulting grid. - grid = _GrowableGrid(self._grid.height, self._grid.width) - for i in range(self._grid.height): - for j in range(self._grid.width): - grid[j, i] = self._grid[i, j] - self._grid = grid - - @property -
    [docs] def width(self): - """ - Returns the number of columns in this diagram layout. - - Examples - ======== - - >>> from sympy.categories import Object, NamedMorphism - >>> from sympy.categories import Diagram, DiagramGrid - >>> A = Object("A") - >>> B = Object("B") - >>> C = Object("C") - >>> f = NamedMorphism(A, B, "f") - >>> g = NamedMorphism(B, C, "g") - >>> diagram = Diagram([f, g]) - >>> grid = DiagramGrid(diagram) - >>> grid.width - 2 - - """ - return self._grid.width -
    - @property -
    [docs] def height(self): - """ - Returns the number of rows in this diagram layout. - - Examples - ======== - - >>> from sympy.categories import Object, NamedMorphism - >>> from sympy.categories import Diagram, DiagramGrid - >>> A = Object("A") - >>> B = Object("B") - >>> C = Object("C") - >>> f = NamedMorphism(A, B, "f") - >>> g = NamedMorphism(B, C, "g") - >>> diagram = Diagram([f, g]) - >>> grid = DiagramGrid(diagram) - >>> grid.height - 2 - - """ - return self._grid.height -
    - def __getitem__(self, xxx_todo_changeme2): - """ - Returns the object placed in the row ``i`` and column ``j``. - The indices are 0-based. - - Examples - ======== - - >>> from sympy.categories import Object, NamedMorphism - >>> from sympy.categories import Diagram, DiagramGrid - >>> A = Object("A") - >>> B = Object("B") - >>> C = Object("C") - >>> f = NamedMorphism(A, B, "f") - >>> g = NamedMorphism(B, C, "g") - >>> diagram = Diagram([f, g]) - >>> grid = DiagramGrid(diagram) - >>> (grid[0, 0], grid[0, 1]) - (Object("A"), Object("B")) - >>> (grid[1, 0], grid[1, 1]) - (None, Object("C")) - - """ - (i, j) = xxx_todo_changeme2 - return self._grid[i, j] - - @property -
    [docs] def morphisms(self): - """ - Returns those morphisms (and their properties) which are - sufficiently meaningful to be drawn. - - Examples - ======== - - >>> from sympy.categories import Object, NamedMorphism - >>> from sympy.categories import Diagram, DiagramGrid - >>> A = Object("A") - >>> B = Object("B") - >>> C = Object("C") - >>> f = NamedMorphism(A, B, "f") - >>> g = NamedMorphism(B, C, "g") - >>> diagram = Diagram([f, g]) - >>> grid = DiagramGrid(diagram) - >>> grid.morphisms - {NamedMorphism(Object("A"), Object("B"), "f"): EmptySet(), - NamedMorphism(Object("B"), Object("C"), "g"): EmptySet()} - - """ - return self._morphisms -
    - def __str__(self): - """ - Produces a string representation of this class. - - This method returns a string representation of the underlying - list of lists of objects. - - Examples - ======== - - >>> from sympy.categories import Object, NamedMorphism - >>> from sympy.categories import Diagram, DiagramGrid - >>> A = Object("A") - >>> B = Object("B") - >>> C = Object("C") - >>> f = NamedMorphism(A, B, "f") - >>> g = NamedMorphism(B, C, "g") - >>> diagram = Diagram([f, g]) - >>> grid = DiagramGrid(diagram) - >>> print(grid) - [[Object("A"), Object("B")], - [None, Object("C")]] - - """ - return repr(self._grid._array) - -
    -
    [docs]class ArrowStringDescription(object): - r""" - Stores the information necessary for producing an Xy-pic - description of an arrow. - - The principal goal of this class is to abstract away the string - representation of an arrow and to also provide the functionality - to produce the actual Xy-pic string. - - ``unit`` sets the unit which will be used to specify the amount of - curving and other distances. ``horizontal_direction`` should be a - string of ``"r"`` or ``"l"`` specifying the horizontal offset of the - target cell of the arrow relatively to the current one. - ``vertical_direction`` should specify the vertical offset using a - series of either ``"d"`` or ``"u"``. ``label_position`` should be - either ``"^"``, ``"_"``, or ``"|"`` to specify that the label should - be positioned above the arrow, below the arrow or just over the arrow, - in a break. Note that the notions "above" and "below" are relative - to arrow direction. ``label`` stores the morphism label. - - This works as follows (disregard the yet unexplained arguments): - - >>> from sympy.categories.diagram_drawing import ArrowStringDescription - >>> astr = ArrowStringDescription( - ... unit="mm", curving=None, curving_amount=None, - ... looping_start=None, looping_end=None, horizontal_direction="d", - ... vertical_direction="r", label_position="_", label="f") - >>> print(str(astr)) - \ar[dr]_{f} - - ``curving`` should be one of ``"^"``, ``"_"`` to specify in which - direction the arrow is going to curve. ``curving_amount`` is a number - describing how many ``unit``'s the morphism is going to curve: - - >>> astr = ArrowStringDescription( - ... unit="mm", curving="^", curving_amount=12, - ... looping_start=None, looping_end=None, horizontal_direction="d", - ... vertical_direction="r", label_position="_", label="f") - >>> print(str(astr)) - \ar@/^12mm/[dr]_{f} - - ``looping_start`` and ``looping_end`` are currently only used for - loop morphisms, those which have the same domain and codomain. - These two attributes should store a valid Xy-pic direction and - specify, correspondingly, the direction the arrow gets out into - and the direction the arrow gets back from: - - >>> astr = ArrowStringDescription( - ... unit="mm", curving=None, curving_amount=None, - ... looping_start="u", looping_end="l", horizontal_direction="", - ... vertical_direction="", label_position="_", label="f") - >>> print(str(astr)) - \ar@(u,l)[]_{f} - - ``label_displacement`` controls how far the arrow label is from - the ends of the arrow. For example, to position the arrow label - near the arrow head, use ">": - - >>> astr = ArrowStringDescription( - ... unit="mm", curving="^", curving_amount=12, - ... looping_start=None, looping_end=None, horizontal_direction="d", - ... vertical_direction="r", label_position="_", label="f") - >>> astr.label_displacement = ">" - >>> print(str(astr)) - \ar@/^12mm/[dr]_>{f} - - Finally, ``arrow_style`` is used to specify the arrow style. To - get a dashed arrow, for example, use "{-->}" as arrow style: - - >>> astr = ArrowStringDescription( - ... unit="mm", curving="^", curving_amount=12, - ... looping_start=None, looping_end=None, horizontal_direction="d", - ... vertical_direction="r", label_position="_", label="f") - >>> astr.arrow_style = "{-->}" - >>> print(str(astr)) - \ar@/^12mm/@{-->}[dr]_{f} - - Notes - ===== - - Instances of :class:`ArrowStringDescription` will be constructed - by :class:`XypicDiagramDrawer` and provided for further use in - formatters. The user is not expected to construct instances of - :class:`ArrowStringDescription` themselves. - - To be able to properly utilise this class, the reader is encouraged - to checkout the Xy-pic user guide, available at [Xypic]. - - See Also - ======== - - XypicDiagramDrawer - - References - ========== - - [Xypic] http://www.tug.org/applications/Xy-pic/ - """ - def __init__(self, unit, curving, curving_amount, looping_start, - looping_end, horizontal_direction, vertical_direction, - label_position, label): - self.unit = unit - self.curving = curving - self.curving_amount = curving_amount - self.looping_start = looping_start - self.looping_end = looping_end - self.horizontal_direction = horizontal_direction - self.vertical_direction = vertical_direction - self.label_position = label_position - self.label = label - - self.label_displacement = "" - self.arrow_style = "" - - # This flag shows that the position of the label of this - # morphism was set while typesetting a curved morphism and - # should not be modified later. - self.forced_label_position = False - - def __str__(self): - if self.curving: - curving_str = "@/%s%d%s/" % (self.curving, self.curving_amount, - self.unit) - else: - curving_str = "" - - if self.looping_start and self.looping_end: - looping_str = "@(%s,%s)" % (self.looping_start, self.looping_end) - else: - looping_str = "" - - if self.arrow_style: - - style_str = "@" + self.arrow_style - else: - style_str = "" - - return "\\ar%s%s%s[%s%s]%s%s{%s}" % \ - (curving_str, looping_str, style_str, self.horizontal_direction, - self.vertical_direction, self.label_position, - self.label_displacement, self.label) - -
    -
    [docs]class XypicDiagramDrawer(object): - r""" - Given a :class:`Diagram` and the corresponding - :class:`DiagramGrid`, produces the Xy-pic representation of the - diagram. - - The most important method in this class is ``draw``. Consider the - following triangle diagram: - - >>> from sympy.categories import Object, NamedMorphism, Diagram - >>> from sympy.categories import DiagramGrid, XypicDiagramDrawer - >>> A = Object("A") - >>> B = Object("B") - >>> C = Object("C") - >>> f = NamedMorphism(A, B, "f") - >>> g = NamedMorphism(B, C, "g") - >>> diagram = Diagram([f, g], {g * f: "unique"}) - - To draw this diagram, its objects need to be laid out with a - :class:`DiagramGrid`:: - - >>> grid = DiagramGrid(diagram) - - Finally, the drawing: - - >>> drawer = XypicDiagramDrawer() - >>> print(drawer.draw(diagram, grid)) - \xymatrix{ - A \ar[d]_{g\circ f} \ar[r]^{f} & B \ar[ld]^{g} \\ - C & - } - - For further details see the docstring of this method. - - To control the appearance of the arrows, formatters are used. The - dictionary ``arrow_formatters`` maps morphisms to formatter - functions. A formatter is accepts an - :class:`ArrowStringDescription` and is allowed to modify any of - the arrow properties exposed thereby. For example, to have all - morphisms with the property ``unique`` appear as dashed arrows, - and to have their names prepended with `\exists !`, the following - should be done: - - >>> def formatter(astr): - ... astr.label = "\exists !" + astr.label - ... astr.arrow_style = "{-->}" - >>> drawer.arrow_formatters["unique"] = formatter - >>> print(drawer.draw(diagram, grid)) - \xymatrix{ - A \ar@{-->}[d]_{\exists !g\circ f} \ar[r]^{f} & B \ar[ld]^{g} \\ - C & - } - - To modify the appearance of all arrows in the diagram, set - ``default_arrow_formatter``. For example, to place all morphism - labels a little bit farther from the arrow head so that they look - more centred, do as follows: - - >>> def default_formatter(astr): - ... astr.label_displacement = "(0.45)" - >>> drawer.default_arrow_formatter = default_formatter - >>> print(drawer.draw(diagram, grid)) - \xymatrix{ - A \ar@{-->}[d]_(0.45){\exists !g\circ f} \ar[r]^(0.45){f} & B \ar[ld]^(0.45){g} \\ - C & - } - - In some diagrams some morphisms are drawn as curved arrows. - Consider the following diagram: - - >>> D = Object("D") - >>> E = Object("E") - >>> h = NamedMorphism(D, A, "h") - >>> k = NamedMorphism(D, B, "k") - >>> diagram = Diagram([f, g, h, k]) - >>> grid = DiagramGrid(diagram) - >>> drawer = XypicDiagramDrawer() - >>> print(drawer.draw(diagram, grid)) - \xymatrix{ - A \ar[r]_{f} & B \ar[d]^{g} & D \ar[l]^{k} \ar@/_3mm/[ll]_{h} \\ - & C & - } - - To control how far the morphisms are curved by default, one can - use the ``unit`` and ``default_curving_amount`` attributes: - - >>> drawer.unit = "cm" - >>> drawer.default_curving_amount = 1 - >>> print(drawer.draw(diagram, grid)) - \xymatrix{ - A \ar[r]_{f} & B \ar[d]^{g} & D \ar[l]^{k} \ar@/_1cm/[ll]_{h} \\ - & C & - } - - In some diagrams, there are multiple curved morphisms between the - same two objects. To control by how much the curving changes - between two such successive morphisms, use - ``default_curving_step``: - - >>> drawer.default_curving_step = 1 - >>> h1 = NamedMorphism(A, D, "h1") - >>> diagram = Diagram([f, g, h, k, h1]) - >>> grid = DiagramGrid(diagram) - >>> print(drawer.draw(diagram, grid)) - \xymatrix{ - A \ar[r]_{f} \ar@/^1cm/[rr]^{h_{1}} & B \ar[d]^{g} & D \ar[l]^{k} \ar@/_2cm/[ll]_{h} \\ - & C & - } - - The default value of ``default_curving_step`` is 4 units. - - See Also - ======== - - draw, ArrowStringDescription - """ - def __init__(self): - self.unit = "mm" - self.default_curving_amount = 3 - self.default_curving_step = 4 - - # This dictionary maps properties to the corresponding arrow - # formatters. - self.arrow_formatters = {} - - # This is the default arrow formatter which will be applied to - # each arrow independently of its properties. - self.default_arrow_formatter = None - - @staticmethod - def _process_loop_morphism(i, j, grid, morphisms_str_info, object_coords): - """ - Produces the information required for constructing the string - representation of a loop morphism. This function is invoked - from ``_process_morphism``. - - See Also - ======== - - _process_morphism - """ - curving = "" - label_pos = "^" - looping_start = "" - looping_end = "" - - # This is a loop morphism. Count how many morphisms stick - # in each of the four quadrants. Note that straight - # vertical and horizontal morphisms count in two quadrants - # at the same time (i.e., a morphism going up counts both - # in the first and the second quadrants). - - # The usual numbering (counterclockwise) of quadrants - # applies. - quadrant = [0, 0, 0, 0] - - obj = grid[i, j] - - for m, m_str_info in list(morphisms_str_info.items()): - if (m.domain == obj) and (m.codomain == obj): - # That's another loop morphism. Check how it - # loops and mark the corresponding quadrants as - # busy. - (l_s, l_e) = (m_str_info.looping_start, m_str_info.looping_end) - - if (l_s, l_e) == ("r", "u"): - quadrant[0] += 1 - elif (l_s, l_e) == ("u", "l"): - quadrant[1] += 1 - elif (l_s, l_e) == ("l", "d"): - quadrant[2] += 1 - elif (l_s, l_e) == ("d", "r"): - quadrant[3] += 1 - - continue - if m.domain == obj: - (end_i, end_j) = object_coords[m.codomain] - goes_out = True - elif m.codomain == obj: - (end_i, end_j) = object_coords[m.domain] - goes_out = False - else: - continue - - d_i = end_i - i - d_j = end_j - j - m_curving = m_str_info.curving - - if (d_i != 0) and (d_j != 0): - # This is really a diagonal morphism. Detect the - # quadrant. - if (d_i > 0) and (d_j > 0): - quadrant[0] += 1 - elif (d_i > 0) and (d_j < 0): - quadrant[1] += 1 - elif (d_i < 0) and (d_j < 0): - quadrant[2] += 1 - elif (d_i < 0) and (d_j > 0): - quadrant[3] += 1 - elif d_i == 0: - # Knowing where the other end of the morphism is - # and which way it goes, we now have to decide - # which quadrant is now the upper one and which is - # the lower one. - if d_j > 0: - if goes_out: - upper_quadrant = 0 - lower_quadrant = 3 - else: - upper_quadrant = 3 - lower_quadrant = 0 - else: - if goes_out: - upper_quadrant = 2 - lower_quadrant = 1 - else: - upper_quadrant = 1 - lower_quadrant = 2 - - if m_curving: - if m_curving == "^": - quadrant[upper_quadrant] += 1 - elif m_curving == "_": - quadrant[lower_quadrant] += 1 - else: - # This morphism counts in both upper and lower - # quadrants. - quadrant[upper_quadrant] += 1 - quadrant[lower_quadrant] += 1 - elif d_j == 0: - # Knowing where the other end of the morphism is - # and which way it goes, we now have to decide - # which quadrant is now the left one and which is - # the right one. - if d_i < 0: - if goes_out: - left_quadrant = 1 - right_quadrant = 0 - else: - left_quadrant = 0 - right_quadrant = 1 - else: - if goes_out: - left_quadrant = 3 - right_quadrant = 2 - else: - left_quadrant = 2 - right_quadrant = 3 - - if m_curving: - if m_curving == "^": - quadrant[left_quadrant] += 1 - elif m_curving == "_": - quadrant[right_quadrant] += 1 - else: - # This morphism counts in both upper and lower - # quadrants. - quadrant[left_quadrant] += 1 - quadrant[right_quadrant] += 1 - - # Pick the freest quadrant to curve our morphism into. - freest_quadrant = 0 - for i in range(4): - if quadrant[i] < quadrant[freest_quadrant]: - freest_quadrant = i - - # Now set up proper looping. - (looping_start, looping_end) = [("r", "u"), ("u", "l"), ("l", "d"), - ("d", "r")][freest_quadrant] - - return (curving, label_pos, looping_start, looping_end) - - @staticmethod - def _process_horizontal_morphism(i, j, target_j, grid, morphisms_str_info, - object_coords): - """ - Produces the information required for constructing the string - representation of a horizontal morphism. This function is - invoked from ``_process_morphism``. - - See Also - ======== - - _process_morphism - """ - # The arrow is horizontal. Check if it goes from left to - # right (``backwards == False``) or from right to left - # (``backwards == True``). - backwards = False - start = j - end = target_j - if end < start: - (start, end) = (end, start) - backwards = True - - # Let's see which objects are there between ``start`` and - # ``end``, and then count how many morphisms stick out - # upwards, and how many stick out downwards. - # - # For example, consider the situation: - # - # B1 C1 - # | | - # A--B--C--D - # | - # B2 - # - # Between the objects `A` and `D` there are two objects: - # `B` and `C`. Further, there are two morphisms which - # stick out upward (the ones between `B1` and `B` and - # between `C` and `C1`) and one morphism which sticks out - # downward (the one between `B and `B2`). - # - # We need this information to decide how to curve the - # arrow between `A` and `D`. First of all, since there - # are two objects between `A` and `D``, we must curve the - # arrow. Then, we will have it curve downward, because - # there is more space (less morphisms stick out downward - # than upward). - up = [] - down = [] - straight_horizontal = [] - for k in range(start + 1, end): - obj = grid[i, k] - if not obj: - continue - - for m in morphisms_str_info: - if m.domain == obj: - (end_i, end_j) = object_coords[m.codomain] - elif m.codomain == obj: - (end_i, end_j) = object_coords[m.domain] - else: - continue - - if end_i > i: - down.append(m) - elif end_i < i: - up.append(m) - elif not morphisms_str_info[m].curving: - # This is a straight horizontal morphism, - # because it has no curving. - straight_horizontal.append(m) - - if len(up) < len(down): - # More morphisms stick out downward than upward, let's - # curve the morphism up. - if backwards: - curving = "_" - label_pos = "_" - else: - curving = "^" - label_pos = "^" - - # Assure that the straight horizontal morphisms have - # their labels on the lower side of the arrow. - for m in straight_horizontal: - (i1, j1) = object_coords[m.domain] - (i2, j2) = object_coords[m.codomain] - - m_str_info = morphisms_str_info[m] - if j1 < j2: - m_str_info.label_position = "_" - else: - m_str_info.label_position = "^" - - # Don't allow any further modifications of the - # position of this label. - m_str_info.forced_label_position = True - else: - # More morphisms stick out downward than upward, let's - # curve the morphism up. - if backwards: - curving = "^" - label_pos = "^" - else: - curving = "_" - label_pos = "_" - - # Assure that the straight horizontal morphisms have - # their labels on the upper side of the arrow. - for m in straight_horizontal: - (i1, j1) = object_coords[m.domain] - (i2, j2) = object_coords[m.codomain] - - m_str_info = morphisms_str_info[m] - if j1 < j2: - m_str_info.label_position = "^" - else: - m_str_info.label_position = "_" - - # Don't allow any further modifications of the - # position of this label. - m_str_info.forced_label_position = True - - return (curving, label_pos) - - @staticmethod - def _process_vertical_morphism(i, j, target_i, grid, morphisms_str_info, - object_coords): - """ - Produces the information required for constructing the string - representation of a vertical morphism. This function is - invoked from ``_process_morphism``. - - See Also - ======== - - _process_morphism - """ - # This arrow is vertical. Check if it goes from top to - # bottom (``backwards == False``) or from bottom to top - # (``backwards == True``). - backwards = False - start = i - end = target_i - if end < start: - (start, end) = (end, start) - backwards = True - - # Let's see which objects are there between ``start`` and - # ``end``, and then count how many morphisms stick out to - # the left, and how many stick out to the right. - # - # See the corresponding comment in the previous branch of - # this if-statement for more details. - left = [] - right = [] - straight_vertical = [] - for k in range(start + 1, end): - obj = grid[k, j] - if not obj: - continue - - for m in morphisms_str_info: - if m.domain == obj: - (end_i, end_j) = object_coords[m.codomain] - elif m.codomain == obj: - (end_i, end_j) = object_coords[m.domain] - else: - continue - - if end_j > j: - right.append(m) - elif end_j < j: - left.append(m) - elif not morphisms_str_info[m].curving: - # This is a straight vertical morphism, - # because it has no curving. - straight_vertical.append(m) - - if len(left) < len(right): - # More morphisms stick out to the left than to the - # right, let's curve the morphism to the right. - if backwards: - curving = "^" - label_pos = "^" - else: - curving = "_" - label_pos = "_" - - # Assure that the straight vertical morphisms have - # their labels on the left side of the arrow. - for m in straight_vertical: - (i1, j1) = object_coords[m.domain] - (i2, j2) = object_coords[m.codomain] - - m_str_info = morphisms_str_info[m] - if i1 < i2: - m_str_info.label_position = "^" - else: - m_str_info.label_position = "_" - - # Don't allow any further modifications of the - # position of this label. - m_str_info.forced_label_position = True - else: - # More morphisms stick out to the right than to the - # left, let's curve the morphism to the left. - if backwards: - curving = "_" - label_pos = "_" - else: - curving = "^" - label_pos = "^" - - # Assure that the straight vertical morphisms have - # their labels on the right side of the arrow. - for m in straight_vertical: - (i1, j1) = object_coords[m.domain] - (i2, j2) = object_coords[m.codomain] - - m_str_info = morphisms_str_info[m] - if i1 < i2: - m_str_info.label_position = "_" - else: - m_str_info.label_position = "^" - - # Don't allow any further modifications of the - # position of this label. - m_str_info.forced_label_position = True - - return (curving, label_pos) - - def _process_morphism(self, diagram, grid, morphism, object_coords, - morphisms, morphisms_str_info): - """ - Given the required information, produces the string - representation of ``morphism``. - """ - def repeat_string_cond(times, str_gt, str_lt): - """ - If ``times > 0``, repeats ``str_gt`` ``times`` times. - Otherwise, repeats ``str_lt`` ``-times`` times. - """ - if times > 0: - return str_gt * times - else: - return str_lt * (-times) - - def count_morphisms_undirected(A, B): - """ - Counts how many processed morphisms there are between the - two supplied objects. - """ - return len([m for m in morphisms_str_info - if set([m.domain, m.codomain]) == set([A, B])]) - - def count_morphisms_filtered(dom, cod, curving): - """ - Counts the processed morphisms which go out of ``dom`` - into ``cod`` with curving ``curving``. - """ - return len([m for m, m_str_info in list(morphisms_str_info.items()) - if (m.domain, m.codomain) == (dom, cod) and - (m_str_info.curving == curving)]) - - (i, j) = object_coords[morphism.domain] - (target_i, target_j) = object_coords[morphism.codomain] - - # We now need to determine the direction of - # the arrow. - delta_i = target_i - i - delta_j = target_j - j - vertical_direction = repeat_string_cond(delta_i, - "d", "u") - horizontal_direction = repeat_string_cond(delta_j, - "r", "l") - - curving = "" - label_pos = "^" - looping_start = "" - looping_end = "" - - if (delta_i == 0) and (delta_j == 0): - # This is a loop morphism. - (curving, label_pos, looping_start, - looping_end) = XypicDiagramDrawer._process_loop_morphism( - i, j, grid, morphisms_str_info, object_coords) - elif (delta_i == 0) and (abs(j - target_j) > 1): - # This is a horizontal morphism. - (curving, label_pos) = XypicDiagramDrawer._process_horizontal_morphism( - i, j, target_j, grid, morphisms_str_info, object_coords) - elif (delta_j == 0) and (abs(i - target_i) > 1): - # This is a vertical morphism. - (curving, label_pos) = XypicDiagramDrawer._process_vertical_morphism( - i, j, target_i, grid, morphisms_str_info, object_coords) - - count = count_morphisms_undirected(morphism.domain, morphism.codomain) - curving_amount = "" - if curving: - # This morphisms should be curved anyway. - curving_amount = self.default_curving_amount + count * \ - self.default_curving_step - elif count: - # There are no objects between the domain and codomain of - # the current morphism, but this is not there already are - # some morphisms with the same domain and codomain, so we - # have to curve this one. - curving = "^" - filtered_morphisms = count_morphisms_filtered( - morphism.domain, morphism.codomain, curving) - curving_amount = self.default_curving_amount + \ - filtered_morphisms * \ - self.default_curving_step - - # Let's now get the name of the morphism. - morphism_name = "" - if isinstance(morphism, IdentityMorphism): - morphism_name = "id_{%s}" + latex(obj) - elif isinstance(morphism, CompositeMorphism): - component_names = [latex(Symbol(component.name)) for - component in morphism.components] - component_names.reverse() - morphism_name = "\\circ ".join(component_names) - elif isinstance(morphism, NamedMorphism): - morphism_name = latex(Symbol(morphism.name)) - - return ArrowStringDescription( - self.unit, curving, curving_amount, looping_start, - looping_end, horizontal_direction, vertical_direction, - label_pos, morphism_name) - - @staticmethod - def _check_free_space_horizontal(dom_i, dom_j, cod_j, grid): - """ - For a horizontal morphism, checks whether there is free space - (i.e., space not occupied by any objects) above the morphism - or below it. - """ - if dom_j < cod_j: - (start, end) = (dom_j, cod_j) - backwards = False - else: - (start, end) = (cod_j, dom_j) - backwards = True - - # Check for free space above. - if dom_i == 0: - free_up = True - else: - free_up = all([grid[dom_i - 1, j] for j in - range(start, end + 1)]) - - # Check for free space below. - if dom_i == grid.height - 1: - free_down = True - else: - free_down = all([not grid[dom_i + 1, j] for j in - range(start, end + 1)]) - - return (free_up, free_down, backwards) - - @staticmethod - def _check_free_space_vertical(dom_i, cod_i, dom_j, grid): - """ - For a vertical morphism, checks whether there is free space - (i.e., space not occupied by any objects) to the left of the - morphism or to the right of it. - """ - if dom_i < cod_i: - (start, end) = (dom_i, cod_i) - backwards = False - else: - (start, end) = (cod_i, dom_i) - backwards = True - - # Check if there's space to the left. - if dom_j == 0: - free_left = True - else: - free_left = all([not grid[i, dom_j - 1] for i in - range(start, end + 1)]) - - if dom_j == grid.width - 1: - free_right = True - else: - free_right = all([not grid[i, dom_j + 1] for i in - range(start, end + 1)]) - - return (free_left, free_right, backwards) - - @staticmethod - def _check_free_space_diagonal(dom_i, cod_i, dom_j, cod_j, grid): - """ - For a diagonal morphism, checks whether there is free space - (i.e., space not occupied by any objects) above the morphism - or below it. - """ - def abs_xrange(start, end): - if start < end: - return range(start, end + 1) - else: - return range(end, start + 1) - - if dom_i < cod_i and dom_j < cod_j: - # This morphism goes from top-left to - # bottom-right. - (start_i, start_j) = (dom_i, dom_j) - (end_i, end_j) = (cod_i, cod_j) - backwards = False - elif dom_i > cod_i and dom_j > cod_j: - # This morphism goes from bottom-right to - # top-left. - (start_i, start_j) = (cod_i, cod_j) - (end_i, end_j) = (dom_i, dom_j) - backwards = True - if dom_i < cod_i and dom_j > cod_j: - # This morphism goes from top-right to - # bottom-left. - (start_i, start_j) = (dom_i, dom_j) - (end_i, end_j) = (cod_i, cod_j) - backwards = True - elif dom_i > cod_i and dom_j < cod_j: - # This morphism goes from bottom-left to - # top-right. - (start_i, start_j) = (cod_i, cod_j) - (end_i, end_j) = (dom_i, dom_j) - backwards = False - - # This is an attempt at a fast and furious strategy to - # decide where there is free space on the two sides of - # a diagonal morphism. For a diagonal morphism - # starting at ``(start_i, start_j)`` and ending at - # ``(end_i, end_j)`` the rectangle defined by these - # two points is considered. The slope of the diagonal - # ``alpha`` is then computed. Then, for every cell - # ``(i, j)`` within the rectangle, the slope - # ``alpha1`` of the line through ``(start_i, - # start_j)`` and ``(i, j)`` is considered. If - # ``alpha1`` is between 0 and ``alpha``, the point - # ``(i, j)`` is above the diagonal, if ``alpha1`` is - # between ``alpha`` and infinity, the point is below - # the diagonal. Also note that, with some beforehand - # precautions, this trick works for both the main and - # the secondary diagonals of the rectangle. - - # I have considered the possibility to only follow the - # shorter diagonals immediately above and below the - # main (or secondary) diagonal. This, however, - # wouldn't have resulted in much performance gain or - # better detection of outer edges, because of - # relatively small sizes of diagram grids, while the - # code would have become harder to understand. - - alpha = float(end_i - start_i)/(end_j - start_j) - free_up = True - free_down = True - for i in abs_xrange(start_i, end_i): - if not free_up and not free_down: - break - - for j in abs_xrange(start_j, end_j): - if not free_up and not free_down: - break - - if (i, j) == (start_i, start_j): - continue - - if j == start_j: - alpha1 = "inf" - else: - alpha1 = float(i - start_i)/(j - start_j) - - if grid[i, j]: - if (alpha1 == "inf") or (abs(alpha1) > abs(alpha)): - free_down = False - elif abs(alpha1) < abs(alpha): - free_up = False - - return (free_up, free_down, backwards) - - def _push_labels_out(self, morphisms_str_info, grid, object_coords): - """ - For all straight morphisms which form the visual boundary of - the laid out diagram, puts their labels on their outer sides. - """ - def set_label_position(free1, free2, pos1, pos2, backwards, m_str_info): - """ - Given the information about room available to one side and - to the other side of a morphism (``free1`` and ``free2``), - sets the position of the morphism label in such a way that - it is on the freer side. This latter operations involves - choice between ``pos1`` and ``pos2``, taking ``backwards`` - in consideration. - - Thus this function will do nothing if either both ``free1 - == True`` and ``free2 == True`` or both ``free1 == False`` - and ``free2 == False``. In either case, choosing one side - over the other presents no advantage. - """ - if backwards: - (pos1, pos2) = (pos2, pos1) - - if free1 and not free2: - m_str_info.label_position = pos1 - elif free2 and not free1: - m_str_info.label_position = pos2 - - for m, m_str_info in list(morphisms_str_info.items()): - if m_str_info.curving or m_str_info.forced_label_position: - # This is either a curved morphism, and curved - # morphisms have other magic, or the position of this - # label has already been fixed. - continue - - if m.domain == m.codomain: - # This is a loop morphism, their labels, again have a - # different magic. - continue - - (dom_i, dom_j) = object_coords[m.domain] - (cod_i, cod_j) = object_coords[m.codomain] - - if dom_i == cod_i: - # Horizontal morphism. - (free_up, free_down, - backwards) = XypicDiagramDrawer._check_free_space_horizontal( - dom_i, dom_j, cod_j, grid) - - set_label_position(free_up, free_down, "^", "_", - backwards, m_str_info) - elif dom_j == cod_j: - # Vertical morphism. - (free_left, free_right, - backwards) = XypicDiagramDrawer._check_free_space_vertical( - dom_i, cod_i, dom_j, grid) - - set_label_position(free_left, free_right, "_", "^", - backwards, m_str_info) - else: - # A diagonal morphism. - (free_up, free_down, - backwards) = XypicDiagramDrawer._check_free_space_diagonal( - dom_i, cod_i, dom_j, cod_j, grid) - - set_label_position(free_up, free_down, "^", "_", - backwards, m_str_info) - - @staticmethod - def _morphism_sort_key(morphism, object_coords): - """ - Provides a morphism sorting key such that horizontal or - vertical morphisms between neighbouring objects come - first, then horizontal or vertical morphisms between more - far away objects, and finally, all other morphisms. - """ - (i, j) = object_coords[morphism.domain] - (target_i, target_j) = object_coords[morphism.codomain] - - if morphism.domain == morphism.codomain: - # Loop morphisms should get after diagonal morphisms - # so that the proper direction in which to curve the - # loop can be determined. - return (3, 0, default_sort_key(morphism)) - - if target_i == i: - return (1, abs(target_j - j), default_sort_key(morphism)) - - if target_j == j: - return (1, abs(target_i - i), default_sort_key(morphism)) - - # Diagonal morphism. - return (2, 0, default_sort_key(morphism)) - - @staticmethod - def _build_xypic_string(diagram, grid, morphisms, - morphisms_str_info, diagram_format): - """ - Given a collection of :class:`ArrowStringDescription` - describing the morphisms of a diagram and the object layout - information of a diagram, produces the final Xy-pic picture. - """ - # Build the mapping between objects and morphisms which have - # them as domains. - object_morphisms = {} - for obj in diagram.objects: - object_morphisms[obj] = [] - for morphism in morphisms: - object_morphisms[morphism.domain].append(morphism) - - result = "\\xymatrix%s{\n" % diagram_format - - for i in range(grid.height): - for j in range(grid.width): - obj = grid[i, j] - if obj: - result += latex(obj) + " " - - morphisms_to_draw = object_morphisms[obj] - for morphism in morphisms_to_draw: - result += str(morphisms_str_info[morphism]) + " " - - # Don't put the & after the last column. - if j < grid.width - 1: - result += "& " - - # Don't put the line break after the last row. - if i < grid.height - 1: - result += "\\\\" - result += "\n" - - result += "}\n" - - return result - -
    [docs] def draw(self, diagram, grid, masked=None, diagram_format=""): - r""" - Returns the Xy-pic representation of ``diagram`` laid out in - ``grid``. - - Consider the following simple triangle diagram. - - >>> from sympy.categories import Object, NamedMorphism, Diagram - >>> from sympy.categories import DiagramGrid, XypicDiagramDrawer - >>> A = Object("A") - >>> B = Object("B") - >>> C = Object("C") - >>> f = NamedMorphism(A, B, "f") - >>> g = NamedMorphism(B, C, "g") - >>> diagram = Diagram([f, g], {g * f: "unique"}) - - To draw this diagram, its objects need to be laid out with a - :class:`DiagramGrid`:: - - >>> grid = DiagramGrid(diagram) - - Finally, the drawing: - - >>> drawer = XypicDiagramDrawer() - >>> print(drawer.draw(diagram, grid)) - \xymatrix{ - A \ar[d]_{g\circ f} \ar[r]^{f} & B \ar[ld]^{g} \\ - C & - } - - The argument ``masked`` can be used to skip morphisms in the - presentation of the diagram: - - >>> print(drawer.draw(diagram, grid, masked=[g * f])) - \xymatrix{ - A \ar[r]^{f} & B \ar[ld]^{g} \\ - C & - } - - Finally, the ``diagram_format`` argument can be used to - specify the format string of the diagram. For example, to - increase the spacing by 1 cm, proceeding as follows: - - >>> print(drawer.draw(diagram, grid, diagram_format="@+1cm")) - \xymatrix@+1cm{ - A \ar[d]_{g\circ f} \ar[r]^{f} & B \ar[ld]^{g} \\ - C & - } - - """ - # This method works in several steps. It starts by removing - # the masked morphisms, if necessary, and then maps objects to - # their positions in the grid (coordinate tuples). Remember - # that objects are unique in ``Diagram`` and in the layout - # produced by ``DiagramGrid``, so every object is mapped to a - # single coordinate pair. - # - # The next step is the central step and is concerned with - # analysing the morphisms of the diagram and deciding how to - # draw them. For example, how to curve the arrows is decided - # at this step. The bulk of the analysis is implemented in - # ``_process_morphism``, to the result of which the - # appropriate formatters are applied. - # - # The result of the previous step is a list of - # ``ArrowStringDescription``. After the analysis and - # application of formatters, some extra logic tries to assure - # better positioning of morphism labels (for example, an - # attempt is made to avoid the situations when arrows cross - # labels). This functionality constitutes the next step and - # is implemented in ``_push_labels_out``. Note that label - # positions which have been set via a formatter are not - # affected in this step. - # - # Finally, at the closing step, the array of - # ``ArrowStringDescription`` and the layout information - # incorporated in ``DiagramGrid`` are combined to produce the - # resulting Xy-pic picture. This part of code lies in - # ``_build_xypic_string``. - - if not masked: - morphisms_props = grid.morphisms - else: - morphisms_props = {} - for m, props in list(grid.morphisms.items()): - if m in masked: - continue - morphisms_props[m] = props - - # Build the mapping between objects and their position in the - # grid. - object_coords = {} - for i in range(grid.height): - for j in range(grid.width): - if grid[i, j]: - object_coords[grid[i, j]] = (i, j) - - morphisms = sorted(morphisms_props, - key=lambda m: XypicDiagramDrawer._morphism_sort_key( - m, object_coords)) - - # Build the tuples defining the string representations of - # morphisms. - morphisms_str_info = {} - for morphism in morphisms: - string_description = self._process_morphism( - diagram, grid, morphism, object_coords, morphisms, - morphisms_str_info) - - if self.default_arrow_formatter: - self.default_arrow_formatter(string_description) - - for prop in morphisms_props[morphism]: - # prop is a Symbol. TODO: Find out why. - if prop.name in self.arrow_formatters: - formatter = self.arrow_formatters[prop.name] - formatter(string_description) - - morphisms_str_info[morphism] = string_description - - # Reposition the labels a bit. - self._push_labels_out(morphisms_str_info, grid, object_coords) - - return XypicDiagramDrawer._build_xypic_string( - diagram, grid, morphisms, morphisms_str_info, diagram_format) - -
    -
    [docs]def xypic_draw_diagram(diagram, masked=None, diagram_format="", - groups=None, **hints): - r""" - Provides a shortcut combining :class:`DiagramGrid` and - :class:`XypicDiagramDrawer`. Returns an Xy-pic presentation of - ``diagram``. The argument ``masked`` is a list of morphisms which - will be not be drawn. The argument ``diagram_format`` is the - format string inserted after "\xymatrix". ``groups`` should be a - set of logical groups. The ``hints`` will be passed directly to - the constructor of :class:`DiagramGrid`. - - For more information about the arguments, see the docstrings of - :class:`DiagramGrid` and ``XypicDiagramDrawer.draw``. - - Examples - ======== - - >>> from sympy.categories import Object, NamedMorphism, Diagram - >>> from sympy.categories import xypic_draw_diagram - >>> A = Object("A") - >>> B = Object("B") - >>> C = Object("C") - >>> f = NamedMorphism(A, B, "f") - >>> g = NamedMorphism(B, C, "g") - >>> diagram = Diagram([f, g], {g * f: "unique"}) - >>> print(xypic_draw_diagram(diagram)) - \xymatrix{ - A \ar[d]_{g\circ f} \ar[r]^{f} & B \ar[ld]^{g} \\ - C & - } - - See Also - ======== - - XypicDiagramDrawer, DiagramGrid - """ - grid = DiagramGrid(diagram, groups, **hints) - drawer = XypicDiagramDrawer() - return drawer.draw(diagram, grid, masked, diagram_format) - -
    -
    [docs]def preview_diagram(diagram, masked=None, diagram_format="", groups=None, - output='png', viewer=None, euler=True, **hints): - """ - Combines the functionality of ``xypic_draw_diagram`` and - ``sympy.printing.preview``. The arguments ``masked``, - ``diagram_format``, ``groups``, and ``hints`` are passed to - ``xypic_draw_diagram``, while ``output``, ``viewer, and ``euler`` - are passed to ``preview``. - - Examples - ======== - - >>> from sympy.categories import Object, NamedMorphism, Diagram - >>> from sympy.categories import preview_diagram - >>> A = Object("A") - >>> B = Object("B") - >>> C = Object("C") - >>> f = NamedMorphism(A, B, "f") - >>> g = NamedMorphism(B, C, "g") - >>> d = Diagram([f, g], {g * f: "unique"}) - >>> preview_diagram(d) #doctest: +SKIP - - See Also - ======== - - xypic_diagram_drawer - """ - from sympy.printing import preview - latex_output = xypic_draw_diagram(diagram, masked, diagram_format, - groups, **hints) - preview(latex_output, output, viewer, euler, ("xypic",))
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/combinatorics.html b/dev-py3k/_modules/sympy/combinatorics.html deleted file mode 100644 index c001d24d1e7..00000000000 --- a/dev-py3k/_modules/sympy/combinatorics.html +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - - - - - sympy.combinatorics — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.combinatorics

    -from sympy.combinatorics.permutations import Permutation, Cycle
    -from sympy.combinatorics.prufer import Prufer
    -from sympy.combinatorics.generators import cyclic, alternating, symmetric, dihedral
    -from sympy.combinatorics.subsets import Subset
    -from sympy.combinatorics.partitions import (Partition, IntegerPartition,
    -    RGS_rank, RGS_unrank, RGS_enum)
    -from sympy.combinatorics.polyhedron import (Polyhedron, tetrahedron, cube,
    -    octahedron, dodecahedron, icosahedron)
    -from sympy.combinatorics.perm_groups import PermutationGroup
    -from sympy.combinatorics.group_constructs import DirectProduct
    -from sympy.combinatorics.graycode import GrayCode
    -from sympy.combinatorics.named_groups import (SymmetricGroup, DihedralGroup,
    -    CyclicGroup, AlternatingGroup, AbelianGroup, RubikGroup)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/combinatorics/graycode.html b/dev-py3k/_modules/sympy/combinatorics/graycode.html deleted file mode 100644 index 8d903d57e24..00000000000 --- a/dev-py3k/_modules/sympy/combinatorics/graycode.html +++ /dev/null @@ -1,531 +0,0 @@ - - - - - - - - - - sympy.combinatorics.graycode — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.combinatorics.graycode

    -from sympy.core import Basic
    -
    -from sympy.core.compatibility import bin
    -
    -import random
    -
    -
    -
    [docs]class GrayCode(Basic): - """ - A Gray code is essentially a Hamiltonian walk on - a n-dimensional cube with edge length of one. - The vertices of the cube are represented by vectors - whose values are binary. The Hamilton walk visits - each vertex exactly once. The Gray code for a 3d - cube is ['000','100','110','010','011','111','101', - '001']. - - A Gray code solves the problem of sequentially - generating all possible subsets of n objects in such - a way that each subset is obtained from the previous - one by either deleting or adding a single object. - In the above example, 1 indicates that the object is - present, and 0 indicates that its absent. - - Gray codes have applications in statistics as well when - we want to compute various statistics related to subsets - in an efficient manner. - - References: - [1] Nijenhuis,A. and Wilf,H.S.(1978). - Combinatorial Algorithms. Academic Press. - [2] Knuth, D. (2011). The Art of Computer Programming, Vol 4 - Addison Wesley - - Examples - ======== - - >>> from sympy.combinatorics.graycode import GrayCode - >>> a = GrayCode(3) - >>> list(a.generate_gray()) - ['000', '001', '011', '010', '110', '111', '101', '100'] - >>> a = GrayCode(4) - >>> list(a.generate_gray()) - ['0000', '0001', '0011', '0010', '0110', '0111', '0101', '0100', \ - '1100', '1101', '1111', '1110', '1010', '1011', '1001', '1000'] - """ - - _skip = False - _current = 0 - _rank = None - - def __new__(cls, n, *args, **kw_args): - """ - Default constructor. - - It takes a single argument ``n`` which gives the dimension of the Gray - code. The starting Gray code string (``start``) or the starting ``rank`` - may also be given; the default is to start at rank = 0 ('0...0'). - - Examples - ======== - - >>> from sympy.combinatorics.graycode import GrayCode - >>> a = GrayCode(3) - >>> a - GrayCode(3) - >>> a.n - 3 - - >>> a = GrayCode(3, start='100') - >>> a.current - '100' - - >>> a = GrayCode(4, rank=4) - >>> a.current - '0110' - >>> a.rank - 4 - - """ - if n < 1 or int(n) != n: - raise ValueError( - 'Gray code dimension must be a positive integer, not %i' % n) - n = int(n) - args = (n,) + args - obj = Basic.__new__(cls, *args) - if 'start' in kw_args: - obj._current = kw_args["start"] - if len(obj._current) > n: - raise ValueError('Gray code start has length %i but ' - 'should not be greater than %i' % (len(obj._current), n)) - elif 'rank' in kw_args: - if int(kw_args["rank"]) != kw_args["rank"]: - raise ValueError('Gray code rank must be a positive integer, ' - 'not %i' % kw_args["rank"]) - obj._rank = int(kw_args["rank"]) % obj.selections - obj._current = obj.unrank(n, obj._rank) - return obj - -
    [docs] def next(self, delta=1): - """ - Returns the Gray code a distance ``delta`` (default = 1) from the - current value in canonical order. - - - Examples - ======== - - >>> from sympy.combinatorics.graycode import GrayCode - >>> a = GrayCode(3, start='110') - >>> a.next().current - '111' - >>> a.next(-1).current - '010' - """ - return GrayCode(self.n, rank=(self.rank + delta) % self.selections) -
    - @property -
    [docs] def selections(self): - """ - Returns the number of bit vectors in the Gray code. - - Examples - ======== - - >>> from sympy.combinatorics.graycode import GrayCode - >>> a = GrayCode(3) - >>> a.selections - 8 - """ - return 2**self.n -
    - @property -
    [docs] def n(self): - """ - Returns the dimension of the Gray code. - - Examples - ======== - - >>> from sympy.combinatorics.graycode import GrayCode - >>> a = GrayCode(5) - >>> a.n - 5 - """ - return self.args[0] -
    -
    [docs] def generate_gray(self, **hints): - """ - Generates the sequence of bit vectors of a Gray Code. - - [1] Knuth, D. (2011). The Art of Computer Programming, - Vol 4, Addison Wesley - - Examples - ======== - - >>> from sympy.combinatorics.graycode import GrayCode - >>> a = GrayCode(3) - >>> list(a.generate_gray()) - ['000', '001', '011', '010', '110', '111', '101', '100'] - >>> list(a.generate_gray(start='011')) - ['011', '010', '110', '111', '101', '100'] - >>> list(a.generate_gray(rank=4)) - ['110', '111', '101', '100'] - - See Also - ======== - skip - """ - bits = self.n - start = None - if "start" in hints: - start = hints["start"] - elif "rank" in hints: - start = GrayCode.unrank(self.n, hints["rank"]) - if start is not None: - self._current = start - current = self.current - graycode_bin = gray_to_bin(current) - if len(graycode_bin) > self.n: - raise ValueError('Gray code start has length %i but should ' - 'not be greater than %i' % (len(graycode_bin), bits)) - self._current = int(current, 2) - graycode_int = int(''.join(graycode_bin), 2) - for i in range(graycode_int, 1 << bits): - if self._skip: - self._skip = False - else: - yield self.current - bbtc = (i ^ (i + 1)) - gbtc = (bbtc ^ (bbtc >> 1)) - self._current = (self._current ^ gbtc) - self._current = 0 -
    -
    [docs] def skip(self): - """ - Skips the bit generation. - - Examples - ======== - - >>> from sympy.combinatorics.graycode import GrayCode - >>> a = GrayCode(3) - >>> for i in a.generate_gray(): - ... if i == '010': - ... a.skip() - ... print(i) - ... - 000 - 001 - 011 - 010 - 111 - 101 - 100 - - See Also - ======== - generate_gray - """ - self._skip = True -
    - @property -
    [docs] def rank(self): - """ - Ranks the Gray code. - - A ranking algorithm determines the position (or rank) - of a combinatorial object among all the objects w.r.t. - a given order. For example, the 4 bit binary reflected - Gray code (BRGC) '0101' has a rank of 6 as it appears in - the 6th position in the canonical ordering of the family - of 4 bit Gray codes. - - References: - [1] http://www-stat.stanford.edu/~susan/courses/s208/node12.html - - Examples - ======== - - >>> from sympy.combinatorics.graycode import GrayCode - >>> a = GrayCode(3) - >>> list(a.generate_gray()) - ['000', '001', '011', '010', '110', '111', '101', '100'] - >>> GrayCode(3, start='100').rank - 7 - >>> GrayCode(3, rank=7).current - '100' - - See Also - ======== - unrank - """ - if self._rank is None: - self._rank = int(gray_to_bin(self.current), 2) - return self._rank -
    - @property -
    [docs] def current(self): - """ - Returns the currently referenced Gray code as a bit string. - - Examples - ======== - >>> from sympy.combinatorics.graycode import GrayCode - >>> GrayCode(3, start='100').current - '100' - """ - rv = self._current or '0' - if type(rv) is not str: - rv = bin(rv)[2:] - return rv.rjust(self.n, '0') -
    - @classmethod -
    [docs] def unrank(self, n, rank): - """ - Unranks an n-bit sized Gray code of rank k. This method exists - so that a derivative GrayCode class can define its own code of - a given rank. - - The string here is generated in reverse order to allow for tail-call - optimization. - - Examples - ======== - - >>> from sympy.combinatorics.graycode import GrayCode - >>> GrayCode(5, rank=3).current - '00010' - >>> GrayCode.unrank(5, 3) - '00010' - - See Also - ======== - rank - """ - def _unrank(k, n): - if n == 1: - return str(k % 2) - m = 2**(n - 1) - if k < m: - return '0' + _unrank(k, n - 1) - return '1' + _unrank(m - (k % m) - 1, n - 1) - return _unrank(rank, n) - -
    -def random_bitstring(n): - """ - Generates a random bitlist of length n. - - Examples - ======== - - >>> from sympy.combinatorics.graycode import random_bitstring - >>> random_bitstring(3) # doctest: +SKIP - 100 - """ - return ''.join([random.choice('01') for i in range(n)]) - - -def gray_to_bin(bin_list): - """ - Convert from Gray coding to binary coding. - - We assume big endian encoding. - - Examples - ======== - - >>> from sympy.combinatorics.graycode import gray_to_bin - >>> gray_to_bin('100') - '111' - - See Also - ======== - bin_to_gray - """ - b = [bin_list[0]] - for i in range(1, len(bin_list)): - b += str(int(b[i - 1] != bin_list[i])) - return ''.join(b) - - -def bin_to_gray(bin_list): - """ - Convert from binary coding to gray coding. - - We assume big endian encoding. - - Examples - ======== - - >>> from sympy.combinatorics.graycode import bin_to_gray - >>> bin_to_gray('111') - '100' - - See Also - ======== - gray_to_bin - """ - b = [bin_list[0]] - for i in range(0, len(bin_list) - 1): - b += str(int(bin_list[i]) ^ int(b[i - 1])) - return ''.join(b) - - -def get_subset_from_bitstring(super_set, bitstring): - """ - Gets the subset defined by the bitstring. - - Examples - ======== - - >>> from sympy.combinatorics.graycode import get_subset_from_bitstring - >>> get_subset_from_bitstring(['a','b','c','d'], '0011') - ['c', 'd'] - >>> get_subset_from_bitstring(['c','a','c','c'], '1100') - ['c', 'a'] - - See Also - ======== - graycode_subsets - """ - if len(super_set) != len(bitstring): - raise ValueError("The sizes of the lists are not equal") - return [super_set[i] for i, j in enumerate(bitstring) - if bitstring[i] == '1'] - - -def graycode_subsets(gray_code_set): - """ - Generates the subsets as enumerated by a Gray code. - - Examples - ======== - - >>> from sympy.combinatorics.graycode import graycode_subsets - >>> list(graycode_subsets(['a','b','c'])) - [[], ['c'], ['b', 'c'], ['b'], ['a', 'b'], ['a', 'b', 'c'], \ - ['a', 'c'], ['a']] - >>> list(graycode_subsets(['a','b','c','c'])) - [[], ['c'], ['c', 'c'], ['c'], ['b', 'c'], ['b', 'c', 'c'], \ - ['b', 'c'], ['b'], ['a', 'b'], ['a', 'b', 'c'], ['a', 'b', 'c', 'c'], \ - ['a', 'b', 'c'], ['a', 'c'], ['a', 'c', 'c'], ['a', 'c'], ['a']] - - See Also - ======== - get_subset_from_bitstring - """ - for bitstring in list(GrayCode(len(gray_code_set)).generate_gray()): - yield get_subset_from_bitstring(gray_code_set, bitstring) -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/combinatorics/group_constructs.html b/dev-py3k/_modules/sympy/combinatorics/group_constructs.html deleted file mode 100644 index 9870bff0f29..00000000000 --- a/dev-py3k/_modules/sympy/combinatorics/group_constructs.html +++ /dev/null @@ -1,176 +0,0 @@ - - - - - - - - - - sympy.combinatorics.group_constructs — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.combinatorics.group_constructs

    -from sympy.combinatorics.perm_groups import PermutationGroup
    -from sympy.combinatorics.permutations import Permutation
    -from sympy.utilities.iterables import uniq
    -
    -_af_new = Permutation._af_new
    -
    -
    -
    [docs]def DirectProduct(*groups): - """ - Returns the direct product of several groups as a permutation group. - - This is implemented much like the __mul__ procedure for taking the direct - product of two permutation groups, but the idea of shifting the - generators is realized in the case of an arbitrary number of groups. - A call to DirectProduct(G1, G2, ..., Gn) is generally expected to be faster - than a call to G1*G2*...*Gn (and thus the need for this algorithm). - - Examples - ======== - - >>> from sympy.combinatorics.group_constructs import DirectProduct - >>> from sympy.combinatorics.named_groups import CyclicGroup - >>> C = CyclicGroup(4) - >>> G = DirectProduct(C,C,C) - >>> G.order() - 64 - - See Also - ======== - __mul__ - - """ - degrees = [] - gens_count = [] - total_degree = 0 - total_gens = 0 - for group in groups: - current_deg = group.degree - current_num_gens = len(group.generators) - degrees.append(current_deg) - total_degree += current_deg - gens_count.append(current_num_gens) - total_gens += current_num_gens - array_gens = [] - for i in range(total_gens): - array_gens.append(list(range(total_degree))) - current_gen = 0 - current_deg = 0 - for i in range(len(gens_count)): - for j in range(current_gen, current_gen + gens_count[i]): - gen = ((groups[i].generators)[j - current_gen]).array_form - array_gens[j][current_deg:current_deg + degrees[i]] = \ - [ x + current_deg for x in gen] - current_gen += gens_count[i] - current_deg += degrees[i] - perm_gens = list(uniq([_af_new(list(a)) for a in array_gens])) - return PermutationGroup(perm_gens, dups=False)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/combinatorics/named_groups.html b/dev-py3k/_modules/sympy/combinatorics/named_groups.html deleted file mode 100644 index 9433742239a..00000000000 --- a/dev-py3k/_modules/sympy/combinatorics/named_groups.html +++ /dev/null @@ -1,398 +0,0 @@ - - - - - - - - - - sympy.combinatorics.named_groups — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.combinatorics.named_groups

    -from sympy.combinatorics.perm_groups import PermutationGroup
    -from sympy.combinatorics.group_constructs import DirectProduct
    -from sympy.combinatorics.permutations import Permutation
    -
    -_af_new = Permutation._af_new
    -
    -
    -
    [docs]def AbelianGroup(*cyclic_orders): - """ - Returns the direct product of cyclic groups with the given orders. - - According to the structure theorem for finite abelian groups ([1]), - every finite abelian group can be written as the direct product of finitely - many cyclic groups. - [1] http://groupprops.subwiki.org/wiki/Structure_theorem_for_finitely_generated_abelian_groups - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation.print_cyclic = True - >>> from sympy.combinatorics.named_groups import AbelianGroup - >>> AbelianGroup(3, 4) - PermutationGroup([ - Permutation(6)(0, 1, 2), - Permutation(3, 4, 5, 6)]) - >>> _.is_group() - False - - See Also - ======== - DirectProduct - """ - groups = [] - degree = 0 - order = 1 - for size in cyclic_orders: - degree += size - order *= size - groups.append(CyclicGroup(size)) - G = DirectProduct(*groups) - G._is_abelian = True - G._degree = degree - G._order = order - - return G - -
    -
    [docs]def AlternatingGroup(n): - """ - Generates the alternating group on ``n`` elements as a permutation group. - - For ``n > 2``, the generators taken are ``(0 1 2), (0 1 2 ... n-1)`` for - ``n`` odd - and ``(0 1 2), (1 2 ... n-1)`` for ``n`` even (See [1], p.31, ex.6.9.). - After the group is generated, some of its basic properties are set. - The cases ``n = 1, 2`` are handled separately. - - Examples - ======== - - >>> from sympy.combinatorics.named_groups import AlternatingGroup - >>> G = AlternatingGroup(4) - >>> G.is_group() - False - >>> a = list(G.generate_dimino()) - >>> len(a) - 12 - >>> all(perm.is_even for perm in a) - True - - See Also - ======== - - SymmetricGroup, CyclicGroup, DihedralGroup - - References - ========== - - [1] Armstrong, M. "Groups and Symmetry" - - """ - # small cases are special - if n in (1, 2): - return PermutationGroup([Permutation([0])]) - - a = list(range(n)) - a[0], a[1], a[2] = a[1], a[2], a[0] - gen1 = a - if n % 2: - a = list(range(1, n)) - a.append(0) - gen2 = a - else: - a = list(range(2, n)) - a.append(1) - a.insert(0, 0) - gen2 = a - gens = [gen1, gen2] - if gen1 == gen2: - gens = gens[:1] - G = PermutationGroup([_af_new(a) for a in gens], dups=False) - - if n < 4: - G._is_abelian = True - else: - G._is_abelian = False - G._degree = n - G._is_transitive = True - G._is_alt = True - return G - -
    -
    [docs]def CyclicGroup(n): - """ - Generates the cyclic group of order ``n`` as a permutation group. - - The generator taken is the ``n``-cycle ``(0 1 2 ... n-1)`` - (in cycle notation). After the group is generated, some of its basic - properties are set. - - Examples - ======== - - >>> from sympy.combinatorics.named_groups import CyclicGroup - >>> G = CyclicGroup(6) - >>> G.is_group() - False - >>> G.order() - 6 - >>> list(G.generate_schreier_sims(af=True)) - [[0, 1, 2, 3, 4, 5], [1, 2, 3, 4, 5, 0], [2, 3, 4, 5, 0, 1], - [3, 4, 5, 0, 1, 2], [4, 5, 0, 1, 2, 3], [5, 0, 1, 2, 3, 4]] - - See Also - ======== - - SymmetricGroup, DihedralGroup, AlternatingGroup - - """ - a = list(range(1, n)) - a.append(0) - gen = _af_new(a) - G = PermutationGroup([gen]) - - G._is_abelian = True - G._degree = n - G._is_transitive = True - G._order = n - return G - -
    -
    [docs]def DihedralGroup(n): - r""" - Generates the dihedral group `D_n` as a permutation group. - - The dihedral group `D_n` is the group of symmetries of the regular - ``n``-gon. The generators taken are the ``n``-cycle ``a = (0 1 2 ... n-1)`` - (a rotation of the ``n``-gon) and ``b = (0 n-1)(1 n-2)...`` - (a reflection of the ``n``-gon) in cycle rotation. It is easy to see that - these satisfy ``a**n = b**2 = 1`` and ``bab = ~a`` so they indeed generate - `D_n` (See [1]). After the group is generated, some of its basic properties - are set. - - Examples - ======== - - >>> from sympy.combinatorics.named_groups import DihedralGroup - >>> G = DihedralGroup(5) - >>> G.is_group() - False - >>> a = list(G.generate_dimino()) - >>> [perm.cyclic_form for perm in a] - [[], [[0, 1, 2, 3, 4]], [[0, 2, 4, 1, 3]], - [[0, 3, 1, 4, 2]], [[0, 4, 3, 2, 1]], [[0, 4], [1, 3]], - [[1, 4], [2, 3]], [[0, 1], [2, 4]], [[0, 2], [3, 4]], - [[0, 3], [1, 2]]] - - See Also - ======== - - SymmetricGroup, CyclicGroup, AlternatingGroup - - References - ========== - - [1] http://en.wikipedia.org/wiki/Dihedral_group - - """ - # small cases are special - if n == 1: - return PermutationGroup([Permutation([1, 0])]) - if n == 2: - return PermutationGroup([Permutation([1, 0, 3, 2]), - Permutation([2, 3, 0, 1]), Permutation([3, 2, 1, 0])]) - - a = list(range(1, n)) - a.append(0) - gen1 = _af_new(a) - a = list(range(n)) - a.reverse() - gen2 = _af_new(a) - G = PermutationGroup([gen1, gen2]) - - G._is_abelian = False - G._degree = n - G._is_transitive = True - G._order = 2*n - return G - -
    -
    [docs]def SymmetricGroup(n): - """ - Generates the symmetric group on ``n`` elements as a permutation group. - - The generators taken are the ``n``-cycle - ``(0 1 2 ... n-1)`` and the transposition ``(0 1)`` (in cycle notation). - (See [1]). After the group is generated, some of its basic properties - are set. - - Examples - ======== - - >>> from sympy.combinatorics.named_groups import SymmetricGroup - >>> G = SymmetricGroup(4) - >>> G.is_group() - False - >>> G.order() - 24 - >>> list(G.generate_schreier_sims(af=True)) - [[0, 1, 2, 3], [1, 2, 3, 0], [2, 3, 0, 1], [3, 1, 2, 0], [0, 2, 3, 1], - [1, 3, 0, 2], [2, 0, 1, 3], [3, 2, 0, 1], [0, 3, 1, 2], [1, 0, 2, 3], - [2, 1, 3, 0], [3, 0, 1, 2], [0, 1, 3, 2], [1, 2, 0, 3], [2, 3, 1, 0], - [3, 1, 0, 2], [0, 2, 1, 3], [1, 3, 2, 0], [2, 0, 3, 1], [3, 2, 1, 0], - [0, 3, 2, 1], [1, 0, 3, 2], [2, 1, 0, 3], [3, 0, 2, 1]] - - See Also - ======== - - CyclicGroup, DihedralGroup, AlternatingGroup - - References - ========== - - [1] http://en.wikipedia.org/wiki/Symmetric_group#Generators_and_relations - - """ - if n == 1: - G = PermutationGroup([Permutation([0])]) - elif n == 2: - G = PermutationGroup([Permutation([1, 0])]) - else: - a = list(range(1, n)) - a.append(0) - gen1 = _af_new(a) - a = list(range(n)) - a[0], a[1] = a[1], a[0] - gen2 = _af_new(a) - G = PermutationGroup([gen1, gen2]) - - if n < 3: - G._is_abelian = True - else: - G._is_abelian = False - G._degree = n - G._is_transitive = True - G._is_sym = True - return G - -
    -def RubikGroup(n): - """Return a group of Rubik's cube generators. - >>> from sympy.combinatorics.named_groups import RubikGroup - >>> RubikGroup(2).is_group() - False - """ - from sympy.combinatorics.generators import rubik - assert n > 1 - return PermutationGroup(rubik(n)) -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/combinatorics/partitions.html b/dev-py3k/_modules/sympy/combinatorics/partitions.html deleted file mode 100644 index 01dc50efba7..00000000000 --- a/dev-py3k/_modules/sympy/combinatorics/partitions.html +++ /dev/null @@ -1,814 +0,0 @@ - - - - - - - - - - sympy.combinatorics.partitions — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.combinatorics.partitions

    -from sympy.core import Basic, C, Dict, sympify
    -from sympy.core.compatibility import as_int, default_sort_key
    -from sympy.functions.combinatorial.numbers import bell
    -from sympy.matrices import zeros
    -from sympy.utilities.iterables import has_dups, flatten, group
    -
    -from collections import defaultdict
    -
    -
    -
    [docs]class Partition(C.FiniteSet): - """ - This class represents an abstract partition. - - A partition is a set of disjoint sets whose union equals a given set. - - See Also - ======== - - sympy.utilities.iterables.partitions, - sympy.utilities.iterables.multiset_partitions - """ - - _rank = None - _partition = None - - def __new__(cls, partition): - """ - Generates a new partition object. - - This method also verifies if the arguments passed are - valid and raises a ValueError if they are not. - - Examples - ======== - - >>> from sympy.combinatorics.partitions import Partition - >>> a = Partition([[1, 2], [3]]) - >>> a - {{1, 2}, {3}} - >>> a.partition - [[1, 2], [3]] - >>> len(a) - 2 - >>> a.members - (1, 2, 3) - - """ - args = partition - if not all(isinstance(part, list) for part in args): - raise ValueError("Partition should be a list of lists.") - - # sort so we have a canonical reference for RGS - partition = sorted(sum(partition, []), key=default_sort_key) - if has_dups(partition): - raise ValueError("Partition contained duplicated elements.") - - obj = C.FiniteSet.__new__(cls, list(map(C.FiniteSet, args))) - obj.members = tuple(partition) - obj.size = len(partition) - return obj - -
    [docs] def sort_key(self, order=None): - """Return a canonical key that can be used for sorting. - - Ordering is based on the size and sorted elements of the partition - and ties are broken with the rank. - - Examples - ======== - - >>> from sympy.utilities.iterables import default_sort_key - >>> from sympy.combinatorics.partitions import Partition - >>> from sympy.abc import x - >>> a = Partition([[1, 2]]) - >>> b = Partition([[3, 4]]) - >>> c = Partition([[1, x]]) - >>> d = Partition([list(range(4))]) - >>> l = [d, b, a + 1, a, c] - >>> l.sort(key=default_sort_key); l - [{{1, 2}}, {{1}, {2}}, {{1, x}}, {{3, 4}}, {{0, 1, 2, 3}}] - """ - if order is None: - members = self.members - else: - members = tuple(sorted(self.members, - key=lambda w: default_sort_key(w, order))) - return self.size, members, self.rank -
    - @property -
    [docs] def partition(self): - """Return partition as a sorted list of lists. - - Examples - ======== - - >>> from sympy.combinatorics.partitions import Partition - >>> Partition([[1], [2, 3]]).partition - [[1], [2, 3]] - """ - if self._partition is None: - self._partition = sorted(sorted(p) for p in self.args) - return self._partition -
    - def __add__(self, other): - """ - Return permutation whose rank is ``other`` greater than current rank, - (mod the maximum rank for the set). - - Examples - ======== - - >>> from sympy.combinatorics.partitions import Partition - >>> a = Partition([[1, 2], [3]]) - >>> a.rank - 1 - >>> (a + 1).rank - 2 - >>> (a + 100).rank - 1 - """ - other = as_int(other) - offset = self.rank + other - result = RGS_unrank((offset) % - RGS_enum(self.size), - self.size) - return Partition.from_rgs(result, self.members) - - def __sub__(self, other): - """ - Return permutation whose rank is ``other`` less than current rank, - (mod the maximum rank for the set). - - Examples - ======== - - >>> from sympy.combinatorics.partitions import Partition - >>> a = Partition([[1, 2], [3]]) - >>> a.rank - 1 - >>> (a - 1).rank - 0 - >>> (a - 100).rank - 1 - """ - return self.__add__(-other) - - def __le__(self, other): - """ - Checks if a partition is less than or equal to - the other based on rank. - - Examples - ======== - - >>> from sympy.combinatorics.partitions import Partition - >>> a = Partition([[1, 2], [3, 4, 5]]) - >>> b = Partition([[1], [2, 3], [4], [5]]) - >>> a.rank, b.rank - (9, 34) - >>> a <= a - True - >>> a <= b - True - """ - return self.sort_key() <= sympify(other).sort_key() - - def __lt__(self, other): - """ - Checks if a partition is less than the other. - - Examples - ======== - - >>> from sympy.combinatorics.partitions import Partition - >>> a = Partition([[1, 2], [3, 4, 5]]) - >>> b = Partition([[1], [2, 3], [4], [5]]) - >>> a.rank, b.rank - (9, 34) - >>> a < b - True - """ - return self.sort_key() < sympify(other).sort_key() - - @property -
    [docs] def rank(self): - """ - Gets the rank of a partition. - - Examples - ======== - - >>> from sympy.combinatorics.partitions import Partition - >>> a = Partition([[1, 2], [3], [4, 5]]) - >>> a.rank - 13 - """ - if self._rank is not None: - return self._rank - self._rank = RGS_rank(self.RGS) - return self._rank -
    - @property -
    [docs] def RGS(self): - """ - Returns the "restricted growth string" of the partition. - - The RGS is returned as a list of indices, L, where L[i] indicates - the block in which element i appears. For example, in a partition - of 3 elements (a, b, c) into 2 blocks ([c], [a, b]) the RGS is - [1, 1, 0]: "a" is in block 1, "b" is in block 1 and "c" is in block 0. - - Examples - ======== - - >>> from sympy.combinatorics.partitions import Partition - >>> a = Partition([[1, 2], [3], [4, 5]]) - >>> a.members - (1, 2, 3, 4, 5) - >>> a.RGS - (0, 0, 1, 2, 2) - >>> a + 1 - {{1, 2}, {3}, {4}, {5}} - >>> _.RGS - (0, 0, 1, 2, 3) - """ - rgs = {} - partition = self.partition - for i, part in enumerate(partition): - for j in part: - rgs[j] = i - return tuple([rgs[i] for i in sorted(i for p in partition for i in p)]) -
    - @classmethod -
    [docs] def from_rgs(self, rgs, elements): - """ - Creates a set partition from a restricted growth string. - - The indices given in rgs are assumed to be the index - of the element as given in elements *as provided* (the - elements are not sorted by this routine). Block numbering - starts from 0. If any block was not referenced in ``rgs`` - an error will be raised. - - Examples - ======== - - >>> from sympy.combinatorics.partitions import Partition - >>> Partition.from_rgs([0, 1, 2, 0, 1], list('abcde')) - {{c}, {a, d}, {b, e}} - >>> Partition.from_rgs([0, 1, 2, 0, 1], list('cbead')) - {{e}, {a, c}, {b, d}} - >>> a = Partition([[1, 4], [2], [3, 5]]) - >>> Partition.from_rgs(a.RGS, a.members) - {{1, 4}, {2}, {3, 5}} - """ - if len(rgs) != len(elements): - raise ValueError('mismatch in rgs and element lengths') - max_elem = max(rgs) + 1 - partition = [[] for i in range(max_elem)] - j = 0 - for i in rgs: - partition[i].append(elements[j]) - j += 1 - if not all(p for p in partition): - raise ValueError('some blocks of the partition were empty.') - return Partition(partition) - -
    -
    [docs]class IntegerPartition(Basic): - """ - This class represents an integer partition. - - In number theory and combinatorics, a partition of a positive integer, - ``n``, also called an integer partition, is a way of writing ``n`` as a - list of positive integers that sum to n. Two partitions that differ only - in the order of summands are considered to be the same partition; if order - matters then the partitions are referred to as compositions. For example, - 4 has five partitions: [4], [3, 1], [2, 2], [2, 1, 1], and [1, 1, 1, 1]; - the compositions [1, 2, 1] and [1, 1, 2] are the same as partition - [2, 1, 1]. - - See Also - ======== - - sympy.utilities.iterables.partitions, - sympy.utilities.iterables.multiset_partitions - - Reference: http://en.wikipedia.org/wiki/Partition_%28number_theory%29 - """ - - _dict = None - _keys = None - - def __new__(cls, partition, integer=None): - """ - Generates a new IntegerPartition object from a list or dictionary. - - The partition can be given as a list of positive integers or a - dictionary of (integer, multiplicity) items. If the partition is - preceeded by an integer an error will be raised if the partition - does not sum to that given integer. - - Examples - ======== - - >>> from sympy.combinatorics.partitions import IntegerPartition - >>> a = IntegerPartition([5, 4, 3, 1, 1]) - >>> a - IntegerPartition(14, (5, 4, 3, 1, 1)) - >>> print(a) - [5, 4, 3, 1, 1] - >>> IntegerPartition({1:3, 2:1}) - IntegerPartition(5, (2, 1, 1, 1)) - - If the value that the partion should sum to is given first, a check - will be made to see n error will be raised if there is a discrepancy: - >>> IntegerPartition(10, [5, 4, 3, 1]) - Traceback (most recent call last): - ... - ValueError: The partition is not valid - - """ - if integer is not None: - integer, partition = partition, integer - if isinstance(partition, (dict, Dict)): - _ = [] - for k, v in sorted(list(partition.items()), reverse=True): - if not v: - continue - k, v = as_int(k), as_int(v) - _.extend([k]*v) - partition = tuple(_) - else: - partition = tuple(sorted(map(as_int, partition), reverse=True)) - sum_ok = False - if integer is None: - integer = sum(partition) - sum_ok = True - else: - integer = as_int(integer) - - if not sum_ok and sum(partition) != integer: - raise ValueError("Partition did not add to %s" % integer) - if any(i < 1 for i in partition): - raise ValueError("The summands must all be positive.") - - obj = Basic.__new__(cls, integer, partition) - obj.partition = list(partition) - obj.integer = integer - return obj - -
    [docs] def prev_lex(self): - """Return the previous partition of the integer, n, in lexical order, - wrapping around to [1, ..., 1] if the partition is [n]. - - Examples - ======== - - >>> from sympy.combinatorics.partitions import IntegerPartition - >>> p = IntegerPartition([4]) - >>> print(p.prev_lex()) - [3, 1] - >>> p.partition > p.prev_lex().partition - True - """ - d = defaultdict(int) - d.update(self.as_dict()) - keys = self._keys - if keys == [1]: - return IntegerPartition({self.integer: 1}) - if keys[-1] != 1: - d[keys[-1]] -= 1 - if keys[-1] == 2: - d[1] = 2 - else: - d[keys[-1] - 1] = d[1] = 1 - else: - d[keys[-2]] -= 1 - left = d[1] + keys[-2] - new = keys[-2] - d[1] = 0 - while left: - new -= 1 - if left - new >= 0: - d[new] += left//new - left -= d[new]*new - return IntegerPartition(self.integer, d) -
    -
    [docs] def next_lex(self): - """Return the next partition of the integer, n, in lexical order, - wrapping around to [n] if the partition is [1, ..., 1]. - - Examples - ======== - - >>> from sympy.combinatorics.partitions import IntegerPartition - >>> p = IntegerPartition([3, 1]) - >>> print(p.next_lex()) - [4] - >>> p.partition < p.next_lex().partition - True - """ - d = defaultdict(int) - d.update(self.as_dict()) - key = self._keys - a = key[-1] - if a == self.integer: - d.clear() - d[1] = self.integer - elif a == 1: - if d[a] > 1: - d[a + 1] += 1 - d[a] -= 2 - else: - b = key[-2] - d[b + 1] += 1 - d[1] = (d[b] - 1)*b - d[b] = 0 - else: - if d[a] > 1: - if len(key) == 1: - d.clear() - d[a + 1] = 1 - d[1] = self.integer - a - 1 - else: - a1 = a + 1 - d[a1] += 1 - d[1] = d[a]*a - a1 - d[a] = 0 - else: - b = key[-2] - b1 = b + 1 - d[b1] += 1 - need = d[b]*b + d[a]*a - b1 - d[a] = d[b] = 0 - d[1] = need - return IntegerPartition(self.integer, d) -
    -
    [docs] def as_dict(self): - """Return the partition as a dictionary whose keys are the - partition integers and the values are the multiplicity of that - integer. - - Examples - ======== - - >>> from sympy.combinatorics.partitions import IntegerPartition - >>> IntegerPartition([1]*3 + [2] + [3]*4).as_dict() - {1: 3, 2: 1, 3: 4} - """ - if self._dict is None: - groups = group(self.partition, multiple=False) - self._keys = [g[0] for g in groups] - self._dict = dict(groups) - return self._dict -
    - @property -
    [docs] def conjugate(self): - """ - Computes the conjugate partition of itself. - - Examples - ======== - - >>> from sympy.combinatorics.partitions import IntegerPartition - >>> a = IntegerPartition([6, 3, 3, 2, 1]) - >>> a.conjugate - [5, 4, 3, 1, 1, 1] - """ - j = 1 - temp_arr = list(self.partition) + [0] - k = temp_arr[0] - b = [0]*k - while k > 0: - while k > temp_arr[j]: - b[k - 1] = j - k -= 1 - j += 1 - return b -
    - def __lt__(self, other): - """Return True if self is less than other when the partition - is listed from smallest to biggest. - - Examples - ======== - - >>> from sympy.combinatorics.partitions import IntegerPartition - >>> a = IntegerPartition([3, 1]) - >>> a < a - False - >>> b = a.next_lex() - >>> a < b - True - >>> a == b - False - """ - return list(reversed(self.partition)) < list(reversed(other.partition)) - - def __le__(self, other): - """Return True if self is less than other when the partition - is listed from smallest to biggest. - - Examples - ======== - - >>> from sympy.combinatorics.partitions import IntegerPartition - >>> a = IntegerPartition([4]) - >>> a <= a - True - """ - return list(reversed(self.partition)) <= list(reversed(other.partition)) - -
    [docs] def as_ferrers(self, char='#'): - """ - Prints the ferrer diagram of a partition. - - Examples - ======== - - >>> from sympy.combinatorics.partitions import IntegerPartition - >>> print(IntegerPartition([1, 1, 5]).as_ferrers()) - ##### - # - # - """ - return "\n".join([char*i for i in self.partition]) -
    - def __str__(self): - return str(list(self.partition)) - -
    -
    [docs]def random_integer_partition(n, seed=None): - """ - Generates a random integer partition summing to ``n`` as a list - of reverse-sorted integers. - - Examples - ======== - - >>> from sympy.combinatorics.partitions import random_integer_partition - - For the following, a seed is given so a known value can be shown; in - practice, the seed would not be given. - - >>> random_integer_partition(100, seed=[1, 1, 12, 1, 2, 1, 85, 1]) - [85, 12, 2, 1] - >>> random_integer_partition(10, seed=[1, 2, 3, 1, 5, 1]) - [5, 3, 1, 1] - >>> random_integer_partition(1) - [1] - """ - from sympy.utilities.randtest import _randint - - n = as_int(n) - if n < 1: - raise ValueError('n must be a positive integer') - - randint = _randint(seed) - - partition = [] - while (n > 0): - k = randint(1, n) - mult = randint(1, n//k) - partition.append((k, mult)) - n -= k*mult - partition.sort(reverse=True) - partition = flatten([[k]*m for k, m in partition]) - return partition - -
    -
    [docs]def RGS_generalized(m): - """ - Computes the m + 1 generalized unrestricted growth strings - and returns them as rows in matrix. - - Examples - ======== - - >>> from sympy.combinatorics.partitions import RGS_generalized - >>> RGS_generalized(6) - [ 1, 1, 1, 1, 1, 1, 1] - [ 1, 2, 3, 4, 5, 6, 0] - [ 2, 5, 10, 17, 26, 0, 0] - [ 5, 15, 37, 77, 0, 0, 0] - [ 15, 52, 151, 0, 0, 0, 0] - [ 52, 203, 0, 0, 0, 0, 0] - [203, 0, 0, 0, 0, 0, 0] - """ - d = zeros(m + 1) - for i in range(0, m + 1): - d[0, i] = 1 - - for i in range(1, m + 1): - for j in range(m): - if j <= m - i: - d[i, j] = j * d[i - 1, j] + d[i - 1, j + 1] - else: - d[i, j] = 0 - return d - -
    -
    [docs]def RGS_enum(m): - """ - RGS_enum computes the total number of restricted growth strings - possible for a superset of size m. - - Examples - ======== - - >>> from sympy.combinatorics.partitions import RGS_enum - >>> from sympy.combinatorics.partitions import Partition - >>> RGS_enum(4) - 15 - >>> RGS_enum(5) - 52 - >>> RGS_enum(6) - 203 - - We can check that the enumeration is correct by actually generating - the partitions. Here, the 15 partitions of 4 items are generated: - - >>> a = Partition([list(range(4))]) - >>> s = set() - >>> for i in range(20): - ... s.add(a) - ... a += 1 - ... - >>> assert len(s) == 15 - - """ - if (m < 1): - return 0 - elif (m == 1): - return 1 - else: - return bell(m) - -
    -
    [docs]def RGS_unrank(rank, m): - """ - Gives the unranked restricted growth string for a given - superset size. - - Examples - ======== - - >>> from sympy.combinatorics.partitions import RGS_unrank - >>> RGS_unrank(14, 4) - [0, 1, 2, 3] - >>> RGS_unrank(0, 4) - [0, 0, 0, 0] - """ - if m < 1: - raise ValueError("The superset size must be >= 1") - if rank < 0 or RGS_enum(m) <= rank: - raise ValueError("Invalid arguments") - - L = [1] * (m + 1) - j = 1 - D = RGS_generalized(m) - for i in range(2, m + 1): - v = D[m - i, j] - cr = j*v - if cr <= rank: - L[i] = j + 1 - rank -= cr - j += 1 - else: - L[i] = int(rank / v + 1) - rank %= v - return [x - 1 for x in L[1:]] - -
    -
    [docs]def RGS_rank(rgs): - """ - Computes the rank of a restricted growth string. - - Examples - ======== - - >>> from sympy.combinatorics.partitions import RGS_rank, RGS_unrank - >>> RGS_rank([0, 1, 2, 1, 3]) - 42 - >>> RGS_rank(RGS_unrank(4, 7)) - 4 - """ - rgs_size = len(rgs) - rank = 0 - D = RGS_generalized(rgs_size) - for i in range(1, rgs_size): - n = len(rgs[(i + 1):]) - m = max(rgs[0:i]) - rank += D[n, m + 1] * rgs[i] - return rank
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/combinatorics/perm_groups.html b/dev-py3k/_modules/sympy/combinatorics/perm_groups.html deleted file mode 100644 index 99924a5f20e..00000000000 --- a/dev-py3k/_modules/sympy/combinatorics/perm_groups.html +++ /dev/null @@ -1,3535 +0,0 @@ - - - - - - - - - - sympy.combinatorics.perm_groups — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.combinatorics.perm_groups

    -from random import randrange, choice
    -from math import log
    -
    -from sympy.core import Basic
    -from sympy.combinatorics import Permutation
    -from sympy.combinatorics.permutations import (_af_commutes_with, _af_invert,
    -    _af_rmul, _af_rmuln, _af_pow, Cycle)
    -from sympy.combinatorics.util import (_check_cycles_alt_sym,
    -    _distribute_gens_by_base, _orbits_transversals_from_bsgs,
    -    _handle_precomputed_bsgs, _base_ordering, _strong_gens_from_distr,
    -    _strip, _strip_af)
    -from sympy.functions.combinatorial.factorials import factorial
    -from sympy.ntheory import sieve
    -from sympy.utilities.iterables import has_variety, is_sequence, uniq
    -from sympy.utilities.randtest import _randrange
    -
    -rmul = Permutation.rmul_with_af
    -_af_new = Permutation._af_new
    -
    -
    -
    [docs]class PermutationGroup(Basic): - """The class defining a Permutation group. - - PermutationGroup([p1, p2, ..., pn]) returns the permutation group - generated by the list of permutations. This group can be supplied - to Polyhedron if one desires to decorate the elements to which the - indices of the permutation refer. - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation.print_cyclic = True - >>> from sympy.combinatorics.permutations import Cycle - >>> from sympy.combinatorics.polyhedron import Polyhedron - >>> from sympy.combinatorics.perm_groups import PermutationGroup - - The permutations corresponding to motion of the front, right and - bottom face of a 2x2 Rubik's cube are defined: - - >>> F = Permutation(2, 19, 21, 8)(3, 17, 20, 10)(4, 6, 7, 5) - >>> R = Permutation(1, 5, 21, 14)(3, 7, 23, 12)(8, 10, 11, 9) - >>> D = Permutation(6, 18, 14, 10)(7, 19, 15, 11)(20, 22, 23, 21) - - These are passed as permutations to PermutationGroup: - - >>> G = PermutationGroup(F, R, D) - >>> G.order() - 3674160 - - The group can be supplied to a Polyhedron in order to track the - objects being moved. An example involving the 2x2 Rubik's cube is - given there, but here is a simple demonstration: - - >>> a = Permutation(2, 1) - >>> b = Permutation(1, 0) - >>> G = PermutationGroup(a, b) - >>> P = Polyhedron(list('ABC'), pgroup=G) - >>> P.corners - (A, B, C) - >>> P.rotate(0) # apply permutation 0 - >>> P.corners - (A, C, B) - >>> P.reset() - >>> P.corners - (A, B, C) - - Or one can make a permutation as a product of selected permutations - and apply them to an iterable directly: - - >>> P10 = G.make_perm([0, 1]) - >>> P10('ABC') - ['C', 'A', 'B'] - - See Also - ======== - - sympy.combinatorics.polyhedron.Polyhedron, - sympy.combinatorics.permutations.Permutation - - References - ========== - - [1] Holt, D., Eick, B., O'Brien, E. - "Handbook of Computational Group Theory" - - [2] Seress, A. - "Permutation Group Algorithms" - - [3] http://en.wikipedia.org/wiki/Schreier_vector - - [4] http://en.wikipedia.org/wiki/Nielsen_transformation - #Product_replacement_algorithm - - [5] Frank Celler, Charles R.Leedham-Green, Scott H.Murray, - Alice C.Niemeyer, and E.A.O'Brien. "Generating Random - Elements of a Finite Group" - - [6] http://en.wikipedia.org/wiki/Block_%28permutation_group_theory%29 - - [7] http://www.algorithmist.com/index.php/Union_Find - - [8] http://en.wikipedia.org/wiki/Multiply_transitive_group#Multiply_transitive_groups - - [9] http://en.wikipedia.org/wiki/Center_%28group_theory%29 - - [10] http://en.wikipedia.org/wiki/Centralizer_and_normalizer - - [11] http://groupprops.subwiki.org/wiki/Derived_subgroup - - [12] http://en.wikipedia.org/wiki/Nilpotent_group - - [13] http://www.math.colostate.edu/~hulpke/CGT/cgtnotes.pdf - - """ - - def __new__(cls, *args, **kwargs): - """The default constructor. Accepts Cycle and Permutation forms. - Removes duplicates unless ``dups`` keyword is False. - """ - args = list(args[0] if is_sequence(args[0]) else args) - if not args: - raise ValueError('must supply one or more permutations ' - 'to define the group') - if any(isinstance(a, Cycle) for a in args): - args = [Permutation(a) for a in args] - if has_variety(a.size for a in args): - degree = kwargs.pop('degree', None) - if degree is None: - degree = max(a.size for a in args) - for i in range(len(args)): - if args[i].size != degree: - args[i] = Permutation(args[i], size=degree) - if kwargs.pop('dups', True): - args = list(uniq([_af_new(list(a)) for a in args])) - obj = Basic.__new__(cls, *args, **kwargs) - obj._generators = args - obj._order = None - obj._center = [] - obj._is_abelian = None - obj._is_transitive = None - obj._is_sym = None - obj._is_alt = None - obj._is_primitive = None - obj._is_nilpotent = None - obj._is_solvable = None - obj._is_trivial = None - obj._transitivity_degree = None - obj._max_div = None - obj._r = len(obj._generators) - obj._degree = obj._generators[0].size - - # these attributes are assigned after running schreier_sims - obj._base = [] - obj._strong_gens = [] - obj._basic_orbits = [] - obj._transversals = [] - - # these attributes are assigned after running _random_pr_init - obj._random_gens = [] - return obj - - def __getitem__(self, i): - return self._generators[i] - - def __len__(self): - return len(self._generators) - - def __eq__(self, other): - """Return True if self and other have the same generators. - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> p = Permutation(0, 1, 2, 3, 4, 5) - >>> G = PermutationGroup([p, p**2]) - >>> H = PermutationGroup([p**2, p]) - >>> G.generators == H.generators - False - >>> G == H - True - - """ - if not isinstance(other, PermutationGroup): - return False - return set(self.generators) == set(other.generators) - - def __hash__(self): - return super(PermutationGroup, self).__hash__() - - def __mul__(self, other): - """Return the direct product of two permutation groups as a permutation - group. - - This implementation realizes the direct product by shifting - the index set for the generators of the second group: so if we have - G acting on n1 points and H acting on n2 points, G*H acts on n1 + n2 - points. - - Examples - ======== - - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> from sympy.combinatorics.named_groups import CyclicGroup - >>> G = CyclicGroup(5) - >>> H = G*G - >>> H - PermutationGroup([ - Permutation(9)(0, 1, 2, 3, 4), - Permutation(5, 6, 7, 8, 9)]) - >>> H.order() - 25 - - """ - gens1 = [perm._array_form for perm in self.generators] - gens2 = [perm._array_form for perm in other.generators] - n1 = self._degree - n2 = other._degree - start = list(range(n1)) - end = list(range(n1, n1 + n2)) - for i in range(len(gens2)): - gens2[i] = [x + n1 for x in gens2[i]] - gens2 = [start + gen for gen in gens2] - gens1 = [gen + end for gen in gens1] - together = gens1 + gens2 - gens = [_af_new(x) for x in together] - return PermutationGroup(gens) - - def _random_pr_init(self, r, n, _random_prec_n=None): - r"""Initialize random generators for the product replacement algorithm. - - The implementation uses a modification of the original product - replacement algorithm due to Leedham-Green, as described in [1], - pp. 69-71; also, see [2], pp. 27-29 for a detailed theoretical - analysis of the original product replacement algorithm, and [4]. - - The product replacement algorithm is used for producing random, - uniformly distributed elements of a group ``G`` with a set of generators - ``S``. For the initialization ``_random_pr_init``, a list ``R`` of - ``\max\{r, |S|\}`` group generators is created as the attribute - ``G._random_gens``, repeating elements of ``S`` if necessary, and the - identity element of ``G`` is appended to ``R`` - we shall refer to this - last element as the accumulator. Then the function ``random_pr()`` - is called ``n`` times, randomizing the list ``R`` while preserving - the generation of ``G`` by ``R``. The function ``random_pr()`` itself - takes two random elements ``g, h`` among all elements of ``R`` but - the accumulator and replaces ``g`` with a randomly chosen element - from ``\{gh, g(~h), hg, (~h)g\}``. Then the accumulator is multiplied - by whatever ``g`` was replaced by. The new value of the accumulator is - then returned by ``random_pr()``. - - The elements returned will eventually (for ``n`` large enough) become - uniformly distributed across ``G`` ([5]). For practical purposes however, - the values ``n = 50, r = 11`` are suggested in [1]. - - Notes - ===== - - THIS FUNCTION HAS SIDE EFFECTS: it changes the attribute - self._random_gens - - See Also - ======== - - random_pr - - """ - deg = self.degree - random_gens = [x._array_form for x in self.generators] - k = len(random_gens) - if k < r: - for i in range(k, r): - random_gens.append(random_gens[i - k]) - acc = list(range(deg)) - random_gens.append(acc) - self._random_gens = random_gens - - # handle randomized input for testing purposes - if _random_prec_n is None: - for i in range(n): - self.random_pr() - else: - for i in range(n): - self.random_pr(_random_prec=_random_prec_n[i]) - - def _union_find_merge(self, first, second, ranks, parents, not_rep): - """Merges two classes in a union-find data structure. - - Used in the implementation of Atkinson's algorithm as suggested in [1], - pp. 83-87. The class merging process uses union by rank as an - optimization. ([7]) - - Notes - ===== - - THIS FUNCTION HAS SIDE EFFECTS: the list of class representatives, - ``parents``, the list of class sizes, ``ranks``, and the list of - elements that are not representatives, ``not_rep``, are changed due to - class merging. - - See Also - ======== - - minimal_block, _union_find_rep - - References - ========== - - [1] Holt, D., Eick, B., O'Brien, E. - "Handbook of computational group theory" - - [7] http://www.algorithmist.com/index.php/Union_Find - - """ - rep_first = self._union_find_rep(first, parents) - rep_second = self._union_find_rep(second, parents) - if rep_first != rep_second: - # union by rank - if ranks[rep_first] >= ranks[rep_second]: - new_1, new_2 = rep_first, rep_second - else: - new_1, new_2 = rep_second, rep_first - total_rank = ranks[new_1] + ranks[new_2] - if total_rank > self.max_div: - return -1 - parents[new_2] = new_1 - ranks[new_1] = total_rank - not_rep.append(new_2) - return 1 - return 0 - - def _union_find_rep(self, num, parents): - """Find representative of a class in a union-find data structure. - - Used in the implementation of Atkinson's algorithm as suggested in [1], - pp. 83-87. After the representative of the class to which ``num`` - belongs is found, path compression is performed as an optimization - ([7]). - - Notes - ===== - - THIS FUNCTION HAS SIDE EFFECTS: the list of class representatives, - ``parents``, is altered due to path compression. - - See Also - ======== - - minimal_block, _union_find_merge - - References - ========== - - [1] Holt, D., Eick, B., O'Brien, E. - "Handbook of computational group theory" - - [7] http://www.algorithmist.com/index.php/Union_Find - - """ - rep, parent = num, parents[num] - while parent != rep: - rep = parent - parent = parents[rep] - # path compression - temp, parent = num, parents[num] - while parent != rep: - parents[temp] = rep - temp = parent - parent = parents[temp] - return rep - - @property -
    [docs] def base(self): - """Return a base from the Schreier-Sims algorithm. - - For a permutation group ``G``, a base is a sequence of points - ``B = (b_1, b_2, ..., b_k)`` such that no element of ``G`` apart - from the identity fixes all the points in ``B``. The concepts of - a base and strong generating set and their applications are - discussed in depth in [1], pp. 87-89 and [2], pp. 55-57. - - An alternative way to think of ``B`` is that it gives the - indices of the stabilizer cosets that contain more than the - identity permutation. - - Examples - ======== - - >>> from sympy.combinatorics import Permutation, PermutationGroup - >>> G = PermutationGroup([Permutation(0, 1, 3)(2, 4)]) - >>> G.base - [0, 2] - - See Also - ======== - - strong_gens, basic_transversals, basic_orbits, basic_stabilizers - - """ - if self._base == []: - self.schreier_sims() - return self._base -
    -
    [docs] def baseswap(self, base, strong_gens, pos, randomized=False, - transversals=None, basic_orbits=None, strong_gens_distr=None): - r"""Swap two consecutive base points in base and strong generating set. - - If a base for a group ``G`` is given by ``(b_1, b_2, ..., b_k)``, this - function returns a base ``(b_1, b_2, ..., b_{i+1}, b_i, ..., b_k)``, - where ``i`` is given by ``pos``, and a strong generating set relative - to that base. The original base and strong generating set are not - modified. - - The randomized version (default) is of Las Vegas type. - - Parameters - ========== - - base, strong_gens - The base and strong generating set. - pos - The position at which swapping is performed. - randomized - A switch between randomized and deterministic version. - transversals - The transversals for the basic orbits, if known. - basic_orbits - The basic orbits, if known. - strong_gens_distr - The strong generators distributed by basic stabilizers, if known. - - Returns - ======= - - (base, strong_gens) - ``base`` is the new base, and ``strong_gens`` is a generating set - relative to it. - - Examples - ======== - - >>> from sympy.combinatorics.named_groups import SymmetricGroup - >>> from sympy.combinatorics.testutil import _verify_bsgs - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> S = SymmetricGroup(4) - >>> S.schreier_sims() - >>> S.base - [0, 1, 2] - >>> base, gens = S.baseswap(S.base, S.strong_gens, 1, randomized=False) - >>> base, gens - ([0, 2, 1], - [Permutation(0, 1, 2, 3), Permutation(3)(0, 1), Permutation(1, 3, 2), - Permutation(2, 3), Permutation(1, 3)]) - - check that base, gens is a BSGS - - >>> S1 = PermutationGroup(gens) - >>> _verify_bsgs(S1, base, gens) - True - - See Also - ======== - - schreier_sims - - Notes - ===== - - The deterministic version of the algorithm is discussed in - [1], pp. 102-103; the randomized version is discussed in [1], p.103, and - [2], p.98. It is of Las Vegas type. - Notice that [1] contains a mistake in the pseudocode and - discussion of BASESWAP: on line 3 of the pseudocode, - ``|\beta_{i+1}^{\left\langle T\right\rangle}|`` should be replaced by - ``|\beta_{i}^{\left\langle T\right\rangle}|``, and the same for the - discussion of the algorithm. - - """ - # construct the basic orbits, generators for the stabilizer chain - # and transversal elements from whatever was provided - transversals, basic_orbits, strong_gens_distr = \ - _handle_precomputed_bsgs(base, strong_gens, transversals, - basic_orbits, strong_gens_distr) - base_len = len(base) - degree = self.degree - # size of orbit of base[pos] under the stabilizer we seek to insert - # in the stabilizer chain at position pos + 1 - size = len(basic_orbits[pos])*len(basic_orbits[pos + 1]) \ - //len(_orbit(degree, strong_gens_distr[pos], base[pos + 1])) - # initialize the wanted stabilizer by a subgroup - if pos + 2 > base_len - 1: - T = [] - else: - T = strong_gens_distr[pos + 2][:] - # randomized version - if randomized is True: - stab_pos = PermutationGroup(strong_gens_distr[pos]) - schreier_vector = stab_pos.schreier_vector(base[pos + 1]) - # add random elements of the stabilizer until they generate it - while len(_orbit(degree, T, base[pos])) != size: - new = stab_pos.random_stab(base[pos + 1], - schreier_vector=schreier_vector) - T.append(new) - # deterministic version - else: - Gamma = set(basic_orbits[pos]) - Gamma.remove(base[pos]) - if base[pos + 1] in Gamma: - Gamma.remove(base[pos + 1]) - # add elements of the stabilizer until they generate it by - # ruling out member of the basic orbit of base[pos] along the way - while len(_orbit(degree, T, base[pos])) != size: - gamma = next(iter(Gamma)) - x = transversals[pos][gamma] - temp = x._array_form.index(base[pos + 1]) # (~x)(base[pos + 1]) - if temp not in basic_orbits[pos + 1]: - Gamma = Gamma - _orbit(degree, T, gamma) - else: - y = transversals[pos + 1][temp] - el = rmul(x, y) - if el(base[pos]) not in _orbit(degree, T, base[pos]): - T.append(el) - Gamma = Gamma - _orbit(degree, T, base[pos]) - # build the new base and strong generating set - strong_gens_new_distr = strong_gens_distr[:] - strong_gens_new_distr[pos + 1] = T - base_new = base[:] - base_new[pos], base_new[pos + 1] = base_new[pos + 1], base_new[pos] - strong_gens_new = _strong_gens_from_distr(strong_gens_new_distr) - for gen in T: - if gen not in strong_gens_new: - strong_gens_new.append(gen) - return base_new, strong_gens_new -
    - @property -
    [docs] def basic_orbits(self): - """ - Return the basic orbits relative to a base and strong generating set. - - If ``(b_1, b_2, ..., b_k)`` is a base for a group ``G``, and - ``G^{(i)} = G_{b_1, b_2, ..., b_{i-1}}`` is the ``i``-th basic stabilizer - (so that ``G^{(1)} = G``), the ``i``-th basic orbit relative to this base - is the orbit of ``b_i`` under ``G^{(i)}``. See [1], pp. 87-89 for more - information. - - Examples - ======== - - >>> from sympy.combinatorics.named_groups import SymmetricGroup - >>> S = SymmetricGroup(4) - >>> S.basic_orbits - [[0, 1, 2, 3], [1, 2, 3], [2, 3]] - - See Also - ======== - - base, strong_gens, basic_transversals, basic_stabilizers - - """ - if self._basic_orbits == []: - self.schreier_sims() - return self._basic_orbits -
    - @property -
    [docs] def basic_stabilizers(self): - """ - Return a chain of stabilizers relative to a base and strong generating - set. - - The ``i``-th basic stabilizer ``G^{(i)}`` relative to a base - ``(b_1, b_2, ..., b_k)`` is ``G_{b_1, b_2, ..., b_{i-1}}``. For more - information, see [1], pp. 87-89. - - Examples - ======== - - >>> from sympy.combinatorics.named_groups import AlternatingGroup - >>> A = AlternatingGroup(4) - >>> A.schreier_sims() - >>> A.base - [0, 1] - >>> for g in A.basic_stabilizers: - ... print(g) - ... - PermutationGroup([ - Permutation(3)(0, 1, 2), - Permutation(1, 2, 3)]) - PermutationGroup([ - Permutation(1, 2, 3)]) - - See Also - ======== - - base, strong_gens, basic_orbits, basic_transversals - - """ - - if self._transversals == []: - self.schreier_sims() - strong_gens = self._strong_gens - base = self._base - strong_gens_distr = _distribute_gens_by_base(base, strong_gens) - basic_stabilizers = [] - for gens in strong_gens_distr: - basic_stabilizers.append(PermutationGroup(gens)) - return basic_stabilizers -
    - @property -
    [docs] def basic_transversals(self): - """ - Return basic transversals relative to a base and strong generating set. - - The basic transversals are transversals of the basic orbits. They - are provided as a list of dictionaries, each dictionary having - keys - the elements of one of the basic orbits, and values - the - corresponding transversal elements. See [1], pp. 87-89 for more - information. - - Examples - ======== - - >>> from sympy.combinatorics.named_groups import AlternatingGroup - >>> A = AlternatingGroup(4) - >>> A.basic_transversals - [{0: Permutation(3), - 1: Permutation(3)(0, 1, 2), - 2: Permutation(3)(0, 2, 1), - 3: Permutation(0, 3, 1)}, - {1: Permutation(3), - 2: Permutation(1, 2, 3), - 3: Permutation(1, 3, 2)}] - - See Also - ======== - - strong_gens, base, basic_orbits, basic_stabilizers - - """ - - if self._transversals == []: - self.schreier_sims() - return self._transversals -
    -
    [docs] def center(self): - r""" - Return the center of a permutation group. - - The center for a group ``G`` is defined as - ``Z(G) = \{z\in G | \forall g\in G, zg = gz \}``, - the set of elements of ``G`` that commute with all elements of ``G``. - It is equal to the centralizer of ``G`` inside ``G``, and is naturally a - subgroup of ``G`` ([9]). - - Examples - ======== - - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> from sympy.combinatorics.named_groups import DihedralGroup - >>> D = DihedralGroup(4) - >>> G = D.center() - >>> G.order() - 2 - - See Also - ======== - - centralizer - - Notes - ===== - - This is a naive implementation that is a straightforward application - of ``.centralizer()`` - - """ - return self.centralizer(self) -
    -
    [docs] def centralizer(self, other): - r""" - Return the centralizer of a group/set/element. - - The centralizer of a set of permutations ``S`` inside - a group ``G`` is the set of elements of ``G`` that commute with all - elements of ``S``:: - - ``C_G(S) = \{ g \in G | gs = sg \forall s \in S\}`` ([10]) - - Usually, ``S`` is a subset of ``G``, but if ``G`` is a proper subgroup of - the full symmetric group, we allow for ``S`` to have elements outside - ``G``. - - It is naturally a subgroup of ``G``; the centralizer of a permutation - group is equal to the centralizer of any set of generators for that - group, since any element commuting with the generators commutes with - any product of the generators. - - Parameters - ========== - - other - a permutation group/list of permutations/single permutation - - Examples - ======== - - >>> from sympy.combinatorics.named_groups import (SymmetricGroup, - ... CyclicGroup) - >>> S = SymmetricGroup(6) - >>> C = CyclicGroup(6) - >>> H = S.centralizer(C) - >>> H.is_subgroup(C) - True - - See Also - ======== - - subgroup_search - - Notes - ===== - - The implementation is an application of ``.subgroup_search()`` with - tests using a specific base for the group ``G``. - - """ - if hasattr(other, 'generators'): - if other.is_trivial or self.is_trivial: - return self - degree = self.degree - identity = _af_new(list(range(degree))) - orbits = other.orbits() - num_orbits = len(orbits) - orbits.sort(key=lambda x: -len(x)) - long_base = [] - orbit_reps = [None]*num_orbits - orbit_reps_indices = [None]*num_orbits - orbit_descr = [None]*degree - for i in range(num_orbits): - orbit = list(orbits[i]) - orbit_reps[i] = orbit[0] - orbit_reps_indices[i] = len(long_base) - for point in orbit: - orbit_descr[point] = i - long_base = long_base + orbit - base, strong_gens = self.schreier_sims_incremental(base=long_base) - strong_gens_distr = _distribute_gens_by_base(base, strong_gens) - i = 0 - for i in range(len(base)): - if strong_gens_distr[i] == [identity]: - break - base = base[:i] - base_len = i - for j in range(num_orbits): - if base[base_len - 1] in orbits[j]: - break - rel_orbits = orbits[: j + 1] - num_rel_orbits = len(rel_orbits) - transversals = [None]*num_rel_orbits - for j in range(num_rel_orbits): - rep = orbit_reps[j] - transversals[j] = dict( - other.orbit_transversal(rep, pairs=True)) - trivial_test = lambda x: True - tests = [None]*base_len - for l in range(base_len): - if base[l] in orbit_reps: - tests[l] = trivial_test - else: - def test(computed_words, l=l): - g = computed_words[l] - rep_orb_index = orbit_descr[base[l]] - rep = orbit_reps[rep_orb_index] - im = g._array_form[base[l]] - im_rep = g._array_form[rep] - tr_el = transversals[rep_orb_index][base[l]] - # using the definition of transversal, - # base[l]^g = rep^(tr_el*g); - # if g belongs to the centralizer, then - # base[l]^g = (rep^g)^tr_el - return im == tr_el._array_form[im_rep] - tests[l] = test - - def prop(g): - return [rmul(g, gen) for gen in other.generators] == \ - [rmul(gen, g) for gen in other.generators] - return self.subgroup_search(prop, base=base, - strong_gens=strong_gens, tests=tests) - elif hasattr(other, '__getitem__'): - gens = list(other) - return self.centralizer(PermutationGroup(gens)) - elif hasattr(other, 'array_form'): - return self.centralizer(PermutationGroup([other])) -
    -
    [docs] def commutator(self, G, H): - """ - Return the commutator of two subgroups. - - For a permutation group ``K`` and subgroups ``G``, ``H``, the - commutator of ``G`` and ``H`` is defined as the group generated - by all the commutators ``[g, h] = hgh^{-1}g^{-1}`` for ``g`` in ``G`` and - ``h`` in ``H``. It is naturally a subgroup of ``K`` ([1], p.27). - - Examples - ======== - - >>> from sympy.combinatorics.named_groups import (SymmetricGroup, - ... AlternatingGroup) - >>> S = SymmetricGroup(5) - >>> A = AlternatingGroup(5) - >>> G = S.commutator(S, A) - >>> G.is_subgroup(A) - True - - See Also - ======== - - derived_subgroup - - Notes - ===== - - The commutator of two subgroups ``H, G`` is equal to the normal closure - of the commutators of all the generators, i.e. ``hgh^{-1}g^{-1}`` for ``h`` - a generator of ``H`` and ``g`` a generator of ``G`` ([1], p.28) - - """ - ggens = G.generators - hgens = H.generators - commutators = [] - for ggen in ggens: - for hgen in hgens: - commutator = rmul(hgen, ggen, ~hgen, ~ggen) - if commutator not in commutators: - commutators.append(commutator) - res = self.normal_closure(commutators) - return res -
    -
    [docs] def coset_factor(self, g, factor_index=False): - """Return ``G``'s (self's) coset factorization of ``g`` - - If ``g`` is an element of ``G`` then it can be written as the product - of permutations drawn from the Schreier-Sims coset decomposition, - - The permutations returned in ``f`` are those for which - the product gives ``g``: ``g = f[n]*...f[1]*f[0]`` where ``n = len(B)`` - and ``B = G.base``. f[i] is one of the permutations in - ``self._basic_orbits[i]``. - - If factor_index==True, - returns a tuple ``[b[0],..,b[n]]``, where ``b[i]`` - belongs to ``self._basic_orbits[i]`` - - Examples - ======== - - >>> from sympy.combinatorics import Permutation, PermutationGroup - >>> Permutation.print_cyclic = True - >>> a = Permutation(0, 1, 3, 7, 6, 4)(2, 5) - >>> b = Permutation(0, 1, 3, 2)(4, 5, 7, 6) - >>> G = PermutationGroup([a, b]) - - Define g: - - >>> g = Permutation(7)(1, 2, 4)(3, 6, 5) - - Confirm that it is an element of G: - - >>> G.contains(g) - True - - Thus, it can be written as a product of factors (up to - 3) drawn from u. See below that a factor from u1 and u2 - and the Identity permutation have been used: - - >>> f = G.coset_factor(g) - >>> f[2]*f[1]*f[0] == g - True - >>> f1 = G.coset_factor(g, True); f1 - [0, 4, 4] - >>> tr = G.basic_transversals - >>> f[0] == tr[0][f1[0]] - True - - If g is not an element of G then [] is returned: - - >>> c = Permutation(5, 6, 7) - >>> G.coset_factor(c) - [] - - see util._strip - """ - if isinstance(g, (Cycle, Permutation)): - g = g.list() - if len(g) != self._degree: - # this could either adjust the size or return [] immediately - # but we don't choose between the two and just signal a possible - # error - raise ValueError('g should be the same size as permutations of G') - I = list(range(self._degree)) - basic_orbits = self.basic_orbits - transversals = self._transversals - factors = [] - base = self.base - h = g - for i in range(len(base)): - beta = h[base[i]] - if beta == base[i]: - factors.append(beta) - continue - if beta not in basic_orbits[i]: - return [] - u = transversals[i][beta]._array_form - h = _af_rmul(_af_invert(u), h) - factors.append(beta) - if h != I: - return [] - if factor_index: - return factors - tr = self.basic_transversals - factors = [tr[i][factors[i]] for i in range(len(base))] - return factors -
    -
    [docs] def coset_rank(self, g): - """rank using Schreier-Sims representation - - The coset rank of ``g`` is the ordering number in which - it appears in the lexicographic listing according to the - coset decomposition - - The ordering is the same as in G.generate(method='coset'). - If ``g`` does not belong to the group it returns None. - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation.print_cyclic = True - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> a = Permutation(0, 1, 3, 7, 6, 4)(2, 5) - >>> b = Permutation(0, 1, 3, 2)(4, 5, 7, 6) - >>> G = PermutationGroup([a, b]) - >>> c = Permutation(7)(2, 4)(3, 5) - >>> G.coset_rank(c) - 16 - >>> G.coset_unrank(16) - Permutation(7)(2, 4)(3, 5) - - See Also - ======== - - coset_factor - - """ - factors = self.coset_factor(g, True) - if not factors: - return None - rank = 0 - b = 1 - transversals = self._transversals - base = self._base - basic_orbits = self._basic_orbits - for i in range(len(base)): - k = factors[i] - j = basic_orbits[i].index(k) - rank += b*j - b = b*len(transversals[i]) - return rank -
    -
    [docs] def coset_unrank(self, rank, af=False): - """unrank using Schreier-Sims representation - - coset_unrank is the inverse operation of coset_rank - if 0 <= rank < order; otherwise it returns None. - - """ - if rank < 0 or rank >= self.order(): - return None - base = self._base - transversals = self._transversals - basic_orbits = self._basic_orbits - m = len(base) - v = [0]*m - for i in range(m): - rank, c = divmod(rank, len(transversals[i])) - v[i] = basic_orbits[i][c] - a = [transversals[i][v[i]]._array_form for i in range(m)] - h = _af_rmuln(*a) - if af: - return h - else: - return _af_new(h) -
    - @property -
    [docs] def degree(self): - """Returns the size of the permutations in the group. - - The number of permutations comprising the group is given by - len(group); the number of permutations that can be generated - by the group is given by group.order(). - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation.print_cyclic = True - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> a = Permutation([1, 0, 2]) - >>> G = PermutationGroup([a]) - >>> G.degree - 3 - >>> len(G) - 1 - >>> G.order() - 2 - >>> list(G.generate()) - [Permutation(2), Permutation(2)(0, 1)] - - See Also - ======== - - order - """ - return self._degree -
    -
    [docs] def derived_series(self): - r"""Return the derived series for the group. - - The derived series for a group ``G`` is defined as - ``G = G_0 > G_1 > G_2 > \ldots`` where ``G_i = [G_{i-1}, G_{i-1}]``, - i.e. ``G_i`` is the derived subgroup of ``G_{i-1}``, for - ``i\in\mathbb{N}``. When we have ``G_k = G_{k-1}`` for some - ``k\in\mathbb{N}``, the series terminates. - - Returns - ======= - - A list of permutation groups containing the members of the derived - series in the order ``G = G_0, G_1, G_2, \ldots``. - - Examples - ======== - - >>> from sympy.combinatorics.named_groups import (SymmetricGroup, - ... AlternatingGroup, DihedralGroup) - >>> A = AlternatingGroup(5) - >>> len(A.derived_series()) - 1 - >>> S = SymmetricGroup(4) - >>> len(S.derived_series()) - 4 - >>> S.derived_series()[1].is_subgroup(AlternatingGroup(4)) - True - >>> S.derived_series()[2].is_subgroup(DihedralGroup(2)) - True - - See Also - ======== - - derived_subgroup - - """ - res = [self] - current = self - next = self.derived_subgroup() - while not current.is_subgroup(next): - res.append(next) - current = next - next = next.derived_subgroup() - return res -
    -
    [docs] def derived_subgroup(self): - """Compute the derived subgroup. - - The derived subgroup, or commutator subgroup is the subgroup generated - by all commutators ``[g, h] = hgh^{-1}g^{-1}`` for ``g, h\in G`` ; it is - equal to the normal closure of the set of commutators of the generators - ([1], p.28, [11]). - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation.print_cyclic = True - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> a = Permutation([1, 0, 2, 4, 3]) - >>> b = Permutation([0, 1, 3, 2, 4]) - >>> G = PermutationGroup([a, b]) - >>> C = G.derived_subgroup() - >>> list(C.generate(af=True)) - [[0, 1, 2, 3, 4], [0, 1, 3, 4, 2], [0, 1, 4, 2, 3]] - - See Also - ======== - - derived_series - - """ - r = self._r - gens = [p._array_form for p in self.generators] - gens_inv = [_af_invert(p) for p in gens] - set_commutators = set() - degree = self._degree - rng = list(range(degree)) - for i in range(r): - for j in range(r): - p1 = gens[i] - p2 = gens[j] - c = list(range(degree)) - for k in rng: - c[p2[p1[k]]] = p1[p2[k]] - ct = tuple(c) - if not ct in set_commutators: - set_commutators.add(ct) - cms = [_af_new(p) for p in set_commutators] - G2 = self.normal_closure(cms) - return G2 -
    -
    [docs] def generate(self, method="coset", af=False): - """Return iterator to generate the elements of the group - - Iteration is done with one of these methods:: - - method='coset' using the Schreier-Sims coset representation - method='dimino' using the Dimino method - - If af = True it yields the array form of the permutations - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation.print_cyclic = True - >>> from sympy.combinatorics import PermutationGroup - >>> from sympy.combinatorics.polyhedron import tetrahedron - - The permutation group given in the tetrahedron object is not - true groups: - - >>> G = tetrahedron.pgroup - >>> G.is_group() - False - - But the group generated by the permutations in the tetrahedron - pgroup -- even the first two -- is a proper group: - - >>> H = PermutationGroup(G[0], G[1]) - >>> J = PermutationGroup(list(H.generate())); J - PermutationGroup([ - Permutation(0, 1)(2, 3), - Permutation(3), - Permutation(1, 2, 3), - Permutation(1, 3, 2), - Permutation(0, 3, 1), - Permutation(0, 2, 3), - Permutation(0, 3)(1, 2), - Permutation(0, 1, 3), - Permutation(3)(0, 2, 1), - Permutation(0, 3, 2), - Permutation(3)(0, 1, 2), - Permutation(0, 2)(1, 3)]) - >>> _.is_group() - True - """ - if method == "coset": - return self.generate_schreier_sims(af) - elif method == "dimino": - return self.generate_dimino(af) - else: - raise NotImplementedError('No generation defined for %s' % method) -
    -
    [docs] def generate_dimino(self, af=False): - """Yield group elements using Dimino's algorithm - - If af == True it yields the array form of the permutations - - References - ========== - - [1] The Implementation of Various Algorithms for Permutation Groups in - the Computer Algebra System: AXIOM, N.J. Doye, M.Sc. Thesis - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation.print_cyclic = True - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> a = Permutation([0, 2, 1, 3]) - >>> b = Permutation([0, 2, 3, 1]) - >>> g = PermutationGroup([a, b]) - >>> list(g.generate_dimino(af=True)) - [[0, 1, 2, 3], [0, 2, 1, 3], [0, 2, 3, 1], - [0, 1, 3, 2], [0, 3, 2, 1], [0, 3, 1, 2]] - - """ - idn = list(range(self.degree)) - order = 0 - element_list = [idn] - set_element_list = set([tuple(idn)]) - if af: - yield idn - else: - yield _af_new(idn) - gens = [p._array_form for p in self.generators] - - for i in range(len(gens)): - # D elements of the subgroup G_i generated by gens[:i] - D = element_list[:] - N = [idn] - while N: - A = N - N = [] - for a in A: - for g in gens[:i + 1]: - ag = _af_rmul(a, g) - if tuple(ag) not in set_element_list: - # produce G_i*g - for d in D: - order += 1 - ap = _af_rmul(d, ag) - if af: - yield ap - else: - p = _af_new(ap) - yield p - element_list.append(ap) - set_element_list.add(tuple(ap)) - N.append(ap) - self._order = len(element_list) -
    -
    [docs] def generate_schreier_sims(self, af=False): - """Yield group elements using the Schreier-Sims representation - in coset_rank order - - If af = True it yields the array form of the permutations - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation.print_cyclic = True - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> a = Permutation([0, 2, 1, 3]) - >>> b = Permutation([0, 2, 3, 1]) - >>> g = PermutationGroup([a, b]) - >>> list(g.generate_schreier_sims(af=True)) - [[0, 1, 2, 3], [0, 2, 1, 3], [0, 3, 2, 1], - [0, 1, 3, 2], [0, 2, 3, 1], [0, 3, 1, 2]] - """ - - n = self._degree - u = self.basic_transversals - basic_orbits = self._basic_orbits - if len(u) == 0: - for x in self.generators: - if af: - yield x._array_form - else: - yield x - raise StopIteration - if len(u) == 1: - for i in basic_orbits[0]: - if af: - yield u[0][i]._array_form - else: - yield u[0][i] - raise StopIteration - - u = list(reversed(u)) - basic_orbits = basic_orbits[::-1] - # stg stack of group elements - stg = [list(range(n))] - posmax = [len(x) for x in u] - n1 = len(posmax) - 1 - pos = [0]*n1 - h = 0 - while 1: - # backtrack when finished iterating over coset - if pos[h] >= posmax[h]: - #count_b += 1 - if h == 0: - raise StopIteration - pos[h] = 0 - h -= 1 - stg.pop() - continue - p = _af_rmul(u[h][basic_orbits[h][pos[h]]]._array_form, stg[-1]) - pos[h] += 1 - stg.append(p) - h += 1 - if h == n1: - if af: - for i in basic_orbits[-1]: - p = _af_rmul(u[-1][i]._array_form, stg[-1]) - yield p - else: - for i in basic_orbits[-1]: - p = _af_rmul(u[-1][i]._array_form, stg[-1]) - p1 = _af_new(p) - yield p1 - stg.pop() - h -= 1 -
    - @property -
    [docs] def generators(self): - """Returns the generators of the group. - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation.print_cyclic = True - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> a = Permutation([0, 2, 1]) - >>> b = Permutation([1, 0, 2]) - >>> G = PermutationGroup([a, b]) - >>> G.generators - [Permutation(1, 2), Permutation(2)(0, 1)] - - """ - return self._generators -
    -
    [docs] def contains(self, g, strict=True): - """Test if permutation ``g`` belong to self, ``G``. - - If ``g`` is an element of ``G`` it can be written as a product - of factors drawn from the cosets of ``G``'s stabilizers. To see - if ``g`` is one of the actual generators defining the group use - ``G.has(g)``. - - If ``strict`` is not True, ``g`` will be resized, if necessary, - to match the size of permutations in ``self``. - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation.print_cyclic = True - >>> from sympy.combinatorics.perm_groups import PermutationGroup - - >>> a = Permutation(1, 2) - >>> b = Permutation(2, 3, 1) - >>> G = PermutationGroup(a, b, degree=5) - >>> G.contains(G[0]) # trivial check - True - >>> elem = Permutation([[2, 3]], size=5) - >>> G.contains(elem) - True - >>> G.contains(Permutation(4)(0, 1, 2, 3)) - False - - If strict is False, a permutation will be resized, if - necessary: - - >>> H = PermutationGroup(Permutation(5)) - >>> H.contains(Permutation(3)) - False - >>> H.contains(Permutation(3), strict=False) - True - - To test if a given permutation is present in the group: - - >>> elem in G.generators - False - >>> G.has(elem) - False - - See Also - ======== - - coset_factor, has, in - - """ - if not isinstance(g, Permutation): - return False - if g.size != self.degree: - if strict: - return False - g = Permutation(g, size=self.degree) - if g in self.generators: - return True - return bool(self.coset_factor(g.array_form, True)) -
    - @property -
    [docs] def is_abelian(self): - """Test if the group is Abelian. - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation.print_cyclic = True - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> a = Permutation([0, 2, 1]) - >>> b = Permutation([1, 0, 2]) - >>> G = PermutationGroup([a, b]) - >>> G.is_abelian - False - >>> a = Permutation([0, 2, 1]) - >>> G = PermutationGroup([a]) - >>> G.is_abelian - True - - """ - if self._is_abelian is not None: - return self._is_abelian - - self._is_abelian = True - gens = [p._array_form for p in self.generators] - for x in gens: - for y in gens: - if y <= x: - continue - if not _af_commutes_with(x, y): - self._is_abelian = False - return False - return True -
    -
    [docs] def is_alt_sym(self, eps=0.05, _random_prec=None): - r"""Monte Carlo test for the symmetric/alternating group for degrees - >= 8. - - More specifically, it is one-sided Monte Carlo with the - answer True (i.e., G is symmetric/alternating) guaranteed to be - correct, and the answer False being incorrect with probability eps. - - Notes - ===== - - The algorithm itself uses some nontrivial results from group theory and - number theory: - 1) If a transitive group ``G`` of degree ``n`` contains an element - with a cycle of length ``n/2 < p < n-2`` for ``p`` a prime, ``G`` is the - symmetric or alternating group ([1], pp. 81-82) - 2) The proportion of elements in the symmetric/alternating group having - the property described in 1) is approximately ``\log(2)/\log(n)`` - ([1], p.82; [2], pp. 226-227). - The helper function ``_check_cycles_alt_sym`` is used to - go over the cycles in a permutation and look for ones satisfying 1). - - Examples - ======== - - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> from sympy.combinatorics.named_groups import DihedralGroup - >>> D = DihedralGroup(10) - >>> D.is_alt_sym() - False - - See Also - ======== - - _check_cycles_alt_sym - - """ - if _random_prec is None: - n = self.degree - if n < 8: - return False - if not self.is_transitive(): - return False - if n < 17: - c_n = 0.34 - else: - c_n = 0.57 - d_n = (c_n*log(2))/log(n) - N_eps = int(-log(eps)/d_n) - for i in range(N_eps): - perm = self.random_pr() - if _check_cycles_alt_sym(perm): - return True - return False - else: - for i in range(_random_prec['N_eps']): - perm = _random_prec[i] - if _check_cycles_alt_sym(perm): - return True - return False -
    - @property -
    [docs] def is_nilpotent(self): - """Test if the group is nilpotent. - - A group ``G`` is nilpotent if it has a central series of finite length. - Alternatively, ``G`` is nilpotent if its lower central series terminates - with the trivial group. Every nilpotent group is also solvable - ([1], p.29, [12]). - - Examples - ======== - - >>> from sympy.combinatorics.named_groups import (SymmetricGroup, - ... CyclicGroup) - >>> C = CyclicGroup(6) - >>> C.is_nilpotent - True - >>> S = SymmetricGroup(5) - >>> S.is_nilpotent - False - - See Also - ======== - - lower_central_series, is_solvable - - """ - if self._is_nilpotent is None: - lcs = self.lower_central_series() - terminator = lcs[len(lcs) - 1] - gens = terminator.generators - degree = self.degree - identity = _af_new(list(range(degree))) - if all(g == identity for g in gens): - self._is_solvable = True - self._is_nilpotent = True - return True - else: - self._is_nilpotent = False - return False - else: - return self._is_nilpotent -
    -
    [docs] def is_normal(self, gr): - """Test if G=self is a normal subgroup of gr. - - G is normal in gr if - for each g2 in G, g1 in gr, g = g1*g2*g1**-1 belongs to G - It is sufficient to check this for each g1 in gr.generator and - g2 g2 in G.generator - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation.print_cyclic = True - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> a = Permutation([1, 2, 0]) - >>> b = Permutation([1, 0, 2]) - >>> G = PermutationGroup([a, b]) - >>> G1 = PermutationGroup([a, Permutation([2, 0, 1])]) - >>> G1.is_normal(G) - True - - """ - gens2 = [p._array_form for p in self.generators] - gens1 = [p._array_form for p in gr.generators] - for g1 in gens1: - for g2 in gens2: - p = _af_rmuln(g1, g2, _af_invert(g1)) - if not self.coset_factor(p, True): - return False - return True -
    -
    [docs] def is_primitive(self, randomized=True): - """Test if a group is primitive. - - A permutation group ``G`` acting on a set ``S`` is called primitive if - ``S`` contains no nontrivial block under the action of ``G`` - (a block is nontrivial if its cardinality is more than ``1``). - - Notes - ===== - - The algorithm is described in [1], p.83, and uses the function - minimal_block to search for blocks of the form ``\{0, k\}`` for ``k`` - ranging over representatives for the orbits of ``G_0``, the stabilizer of - ``0``. This algorithm has complexity ``O(n^2)`` where ``n`` is the degree - of the group, and will perform badly if ``G_0`` is small. - - There are two implementations offered: one finds ``G_0`` - deterministically using the function ``stabilizer``, and the other - (default) produces random elements of ``G_0`` using ``random_stab``, - hoping that they generate a subgroup of ``G_0`` with not too many more - orbits than G_0 (this is suggested in [1], p.83). Behavior is changed - by the ``randomized`` flag. - - Examples - ======== - - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> from sympy.combinatorics.named_groups import DihedralGroup - >>> D = DihedralGroup(10) - >>> D.is_primitive() - False - - See Also - ======== - - minimal_block, random_stab - - """ - if self._is_primitive is not None: - return self._is_primitive - n = self.degree - if randomized: - random_stab_gens = [] - v = self.schreier_vector(0) - for i in range(len(self)): - random_stab_gens.append(self.random_stab(0, v)) - stab = PermutationGroup(random_stab_gens) - else: - stab = self.stabilizer(0) - orbits = stab.orbits() - for orb in orbits: - x = orb.pop() - if x != 0 and self.minimal_block([0, x]) != [0]*n: - self._is_primitive = False - return False - self._is_primitive = True - return True -
    - @property -
    [docs] def is_solvable(self): - """Test if the group is solvable. - - ``G`` is solvable if its derived series terminates with the trivial - group ([1], p.29). - - Examples - ======== - - >>> from sympy.combinatorics.named_groups import SymmetricGroup - >>> S = SymmetricGroup(3) - >>> S.is_solvable - True - - See Also - ======== - - is_nilpotent, derived_series - - """ - if self._is_solvable is None: - ds = self.derived_series() - terminator = ds[len(ds) - 1] - gens = terminator.generators - degree = self.degree - identity = _af_new(list(range(degree))) - if all(g == identity for g in gens): - self._is_solvable = True - return True - else: - self._is_solvable = False - return False - else: - return self._is_solvable -
    -
    [docs] def is_subgroup(self, G, strict=True): - """Return True if all elements of self belong to G. - - If ``strict`` is False then if ``self``'s degree is smaller - than ``G``'s, the elements will be resized to have the same degree. - - Examples - ======== - - >>> from sympy.combinatorics import Permutation, PermutationGroup - >>> from sympy.combinatorics.named_groups import (SymmetricGroup, - ... CyclicGroup) - - Testing is strict by default: the degree of each group must be the - same: - - >>> p = Permutation(0, 1, 2, 3, 4, 5) - >>> G1 = PermutationGroup([Permutation(0, 1, 2), Permutation(0, 1)]) - >>> G2 = PermutationGroup([Permutation(0, 2), Permutation(0, 1, 2)]) - >>> G3 = PermutationGroup([p, p**2]) - >>> assert G1.order() == G2.order() == G3.order() == 6 - >>> G1.is_subgroup(G2) - True - >>> G1.is_subgroup(G3) - False - >>> G3.is_subgroup(PermutationGroup(G3[1])) - False - >>> G3.is_subgroup(PermutationGroup(G3[0])) - True - - To ignore the size, set ``strict`` to False: - - >>> S3 = SymmetricGroup(3) - >>> S5 = SymmetricGroup(5) - >>> S3.is_subgroup(S5, strict=False) - True - >>> C7 = CyclicGroup(7) - >>> G = S5*C7 - >>> S5.is_subgroup(G, False) - True - >>> C7.is_subgroup(G, 0) - False - """ - if not isinstance(G, PermutationGroup): - return False - if self == G: - return True - if G.order() % self.order() != 0: - return False - if self.degree == G.degree or \ - (self.degree < G.degree and not strict): - gens = self.generators - else: - return False - return all(G.contains(g, strict=strict) for g in gens) -
    -
    [docs] def is_transitive(self, strict=True): - """Test if the group is transitive. - - A group is transitive if it has a single orbit. - - If ``strict`` is False the group is transitive if it has - a single orbit of length different from 1. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> a = Permutation([0, 2, 1, 3]) - >>> b = Permutation([2, 0, 1, 3]) - >>> G1 = PermutationGroup([a, b]) - >>> G1.is_transitive() - False - >>> G1.is_transitive(strict=False) - True - >>> c = Permutation([2, 3, 0, 1]) - >>> G2 = PermutationGroup([a, c]) - >>> G2.is_transitive() - True - >>> d = Permutation([1,0,2,3]) - >>> e = Permutation([0,1,3,2]) - >>> G3 = PermutationGroup([d, e]) - >>> G3.is_transitive() or G3.is_transitive(strict=False) - False - """ - if self._is_transitive: # strict or not, if True then True - return self._is_transitive - if strict: - if self._is_transitive is not None: # we only store strict=True - return self._is_transitive - - ans = len(self.orbit(0)) == self.degree - self._is_transitive = ans - return ans - - got_orb = False - for x in self.orbits(): - if len(x) > 1: - if got_orb: - return False - got_orb = True - return got_orb -
    - @property -
    [docs] def is_trivial(self): - """Test if the group is the trivial group. - - This is true if the group contains only the identity permutation. - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> G = PermutationGroup([Permutation([0, 1, 2])]) - >>> G.is_trivial - True - - """ - if self._is_trivial is None: - self._is_trivial = len(self) == 1 and self[0].is_Identity - return self._is_trivial -
    -
    [docs] def lower_central_series(self): - r"""Return the lower central series for the group. - - The lower central series for a group ``G`` is the series - ``G = G_0 > G_1 > G_2 > \ldots`` where - ``G_k = [G, G_{k-1}]``, i.e. every term after the first is equal to the - commutator of ``G`` and the previous term in ``G1`` ([1], p.29). - - Returns - ======= - - A list of permutation groups in the order - ``G = G_0, G_1, G_2, \ldots`` - - Examples - ======== - - >>> from sympy.combinatorics.named_groups import (AlternatingGroup, - ... DihedralGroup) - >>> A = AlternatingGroup(4) - >>> len(A.lower_central_series()) - 2 - >>> A.lower_central_series()[1].is_subgroup(DihedralGroup(2)) - True - - See Also - ======== - - commutator, derived_series - - """ - res = [self] - current = self - next = self.commutator(self, current) - while not current.is_subgroup(next): - res.append(next) - current = next - next = self.commutator(self, current) - return res -
    - @property -
    [docs] def max_div(self): - """Maximum proper divisor of the degree of a permutation group. - - Notes - ===== - - Obviously, this is the degree divided by its minimal proper divisor - (larger than ``1``, if one exists). As it is guaranteed to be prime, - the ``sieve`` from ``sympy.ntheory`` is used. - This function is also used as an optimization tool for the functions - ``minimal_block`` and ``_union_find_merge``. - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> G = PermutationGroup([Permutation([0,2,1,3])]) - >>> G.max_div - 2 - - See Also - ======== - - minimal_block, _union_find_merge - - """ - if self._max_div is not None: - return self._max_div - n = self.degree - if n == 1: - return 1 - for x in sieve: - if n % x == 0: - d = n//x - self._max_div = d - return d -
    -
    [docs] def minimal_block(self, points): - r"""For a transitive group, finds the block system generated by - ``points``. - - If a group ``G`` acts on a set ``S``, a nonempty subset ``B`` of ``S`` - is called a block under the action of ``G`` if for all ``g`` in ``G`` - we have ``gB = B`` (``g`` fixes ``B``) or ``gB`` and ``B`` have no - common points (``g`` moves ``B`` entirely). ([1], p.23; [6]). - - The distinct translates ``gB`` of a block ``B`` for ``g`` in ``G`` - partition the set ``S`` and this set of translates is known as a block - system. Moreover, we obviously have that all blocks in the partition - have the same size, hence the block size divides ``|S|`` ([1], p.23). - A ``G``-congruence is an equivalence relation ``~`` on the set ``S`` - such that ``a ~ b`` implies ``g(a) ~ g(b)`` for all ``g`` in ``G``. - For a transitive group, the equivalence classes of a ``G``-congruence - and the blocks of a block system are the same thing ([1], p.23). - - The algorithm below checks the group for transitivity, and then finds - the ``G``-congruence generated by the pairs ``(p_0, p_1), (p_0, p_2), - ..., (p_0,p_{k-1})`` which is the same as finding the maximal block - system (i.e., the one with minimum block size) such that - ``p_0, ..., p_{k-1}`` are in the same block ([1], p.83). - - It is an implementation of Atkinson's algorithm, as suggested in [1], - and manipulates an equivalence relation on the set ``S`` using a - union-find data structure. The running time is just above - ``O(|points||S|)``. ([1], pp. 83-87; [7]). - - Examples - ======== - - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> from sympy.combinatorics.named_groups import DihedralGroup - >>> D = DihedralGroup(10) - >>> D.minimal_block([0,5]) - [0, 6, 2, 8, 4, 0, 6, 2, 8, 4] - >>> D.minimal_block([0,1]) - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - - See Also - ======== - - _union_find_rep, _union_find_merge, is_transitive, is_primitive - - """ - if not self.is_transitive(): - return False - n = self.degree - gens = self.generators - # initialize the list of equivalence class representatives - parents = list(range(n)) - ranks = [1]*n - not_rep = [] - k = len(points) - # the block size must divide the degree of the group - if k > self.max_div: - return [0]*n - for i in range(k - 1): - parents[points[i + 1]] = points[0] - not_rep.append(points[i + 1]) - ranks[points[0]] = k - i = 0 - len_not_rep = k - 1 - while i < len_not_rep: - temp = not_rep[i] - i += 1 - for gen in gens: - # find has side effects: performs path compression on the list - # of representatives - delta = self._union_find_rep(temp, parents) - # union has side effects: performs union by rank on the list - # of representatives - temp = self._union_find_merge(gen(temp), gen(delta), ranks, - parents, not_rep) - if temp == -1: - return [0]*n - len_not_rep += temp - for i in range(n): - # force path compression to get the final state of the equivalence - # relation - self._union_find_rep(i, parents) - return parents -
    -
    [docs] def normal_closure(self, other, k=10): - r"""Return the normal closure of a subgroup/set of permutations. - - If ``S`` is a subset of a group ``G``, the normal closure of ``A`` in ``G`` - is defined as the intersection of all normal subgroups of ``G`` that - contain ``A`` ([1], p.14). Alternatively, it is the group generated by - the conjugates ``x^{-1}yx`` for ``x`` a generator of ``G`` and ``y`` a - generator of the subgroup ``\left\langle S\right\rangle`` generated by - ``S`` (for some chosen generating set for ``\left\langle S\right\rangle``) - ([1], p.73). - - Parameters - ========== - - other - a subgroup/list of permutations/single permutation - k - an implementation-specific parameter that determines the number - of conjugates that are adjoined to ``other`` at once - - Examples - ======== - - >>> from sympy.combinatorics.named_groups import (SymmetricGroup, - ... CyclicGroup, AlternatingGroup) - >>> S = SymmetricGroup(5) - >>> C = CyclicGroup(5) - >>> G = S.normal_closure(C) - >>> G.order() - 60 - >>> G.is_subgroup(AlternatingGroup(5)) - True - - See Also - ======== - - commutator, derived_subgroup, random_pr - - Notes - ===== - - The algorithm is described in [1], pp. 73-74; it makes use of the - generation of random elements for permutation groups by the product - replacement algorithm. - - """ - if hasattr(other, 'generators'): - degree = self.degree - identity = _af_new(list(range(degree))) - - if all(g == identity for g in other.generators): - return other - Z = PermutationGroup(other.generators[:]) - base, strong_gens = Z.schreier_sims_incremental() - strong_gens_distr = _distribute_gens_by_base(base, strong_gens) - basic_orbits, basic_transversals = \ - _orbits_transversals_from_bsgs(base, strong_gens_distr) - - self._random_pr_init(r=10, n=20) - - _loop = True - while _loop: - Z._random_pr_init(r=10, n=10) - for i in range(k): - g = self.random_pr() - h = Z.random_pr() - conj = h^g - res = _strip(conj, base, basic_orbits, basic_transversals) - if res[0] != identity or res[1] != len(base) + 1: - gens = Z.generators - gens.append(conj) - Z = PermutationGroup(gens) - strong_gens.append(conj) - temp_base, temp_strong_gens = \ - Z.schreier_sims_incremental(base, strong_gens) - base, strong_gens = temp_base, temp_strong_gens - strong_gens_distr = \ - _distribute_gens_by_base(base, strong_gens) - basic_orbits, basic_transversals = \ - _orbits_transversals_from_bsgs(base, - strong_gens_distr) - _loop = False - for g in self.generators: - for h in Z.generators: - conj = h^g - res = _strip(conj, base, basic_orbits, - basic_transversals) - if res[0] != identity or res[1] != len(base) + 1: - _loop = True - break - if _loop: - break - return Z - elif hasattr(other, '__getitem__'): - return self.normal_closure(PermutationGroup(other)) - elif hasattr(other, 'array_form'): - return self.normal_closure(PermutationGroup([other])) -
    -
    [docs] def orbit(self, alpha, action='tuples'): - r"""Compute the orbit of alpha ``\{g(\alpha) | g \in G\}`` as a set. - - The time complexity of the algorithm used here is ``O(|Orb|*r)`` where - ``|Orb|`` is the size of the orbit and ``r`` is the number of generators of - the group. For a more detailed analysis, see [1], p.78, [2], pp. 19-21. - Here alpha can be a single point, or a list of points. - - If alpha is a single point, the ordinary orbit is computed. - if alpha is a list of points, there are three available options: - - 'union' - computes the union of the orbits of the points in the list - 'tuples' - computes the orbit of the list interpreted as an ordered - tuple under the group action ( i.e., g((1,2,3)) = (g(1), g(2), g(3)) ) - 'sets' - computes the orbit of the list interpreted as a sets - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> a = Permutation([1,2,0,4,5,6,3]) - >>> G = PermutationGroup([a]) - >>> G.orbit(0) - set([0, 1, 2]) - >>> G.orbit([0,4], 'union') - set([0, 1, 2, 3, 4, 5, 6]) - - See Also - ======== - - orbit_transversal - - """ - return _orbit(self.degree, self.generators, alpha, action) -
    -
    [docs] def orbit_rep(self, alpha, beta, schreier_vector=None): - """Return a group element which sends ``alpha`` to ``beta``. - - If ``beta`` is not in the orbit of ``alpha``, the function returns - ``False``. This implementation makes use of the schreier vector. - For a proof of correctness, see [1], p.80 - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation.print_cyclic = True - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> from sympy.combinatorics.named_groups import AlternatingGroup - >>> G = AlternatingGroup(5) - >>> G.orbit_rep(0, 4) - Permutation(0, 4, 1, 2, 3) - - See Also - ======== - - schreier_vector - - """ - if schreier_vector is None: - schreier_vector = self.schreier_vector(alpha) - if schreier_vector[beta] is None: - return False - k = schreier_vector[beta] - gens = [x._array_form for x in self.generators] - a = [] - while k != -1: - a.append(gens[k]) - beta = gens[k].index(beta) # beta = (~gens[k])(beta) - k = schreier_vector[beta] - if a: - return _af_new(_af_rmuln(*a)) - else: - return _af_new(list(range(self._degree))) -
    -
    [docs] def orbit_transversal(self, alpha, pairs=False): - r"""Computes a transversal for the orbit of ``alpha`` as a set. - - For a permutation group ``G``, a transversal for the orbit - ``Orb = \{g(\alpha) | g \in G\}`` is a set - ``\{g_\beta | g_\beta(\alpha) = \beta\}`` for ``\beta \in Orb``. - Note that there may be more than one possible transversal. - If ``pairs`` is set to ``True``, it returns the list of pairs - ``(\beta, g_\beta)``. For a proof of correctness, see [1], p.79 - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation.print_cyclic = True - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> from sympy.combinatorics.named_groups import DihedralGroup - >>> G = DihedralGroup(6) - >>> G.orbit_transversal(0) - [Permutation(5), - Permutation(0, 1, 2, 3, 4, 5), - Permutation(0, 5)(1, 4)(2, 3), - Permutation(0, 2, 4)(1, 3, 5), - Permutation(5)(0, 4)(1, 3), - Permutation(0, 3)(1, 4)(2, 5)] - - See Also - ======== - - orbit - - """ - return _orbit_transversal(self._degree, self.generators, alpha, pairs) -
    -
    [docs] def orbits(self, rep=False): - """Return the orbits of self, ordered according to lowest element - in each orbit. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> a = Permutation(1,5)(2,3)(4,0,6) - >>> b = Permutation(1,5)(3,4)(2,6,0) - >>> G = PermutationGroup([a, b]) - >>> G.orbits() - [set([0, 2, 3, 4, 6]), set([1, 5])] - """ - return _orbits(self._degree, self._generators) -
    -
    [docs] def order(self): - """Return the order of the group: the number of permutations that - can be generated from elements of the group. - - The number of permutations comprising the group is given by - len(group); the length of each permutation in the group is - given by group.size. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> from sympy.combinatorics.perm_groups import PermutationGroup - - >>> a = Permutation([1, 0, 2]) - >>> G = PermutationGroup([a]) - >>> G.degree - 3 - >>> len(G) - 1 - >>> G.order() - 2 - >>> list(G.generate()) - [Permutation(2), Permutation(2)(0, 1)] - - >>> a = Permutation([0, 2, 1]) - >>> b = Permutation([1, 0, 2]) - >>> G = PermutationGroup([a, b]) - >>> G.order() - 6 - - See Also - ======== - - degree - """ - if self._order != None: - return self._order - if self._is_sym: - n = self._degree - self._order = factorial(n) - return self._order - if self._is_alt: - n = self._degree - self._order = factorial(n)/2 - return self._order - - basic_transversals = self.basic_transversals - m = 1 - for x in basic_transversals: - m *= len(x) - self._order = m - return m -
    -
    [docs] def pointwise_stabilizer(self, points, incremental=True): - r"""Return the pointwise stabilizer for a set of points. - - For a permutation group ``G`` and a set of points - ``\{p_1, p_2,\ldots, p_k\}``, the pointwise stabilizer of - ``p_1, p_2, \ldots, p_k`` is defined as - ``G_{p_1,\ldots, p_k} = - \{g\in G | g(p_i) = p_i \forall i\in\{1, 2,\ldots,k\}\} ([1],p20). - It is a subgroup of ``G``. - - Examples - ======== - - >>> from sympy.combinatorics.named_groups import SymmetricGroup - >>> S = SymmetricGroup(7) - >>> Stab = S.pointwise_stabilizer([2, 3, 5]) - >>> Stab.is_subgroup(S.stabilizer(2).stabilizer(3).stabilizer(5)) - True - - See Also - ======== - - stabilizer, schreier_sims_incremental - - Notes - ===== - - When incremental == True, - rather than the obvious implementation using successive calls to - .stabilizer(), this uses the incremental Schreier-Sims algorithm - to obtain a base with starting segment - the given points. - - """ - if incremental: - base, strong_gens = self.schreier_sims_incremental(base=points) - stab_gens = [] - degree = self.degree - for gen in strong_gens: - if [gen(point) for point in points] == points: - stab_gens.append(gen) - if not stab_gens: - stab_gens = _af_new(list(range(degree))) - return PermutationGroup(stab_gens) - else: - gens = self._generators - degree = self.degree - for x in points: - gens = _stabilizer(degree, gens, x) - return PermutationGroup(gens) -
    -
    [docs] def make_perm(self, n, seed=None): - """ - Multiply ``n`` randomly selected permutations from - pgroup together, starting with the identity - permutation. If ``n`` is a list of integers, those - integers will be used to select the permutations and they - will be applied in L to R order: make_perm((A, B, C)) will - give CBA(I) where I is the identity permutation. - - ``seed`` is used to set the seed for the random selection - of permutations from pgroup. If this is a list of integers, - the corresponding permutations from pgroup will be selected - in the order give. This is mainly used for testing purposes. - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation.print_cyclic = True - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> a, b = [Permutation([1, 0, 3, 2]), Permutation([1, 3, 0, 2])] - >>> G = PermutationGroup([a, b]) - >>> G.make_perm(1, [0]) - Permutation(0, 1)(2, 3) - >>> G.make_perm(3, [0, 1, 0]) - Permutation(0, 2, 3, 1) - >>> G.make_perm([0, 1, 0]) - Permutation(0, 2, 3, 1) - - See Also - ======== - - random - """ - if is_sequence(n): - if seed is not None: - raise ValueError('If n is a sequence, seed should be None') - n, seed = len(n), n - else: - try: - n = int(n) - except TypeError: - raise ValueError('n must be an integer or a sequence.') - randrange = _randrange(seed) - - # start with the identity permutation - result = Permutation(list(range(self.degree))) - m = len(self) - for i in range(n): - p = self[randrange(m)] - result = rmul(result, p) - return result -
    -
    [docs] def random(self, af=False): - """Return a random group element - """ - rank = randrange(self.order()) - return self.coset_unrank(rank, af) -
    -
    [docs] def random_pr(self, gen_count=11, iterations=50, _random_prec=None): - """Return a random group element using product replacement. - - For the details of the product replacement algorithm, see - ``_random_pr_init`` In ``random_pr`` the actual 'product replacement' - is performed. Notice that if the attribute ``_random_gens`` - is empty, it needs to be initialized by ``_random_pr_init``. - - See Also - ======== - - _random_pr_init - - """ - if self._random_gens == []: - self._random_pr_init(gen_count, iterations) - random_gens = self._random_gens - r = len(random_gens) - 1 - - # handle randomized input for testing purposes - if _random_prec is None: - s = randrange(r) - t = randrange(r - 1) - if t == s: - t = r - 1 - x = choice([1, 2]) - e = choice([-1, 1]) - else: - s = _random_prec['s'] - t = _random_prec['t'] - if t == s: - t = r - 1 - x = _random_prec['x'] - e = _random_prec['e'] - - if x == 1: - random_gens[s] = _af_rmul(random_gens[s], _af_pow(random_gens[t], e)) - random_gens[r] = _af_rmul(random_gens[r], random_gens[s]) - else: - random_gens[s] = _af_rmul(_af_pow(random_gens[t], e), random_gens[s]) - random_gens[r] = _af_rmul(random_gens[s], random_gens[r]) - return _af_new(random_gens[r]) -
    -
    [docs] def random_stab(self, alpha, schreier_vector=None, _random_prec=None): - """Random element from the stabilizer of ``alpha``. - - The schreier vector for ``alpha`` is an optional argument used - for speeding up repeated calls. The algorithm is described in [1], p.81 - - See Also - ======== - - random_pr, orbit_rep - - """ - if schreier_vector is None: - schreier_vector = self.schreier_vector(alpha) - if _random_prec is None: - rand = self.random_pr() - else: - rand = _random_prec['rand'] - beta = rand(alpha) - h = self.orbit_rep(alpha, beta, schreier_vector) - return rmul(~h, rand) -
    -
    [docs] def schreier_sims(self): - """Schreier-Sims algorithm. - - It computes the generators of the chain of stabilizers - G > G_{b_1} > .. > G_{b1,..,b_r} > 1 - in which G_{b_1,..,b_i} stabilizes b_1,..,b_i, - and the corresponding ``s`` cosets. - An element of the group can be written as the product - h_1*..*h_s. - - We use the incremental Schreier-Sims algorithm. - - Examples - ======== - >>> from sympy.combinatorics.permutations import Permutation - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> a = Permutation([0, 2, 1]) - >>> b = Permutation([1, 0, 2]) - >>> G = PermutationGroup([a, b]) - >>> G.schreier_sims() - >>> G.basic_transversals - [{0: Permutation(2)(0, 1), 1: Permutation(2), 2: Permutation(1, 2)}, - {0: Permutation(2), 2: Permutation(0, 2)}] - """ - if self._transversals: - return - base, strong_gens = self.schreier_sims_incremental() - self._base = base - self._strong_gens = strong_gens - if not base: - self._transversals = [] - self._basic_orbits = [] - return - - strong_gens_distr = _distribute_gens_by_base(base, strong_gens) - basic_orbits, transversals = _orbits_transversals_from_bsgs(base,\ - strong_gens_distr) - self._transversals = transversals - self._basic_orbits = [sorted(x) for x in basic_orbits] -
    -
    [docs] def schreier_sims_incremental(self, base=None, gens=None): - """Extend a sequence of points and generating set to a base and strong - generating set. - - Parameters - ========== - - base - The sequence of points to be extended to a base. Optional - parameter with default value ``[]``. - gens - The generating set to be extended to a strong generating set - relative to the base obtained. Optional parameter with default - value ``self.generators``. - - Returns - ======= - - (base, strong_gens) - ``base`` is the base obtained, and ``strong_gens`` is the strong - generating set relative to it. The original parameters ``base``, - ``gens`` remain unchanged. - - Examples - ======== - - >>> from sympy.combinatorics.named_groups import AlternatingGroup - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> from sympy.combinatorics.testutil import _verify_bsgs - >>> A = AlternatingGroup(7) - >>> base = [2, 3] - >>> seq = [2, 3] - >>> base, strong_gens = A.schreier_sims_incremental(base=seq) - >>> _verify_bsgs(A, base, strong_gens) - True - >>> base[:2] - [2, 3] - - Notes - ===== - - This version of the Schreier-Sims algorithm runs in polynomial time. - There are certain assumptions in the implementation - if the trivial - group is provided, ``base`` and ``gens`` are returned immediately, - as any sequence of points is a base for the trivial group. If the - identity is present in the generators ``gens``, it is removed as - it is a redundant generator. - The implementation is described in [1], pp. 90-93. - - See Also - ======== - - schreier_sims, schreier_sims_random - - """ - if base is None: - base = [] - if gens is None: - gens = self.generators[:] - degree = self.degree - id_af = list(range(degree)) - # handle the trivial group - if len(gens) == 1 and gens[0].is_Identity: - return base, gens - # prevent side effects - _base, _gens = base[:], gens[:] - # remove the identity as a generator - _gens = [x for x in _gens if not x.is_Identity] - # make sure no generator fixes all base points - for gen in _gens: - if all(x == gen._array_form[x] for x in _base): - for new in id_af: - if gen._array_form[new] != new: - break - else: - assert None # can this ever happen? - _base.append(new) - # distribute generators according to basic stabilizers - strong_gens_distr = _distribute_gens_by_base(_base, _gens) - # initialize the basic stabilizers, basic orbits and basic transversals - orbs = {} - transversals = {} - base_len = len(_base) - for i in range(base_len): - transversals[i] = dict(_orbit_transversal(degree, strong_gens_distr[i], - _base[i], pairs=True, af=True)) - orbs[i] = list(transversals[i].keys()) - # main loop: amend the stabilizer chain until we have generators - # for all stabilizers - i = base_len - 1 - while i >= 0: - # this flag is used to continue with the main loop from inside - # a nested loop - continue_i = False - # test the generators for being a strong generating set - db = {} - for beta, u_beta in list(transversals[i].items()): - for gen in strong_gens_distr[i]: - gb = gen._array_form[beta] - u1 = transversals[i][gb] - g1 = _af_rmul(gen._array_form, u_beta) - if g1 != u1: - # test if the schreier generator is in the i+1-th - # would-be basic stabilizer - y = True - try: - u1_inv = db[gb] - except KeyError: - u1_inv = db[gb] = _af_invert(u1) - schreier_gen = _af_rmul(u1_inv, g1) - h, j = _strip_af(schreier_gen, _base, orbs, transversals, i) - if j <= base_len: - # new strong generator h at level j - y = False - elif h: - # h fixes all base points - y = False - moved = 0 - while h[moved] == moved: - moved += 1 - _base.append(moved) - base_len += 1 - strong_gens_distr.append([]) - if y is False: - # if a new strong generator is found, update the - # data structures and start over - h = _af_new(h) - for l in range(i + 1, j): - strong_gens_distr[l].append(h) - transversals[l] =\ - dict(_orbit_transversal(degree, strong_gens_distr[l], - _base[l], pairs=True, af=True)) - orbs[l] = list(transversals[l].keys()) - i = j - 1 - # continue main loop using the flag - continue_i = True - if continue_i is True: - break - if continue_i is True: - break - if continue_i is True: - continue - i -= 1 - # build the strong generating set - strong_gens = [] - for gens in strong_gens_distr: - for gen in gens: - if gen not in strong_gens: - strong_gens.append(gen) - return _base, strong_gens -
    -
    [docs] def schreier_sims_random(self, base=None, gens=None, consec_succ=10, - _random_prec=None): - r"""Randomized Schreier-Sims algorithm. - - The randomized Schreier-Sims algorithm takes the sequence ``base`` - and the generating set ``gens``, and extends ``base`` to a base, and - ``gens`` to a strong generating set relative to that base with - probability of a wrong answer at most ``2^{-consec\_succ}``, - provided the random generators are sufficiently random. - - Parameters - ========== - - base - The sequence to be extended to a base. - gens - The generating set to be extended to a strong generating set. - consec_succ - The parameter defining the probability of a wrong answer. - _random_prec - An internal parameter used for testing purposes. - - Returns - ======= - - (base, strong_gens) - ``base`` is the base and ``strong_gens`` is the strong generating - set relative to it. - - Examples - ======== - - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> from sympy.combinatorics.testutil import _verify_bsgs - >>> from sympy.combinatorics.named_groups import SymmetricGroup - >>> S = SymmetricGroup(5) - >>> base, strong_gens = S.schreier_sims_random(consec_succ=5) - >>> _verify_bsgs(S, base, strong_gens) #doctest: +SKIP - True - - Notes - ===== - - The algorithm is described in detail in [1], pp. 97-98. It extends - the orbits ``orbs`` and the permutation groups ``stabs`` to - basic orbits and basic stabilizers for the base and strong generating - set produced in the end. - The idea of the extension process - is to "sift" random group elements through the stabilizer chain - and amend the stabilizers/orbits along the way when a sift - is not successful. - The helper function ``_strip`` is used to attempt - to decompose a random group element according to the current - state of the stabilizer chain and report whether the element was - fully decomposed (successful sift) or not (unsuccessful sift). In - the latter case, the level at which the sift failed is reported and - used to amend ``stabs``, ``base``, ``gens`` and ``orbs`` accordingly. - The halting condition is for ``consec_succ`` consecutive successful - sifts to pass. This makes sure that the current ``base`` and ``gens`` - form a BSGS with probability at least ``1 - 1/\text{consec\_succ}``. - - See Also - ======== - - schreier_sims - - """ - if base is None: - base = [] - if gens is None: - gens = self.generators - base_len = len(base) - n = self.degree - # make sure no generator fixes all base points - for gen in gens: - if all(gen(x) == x for x in base): - new = 0 - while gen._array_form[new] == new: - new += 1 - base.append(new) - base_len += 1 - # distribute generators according to basic stabilizers - strong_gens_distr = _distribute_gens_by_base(base, gens) - # initialize the basic stabilizers, basic transversals and basic orbits - transversals = {} - orbs = {} - for i in range(base_len): - transversals[i] = dict(_orbit_transversal(n, strong_gens_distr[i], - base[i], pairs=True)) - orbs[i] = list(transversals[i].keys()) - # initialize the number of consecutive elements sifted - c = 0 - # start sifting random elements while the number of consecutive sifts - # is less than consec_succ - while c < consec_succ: - if _random_prec is None: - g = self.random_pr() - else: - g = _random_prec['g'].pop() - h, j = _strip(g, base, orbs, transversals) - y = True - # determine whether a new base point is needed - if j <= base_len: - y = False - elif not h.is_Identity: - y = False - moved = 0 - while h(moved) == moved: - moved += 1 - base.append(moved) - base_len += 1 - strong_gens_distr.append([]) - # if the element doesn't sift, amend the strong generators and - # associated stabilizers and orbits - if y is False: - for l in range(1, j): - strong_gens_distr[l].append(h) - transversals[l] = dict(_orbit_transversal(n, - strong_gens_distr[l], base[l], pairs=True)) - orbs[l] = list(transversals[l].keys()) - c = 0 - else: - c += 1 - # build the strong generating set - strong_gens = strong_gens_distr[0][:] - for gen in strong_gens_distr[1]: - if gen not in strong_gens: - strong_gens.append(gen) - return base, strong_gens -
    -
    [docs] def schreier_vector(self, alpha): - """Computes the schreier vector for ``alpha``. - - The Schreier vector efficiently stores information - about the orbit of ``alpha``. It can later be used to quickly obtain - elements of the group that send ``alpha`` to a particular element - in the orbit. Notice that the Schreier vector depends on the order - in which the group generators are listed. For a definition, see [3]. - Since list indices start from zero, we adopt the convention to use - "None" instead of 0 to signify that an element doesn't belong - to the orbit. - For the algorithm and its correctness, see [2], pp.78-80. - - Examples - ======== - - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> from sympy.combinatorics.permutations import Permutation - >>> a = Permutation([2,4,6,3,1,5,0]) - >>> b = Permutation([0,1,3,5,4,6,2]) - >>> G = PermutationGroup([a,b]) - >>> G.schreier_vector(0) - [-1, None, 0, 1, None, 1, 0] - - See Also - ======== - - orbit - - """ - n = self.degree - v = [None]*n - v[alpha] = -1 - orb = [alpha] - used = [False]*n - used[alpha] = True - gens = self.generators - r = len(gens) - for b in orb: - for i in range(r): - temp = gens[i]._array_form[b] - if used[temp] is False: - orb.append(temp) - used[temp] = True - v[temp] = i - return v -
    -
    [docs] def stabilizer(self, alpha): - r"""Return the stabilizer subgroup of ``alpha``. - - The stabilizer of ``\alpha`` is the group ``G_\alpha = - \{g \in G | g(\alpha) = \alpha\}``. - For a proof of correctness, see [1], p.79. - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation.print_cyclic = True - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> from sympy.combinatorics.named_groups import DihedralGroup - >>> G = DihedralGroup(6) - >>> G.stabilizer(5) - PermutationGroup([ - Permutation(5)(0, 4)(1, 3), - Permutation(5)]) - - See Also - ======== - - orbit - - """ - return PermGroup(_stabilizer(self._degree, self._generators, alpha)) -
    - @property -
    [docs] def strong_gens(self): - """Return a strong generating set from the Schreier-Sims algorithm. - - A generating set ``S = \{g_1, g_2, ..., g_t\}`` for a permutation group - ``G`` is a strong generating set relative to the sequence of points - (referred to as a "base") ``(b_1, b_2, ..., b_k)`` if, for - ``1 \leq i \leq k`` we have that the intersection of the pointwise - stabilizer ``G^{(i+1)} := G_{b_1, b_2, ..., b_i}`` with ``S`` generates - the pointwise stabilizer ``G^{(i+1)}``. The concepts of a base and - strong generating set and their applications are discussed in depth - in [1], pp. 87-89 and [2], pp. 55-57. - - Examples - ======== - - >>> from sympy.combinatorics.named_groups import DihedralGroup - >>> D = DihedralGroup(4) - >>> D.strong_gens - [Permutation(0, 1, 2, 3), Permutation(0, 3)(1, 2), Permutation(1, 3)] - >>> D.base - [0, 1] - - See Also - ======== - - base, basic_transversals, basic_orbits, basic_stabilizers - - """ - if self._strong_gens == []: - self.schreier_sims() - return self._strong_gens -
    - - @property -
    [docs] def transitivity_degree(self): - """Compute the degree of transitivity of the group. - - A permutation group ``G`` acting on ``\Omega = \{0, 1, ..., n-1\}`` is - ``k``-fold transitive, if, for any k points - ``(a_1, a_2, ..., a_k)\in\Omega`` and any k points - ``(b_1, b_2, ..., b_k)\in\Omega`` there exists ``g\in G`` such that - ``g(a_1)=b_1, g(a_2)=b_2, ..., g(a_k)=b_k`` - The degree of transitivity of ``G`` is the maximum ``k`` such that - ``G`` is ``k``-fold transitive. ([8]) - - Examples - ======== - - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> from sympy.combinatorics.permutations import Permutation - >>> a = Permutation([1, 2, 0]) - >>> b = Permutation([1, 0, 2]) - >>> G = PermutationGroup([a,b]) - >>> G.transitivity_degree - 3 - - See Also - ======== - is_transitive, orbit - - """ - if self._transitivity_degree is None: - n = self.degree - G = self - # if G is k-transitive, a tuple (a_0,..,a_k) - # can be brought to (b_0,...,b_(k-1), b_k) - # where b_0,...,b_(k-1) are fixed points; - # consider the group G_k which stabilizes b_0,...,b_(k-1) - # if G_k is transitive on the subset excluding b_0,...,b_(k-1) - # then G is (k+1)-transitive - for i in range(n): - orb = G.orbit((i)) - if len(orb) != n - i: - self._transitivity_degree = i - return i - G = G.stabilizer(i) - self._transitivity_degree = n - return n - else: - return self._transitivity_degree -
    -
    [docs] def is_group(self): - """Return True if the group meets three criteria: identity is present, - the inverse of every element is also an element, and the product of - any two elements is also an element. If any of the tests fail, False - is returned. - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation.print_cyclic = True - >>> from sympy.combinatorics import PermutationGroup - >>> from sympy.combinatorics.polyhedron import tetrahedron - - The permutation group given in the tetrahedron object is not - a true group: - - >>> G = tetrahedron.pgroup - >>> G.is_group() - False - - But the group generated by the permutations in the tetrahedron - pgroup is a proper group: - - >>> H = PermutationGroup(list(G.generate())) - >>> H.is_group() - True - - The identity permutation is present: - - >>> H.has(Permutation(G.degree - 1)) - True - - The product of any two elements from the group is also in the group: - - >>> from sympy import TableForm - >>> g = list(H) - >>> n = len(g) - >>> m = [] - >>> for i in g: - ... m.append([g.index(i*H) for H in g]) - ... - >>> TableForm(m, headings=[list(range(n)), list(range(n))], wipe_zeros=False) - | 0 1 2 3 4 5 6 7 8 9 10 11 - ---------------------------------------- - 0 | 11 0 8 10 6 2 7 4 5 3 9 1 - 1 | 0 1 2 3 4 5 6 7 8 9 10 11 - 2 | 6 2 7 4 5 3 9 1 11 0 8 10 - 3 | 5 3 9 1 11 0 8 10 6 2 7 4 - 4 | 3 4 0 2 10 6 11 8 9 7 1 5 - 5 | 4 5 6 7 8 9 10 11 0 1 2 3 - 6 | 10 6 11 8 9 7 1 5 3 4 0 2 - 7 | 9 7 1 5 3 4 0 2 10 6 11 8 - 8 | 7 8 4 6 2 10 3 0 1 11 5 9 - 9 | 8 9 10 11 0 1 2 3 4 5 6 7 - 10 | 2 10 3 0 1 11 5 9 7 8 4 6 - 11 | 1 11 5 9 7 8 4 6 2 10 3 0 - >>> - The entries in the table give the element in the group corresponding - to the product of a given column element and row element: - - >>> g[3]*g[2] == g[9] - True - - The inverse of every element is also in the group: - - >>> TableForm([[g.index(~gi) for gi in g]], headings=[[], list(range(n))], - ... wipe_zeros=False) - 0 1 2 3 4 5 6 7 8 9 10 11 - --------------------------- - 11 1 7 3 10 9 6 2 8 5 4 0 - - So we see that g[1] and g[3] are equivalent to their inverse while - g[7] == ~g[2]. - """ - # identity present - I = Permutation(size=self.degree) - for g in self: - if g == I: - break - else: - return False - - # associativity already holds: a*(b*c) == (a*b)*c for permutations - - # inverse of each is present - if not all(self.has(~a) for a in self): - return False - - # closure - for a in self: - for b in self: - if not self.has(a*b): - return False - return True -
    -def _orbit(degree, generators, alpha, action='tuples'): - r"""Compute the orbit of alpha ``\{g(\alpha) | g \in G\}`` as a set. - - The time complexity of the algorithm used here is ``O(|Orb|*r)`` where - ``|Orb|`` is the size of the orbit and ``r`` is the number of generators of - the group. For a more detailed analysis, see [1], p.78, [2], pp. 19-21. - Here alpha can be a single point, or a list of points. - - If alpha is a single point, the ordinary orbit is computed. - if alpha is a list of points, there are three available options: - - 'union' - computes the union of the orbits of the points in the list - 'tuples' - computes the orbit of the list interpreted as an ordered - tuple under the group action ( i.e., g((1,2,3)) = (g(1), g(2), g(3)) ) - 'sets' - computes the orbit of the list interpreted as a sets - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> from sympy.combinatorics.perm_groups import PermutationGroup, _orbit - >>> a = Permutation([1,2,0,4,5,6,3]) - >>> G = PermutationGroup([a]) - >>> _orbit(G.degree, G.generators, 0) - set([0, 1, 2]) - >>> _orbit(G.degree, G.generators, [0, 4], 'union') - set([0, 1, 2, 3, 4, 5, 6]) - - See Also - ======== - - orbit, orbit_transversal - - """ - if not hasattr(alpha, '__getitem__'): - alpha = [alpha] - - gens = [x._array_form for x in generators] - if len(alpha) == 1 or action == 'union': - orb = alpha - used = [False]*degree - for el in alpha: - used[el] = True - for b in orb: - for gen in gens: - temp = gen[b] - if used[temp] == False: - orb.append(temp) - used[temp] = True - return set(orb) - elif action == 'tuples': - alpha = tuple(alpha) - orb = [alpha] - used = set([alpha]) - for b in orb: - for gen in gens: - temp = tuple([gen[x] for x in b]) - if temp not in used: - orb.append(temp) - used.add(temp) - return set(orb) - elif action == 'sets': - alpha = frozenset(alpha) - orb = [alpha] - used = set([alpha]) - for b in orb: - for gen in gens: - temp = frozenset([gen[x] for x in b]) - if temp not in used: - orb.append(temp) - used.add(temp) - return set([tuple(x) for x in orb]) - -def _orbits(degree, generators): - """Compute the orbits of G. - - If rep=False it returns a list of sets else it returns a list of - representatives of the orbits - - Examples - ======== - >>> from sympy.combinatorics.permutations import Permutation - >>> from sympy.combinatorics.perm_groups import PermutationGroup, _orbits - >>> a = Permutation([0, 2, 1]) - >>> b = Permutation([1, 0, 2]) - >>> _orbits(a.size, [a, b]) - [set([0, 1, 2])] - """ - - seen = set() # elements that have already appeared in orbits - orbs = [] - sorted_I = list(range(degree)) - I = set(sorted_I) - while I: - i = sorted_I[0] - orb = _orbit(degree, generators, i) - orbs.append(orb) - # remove all indices that are in this orbit - I -= orb - sorted_I = [i for i in sorted_I if i not in orb] - return orbs - -def _orbit_transversal(degree, generators, alpha, pairs, af=False): - r"""Computes a transversal for the orbit of ``alpha`` as a set. - - generators generators of the group ``G`` - - For a permutation group ``G``, a transversal for the orbit - ``Orb = \{g(\alpha) | g \in G\}`` is a set - ``\{g_\beta | g_\beta(\alpha) = \beta\}`` for ``\beta \in Orb``. - Note that there may be more than one possible transversal. - If ``pairs`` is set to ``True``, it returns the list of pairs - ``(\beta, g_\beta)``. For a proof of correctness, see [1], p.79 - - if af is True, the transversal elements are given in array form - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation.print_cyclic = True - >>> from sympy.combinatorics.named_groups import DihedralGroup - >>> from sympy.combinatorics.perm_groups import _orbit_transversal - >>> G = DihedralGroup(6) - >>> _orbit_transversal(G.degree, G.generators, 0, False) - [Permutation(5), - Permutation(0, 1, 2, 3, 4, 5), - Permutation(0, 5)(1, 4)(2, 3), - Permutation(0, 2, 4)(1, 3, 5), - Permutation(5)(0, 4)(1, 3), - Permutation(0, 3)(1, 4)(2, 5)] - """ - - tr = [(alpha, list(range(degree)))] - used = [False]*degree - used[alpha] = True - gens = [x._array_form for x in generators] - for x, px in tr: - for gen in gens: - temp = gen[x] - if used[temp] == False: - tr.append((temp, _af_rmul(gen, px))) - used[temp] = True - if pairs: - if not af: - tr = [(x, _af_new(y)) for x, y in tr] - return tr - - if af: - return [y for _, y in tr] - - return [_af_new(y) for _, y in tr] - -def _stabilizer(degree, generators, alpha): - r"""Return the stabilizer subgroup of ``alpha``. - - The stabilizer of ``\alpha`` is the group ``G_\alpha = - \{g \in G | g(\alpha) = \alpha\}``. - For a proof of correctness, see [1], p.79. - - degree degree of G - generators generators of G - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation.print_cyclic = True - >>> from sympy.combinatorics.perm_groups import _stabilizer - >>> from sympy.combinatorics.named_groups import DihedralGroup - >>> G = DihedralGroup(6) - >>> _stabilizer(G.degree, G.generators, 5) - [Permutation(5)(0, 4)(1, 3), Permutation(5)] - - See Also - ======== - - orbit - - """ - orb = [alpha] - table = {alpha: list(range(degree))} - table_inv = {alpha: list(range(degree))} - used = [False]*degree - used[alpha] = True - gens = [x._array_form for x in generators] - stab_gens = [] - for b in orb: - for gen in gens: - temp = gen[b] - if used[temp] is False: - gen_temp = _af_rmul(gen, table[b]) - orb.append(temp) - table[temp] = gen_temp - table_inv[temp] = _af_invert(gen_temp) - used[temp] = True - else: - schreier_gen = _af_rmuln(table_inv[temp], gen, table[b]) - if schreier_gen not in stab_gens: - stab_gens.append(schreier_gen) - return [_af_new(x) for x in stab_gens] - -PermGroup = PermutationGroup -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/combinatorics/permutations.html b/dev-py3k/_modules/sympy/combinatorics/permutations.html deleted file mode 100644 index 3d2ca04c03c..00000000000 --- a/dev-py3k/_modules/sympy/combinatorics/permutations.html +++ /dev/null @@ -1,2885 +0,0 @@ - - - - - - - - - - sympy.combinatorics.permutations — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.combinatorics.permutations

    -import random
    -from collections import defaultdict
    -
    -from sympy.core import Basic
    -from sympy.core.compatibility import is_sequence
    -from sympy.utilities.iterables import (flatten, has_variety, minlex,
    -    has_dups, runs)
    -from sympy.polys.polytools import lcm
    -from sympy.matrices import zeros
    -from sympy.mpmath.libmp.libintmath import ifac
    -from functools import reduce
    -
    -
    -def _af_rmul(a, b):
    -    """
    -    Return the product b*a; input and output are array forms. The ith value
    -    is a[b[i]].
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.combinatorics.permutations import _af_rmul, Permutation
    -    >>> Permutation.print_cyclic = False
    -
    -    >>> a, b = [1, 0, 2], [0, 2, 1]
    -    >>> _af_rmul(a, b)
    -    [1, 2, 0]
    -    >>> [a[b[i]] for i in range(3)]
    -    [1, 2, 0]
    -
    -    This handles the operands in reverse order compared to the ``*`` operator:
    -
    -    >>> a = Permutation(a); b = Permutation(b)
    -    >>> list(a*b)
    -    [2, 0, 1]
    -    >>> [b(a(i)) for i in range(3)]
    -    [2, 0, 1]
    -
    -    See Also
    -    ========
    -    rmul, _af_rmuln
    -    """
    -    return [a[i] for i in b]
    -
    -
    -def _af_rmuln(*abc):
    -    """
    -    Given [a, b, c, ...] return the product of ...*c*b*a using array forms.
    -    The ith value is a[b[c[i]]].
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.combinatorics.permutations import _af_rmul, Permutation
    -    >>> Permutation.print_cyclic = False
    -
    -    >>> a, b = [1, 0, 2], [0, 2, 1]
    -    >>> _af_rmul(a, b)
    -    [1, 2, 0]
    -    >>> [a[b[i]] for i in range(3)]
    -    [1, 2, 0]
    -
    -    This handles the operands in reverse order compared to the ``*`` operator:
    -
    -    >>> a = Permutation(a); b = Permutation(b)
    -    >>> list(a*b)
    -    [2, 0, 1]
    -    >>> [b(a(i)) for i in range(3)]
    -    [2, 0, 1]
    -
    -    See Also
    -    ========
    -    rmul, _af_rmul
    -    """
    -    a = abc
    -    m = len(a)
    -    if m == 3:
    -        p0, p1, p2 = a
    -        return [p0[p1[i]] for i in p2]
    -    if m == 4:
    -        p0, p1, p2, p3 = a
    -        return [p0[p1[p2[i]]] for i in p3]
    -    if m == 5:
    -        p0, p1, p2, p3, p4 = a
    -        return [p0[p1[p2[p3[i]]]] for i in p4]
    -    if m == 6:
    -        p0, p1, p2, p3, p4, p5 = a
    -        return [p0[p1[p2[p3[p4[i]]]]] for i in p5]
    -    if m == 7:
    -        p0, p1, p2, p3, p4, p5, p6 = a
    -        return [p0[p1[p2[p3[p4[p5[i]]]]]] for i in p6]
    -    if m == 8:
    -        p0, p1, p2, p3, p4, p5, p6, p7 = a
    -        return [p0[p1[p2[p3[p4[p5[p6[i]]]]]]] for i in p7]
    -    if m == 1:
    -        return a[0][:]
    -    if m == 2:
    -        a, b = a
    -        return [a[i] for i in b]
    -    assert m != 0
    -    p0 = _af_rmuln(*a[:m//2])
    -    p1 = _af_rmuln(*a[m//2:])
    -    return [p0[i] for i in p1]
    -
    -
    -def _af_parity(pi):
    -    """
    -    Computes the parity of a permutation in array form.
    -
    -    The parity of a permutation reflects the parity of the
    -    number of inversions in the permutation, i.e., the
    -    number of pairs of x and y such that x > y but p[x] < p[y].
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.combinatorics.permutations import _af_parity
    -    >>> _af_parity([0,1,2,3])
    -    0
    -    >>> _af_parity([3,2,0,1])
    -    1
    -
    -    See Also
    -    ========
    -
    -    Permutation
    -    """
    -    n = len(pi)
    -    a = [0] * n
    -    c = 0
    -    for j in range(n):
    -        if a[j] == 0:
    -            c += 1
    -            a[j] = 1
    -            i = j
    -            while pi[i] != j:
    -                i = pi[i]
    -                a[i] = 1
    -    return (n - c) % 2
    -
    -
    -def _af_invert(a):
    -    """
    -    Finds the inverse, ~A, of a permutation, A, given in array form.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.combinatorics.permutations import _af_invert, _af_rmul
    -    >>> A = [1, 2, 0, 3]
    -    >>> _af_invert(A)
    -    [2, 0, 1, 3]
    -    >>> _af_rmul(_, A)
    -    [0, 1, 2, 3]
    -
    -    See Also
    -    ========
    -
    -    Permutation, __invert__
    -    """
    -    inv_form = [0] * len(a)
    -    for i, ai in enumerate(a):
    -        inv_form[ai] = i
    -    return inv_form
    -
    -def _af_pow(a, n):
    -    """
    -    Routine for finding powers of a permutation.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.combinatorics.permutations import Permutation, _af_pow
    -    >>> Permutation.print_cyclic = False
    -    >>> p = Permutation([2,0,3,1])
    -    >>> p.order()
    -    4
    -    >>> _af_pow(p._array_form, 4)
    -    [0, 1, 2, 3]
    -    """
    -    if n == 0:
    -        return list(range(len(a)))
    -    if n < 0:
    -        return _af_pow(_af_invert(a), -n)
    -    if n == 1:
    -        return a[:]
    -    elif n == 2:
    -        b = [a[i] for i in a]
    -    elif n == 3:
    -        b = [a[a[i]] for i in a]
    -    elif n == 4:
    -        b = [a[a[a[i]]] for i in a]
    -    else:
    -        # use binary multiplication
    -        b = list(range(len(a)))
    -        while 1:
    -            if n & 1:
    -                b = [b[i] for i in a]
    -                n -= 1
    -                if not n:
    -                    break
    -            if n % 4 == 0:
    -                a = [a[a[a[i]]] for i in a]
    -                n = n // 4
    -            elif n % 2 == 0:
    -                a = [a[i] for i in a]
    -                n = n // 2
    -    return b
    -
    -def _af_commutes_with(a, b):
    -    """
    -    Checks if the two permutations with array forms
    -    given by ``a`` and ``b`` commute.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.combinatorics.permutations import _af_commutes_with
    -    >>> _af_commutes_with([1,2,0], [0,2,1])
    -    False
    -
    -    See Also
    -    ========
    -
    -    Permutation, commutes_with
    -    """
    -    return not any(a[b[i]] != b[a[i]] for i in range(len(a) - 1))
    -
    -
    -
    [docs]class Cycle(dict): - """ - Wrapper around dict which provides the functionality of a disjoint cycle. - - A cycle shows the rule to use to move subsets of elements to obtain - a permutation. The Cycle class is more flexible that Permutation in - that 1) all elements need not be present in order to investigate how - multiple cycles act in sequence and 2) it can contain singletons: - - >>> from sympy.combinatorics.permutations import Perm, Cycle - - A Cycle will automatically parse a cycle given as a tuple on the rhs: - - >>> Cycle(1, 2)(2, 3) - Cycle(1, 3, 2) - - The identity cycle, Cycle(), can be used to start a product: - - >>> Cycle()(1, 2)(2,3) - Cycle(1, 3, 2) - - The array form of a Cycle can be obtained by calling the list - method (or passing it to the list function) and all elements from - 0 will be shown: - - >>> a = Cycle(1, 2) - >>> a.list() - [0, 2, 1] - >>> list(a) - [0, 2, 1] - - If a larger (or smaller) range is desired use the list method and - provide the desired size -- but the Cycle cannot be truncated to - a size smaller than the largest element that is out of place: - - >>> b = Cycle(2,4)(1,2)(3,1,4)(1,3) - >>> b.list() - [0, 2, 1, 3, 4] - >>> b.list(b.size + 1) - [0, 2, 1, 3, 4, 5] - >>> b.list(-1) - [0, 2, 1] - - Singletons are not shown when printing with one exception: the largest - element is always shown -- as a singleton if necessary: - - >>> Cycle(1, 4, 10)(4, 5) - Cycle(1, 5, 4, 10) - >>> Cycle(1, 2)(4)(5)(10) - Cycle(1, 2)(10) - - The array form can be used to instantiate a Permutation so other - properties of the permutation can be investigated: - - >>> Perm(Cycle(1,2)(3,4).list()).transpositions() - [(1, 2), (3, 4)] - - Notes - ===== - - The underlying structure of the Cycle is a dictionary and although - the __iter__ method has been redefiend to give the array form of the - cycle, the underlying dictionary items are still available with the - such methods as items(): - - >>> list(Cycle(1, 2).items()) - [(1, 2), (2, 1)] - - See Also - ======== - - Permutation - """ - def __missing__(self, arg): - """Enter arg into dictionary and return arg.""" - self[arg] = arg - return arg - - def __iter__(self): - for i in self.list(): - yield i - - def __call__(self, *other): - """Return product of cycles processed from R to L. - - Examples - ======== - >>> from sympy.combinatorics.permutations import Cycle as C - >>> from sympy.combinatorics.permutations import Permutation as Perm - >>> C(1, 2)(2, 3) - Cycle(1, 3, 2) - - An instance of a Cycle will automatically parse list-like - objects and Permutations that are on the right. It is more - flexible than the Permutation in that all elements need not - be present: - - >>> a = C(1, 2) - >>> a(2, 3) - Cycle(1, 3, 2) - >>> a(2, 3)(4, 5) - Cycle(1, 3, 2)(4, 5) - - """ - rv = Cycle(*other) - for k, v in zip(list(self.keys()), [rv[self[k]] for k in list(self.keys())]): - rv[k] = v - return rv - -
    [docs] def list(self, size=None): - """Return the cycles as an explicit list starting from 0 up - to the greater of the largest value in the cycles and size. - - Truncation of trailing unmoved items will occur when size - is less than the maximum element in the cycle; if this is - desired, setting ``size=-1`` will guarantee such trimming. - - Examples - ======== - >>> from sympy.combinatorics.permutations import Cycle - >>> from sympy.combinatorics.permutations import Permutation - >>> Permutation.print_cyclic = False - >>> p = Cycle(2, 3)(4, 5) - >>> p.list() - [0, 1, 3, 2, 5, 4] - >>> p.list(10) - [0, 1, 3, 2, 5, 4, 6, 7, 8, 9] - - Passing a length too small will trim trailing, unchanged elements - in the permutation: - - >>> Cycle(2, 4)(1, 2, 4).list(-1) - [0, 2, 1] - """ - if not self and size is None: - raise ValueError('must give size for empty Cycle') - if size is not None: - big = max([i for i in list(self.keys()) if self[i] != i]) - size = max(size, big + 1) - else: - size = self.size - return [self[i] for i in range(size)] -
    - def __repr__(self): - """We want it to print as a Cycle, not as a dict. - - Examples - ======== - - >>> from sympy.combinatorics import Cycle - >>> Cycle(1, 2) - Cycle(1, 2) - >>> print(_) - Cycle(1, 2) - >>> list(Cycle(1, 2).items()) - [(1, 2), (2, 1)] - """ - if not self: - return 'Cycle()' - cycles = Permutation(self).cyclic_form - s = ''.join(str(tuple(c)) for c in cycles) - big = self.size - 1 - if not any(i == big for c in cycles for i in c): - s += '(%s)' % big - return 'Cycle%s' % s - - def __init__(self, *args): - """Load up a Cycle instance with the values for the cycle. - - Examples - ======== - >>> from sympy.combinatorics.permutations import Cycle - >>> Cycle(1, 2, 6) - Cycle(1, 2, 6) - """ - - if not args: - return - if len(args) == 1: - if isinstance(args[0], Permutation): - for c in args[0].cyclic_form: - self.update(self(*c)) - return - elif isinstance(args[0], Cycle): - for k, v in args[0].items(): - self[k] = v - return - args = [int(a) for a in args] - if has_dups(args): - raise ValueError('All elements must be unique in a cycle.') - for i in range(-len(args), 0): - self[args[i]] = args[i + 1] - - @property - def size(self): - return max(self.keys()) + 1 - - def copy(self): - return Cycle(self) -
    -
    [docs]class Permutation(Basic): - """ - A permutation, alternatively known as an 'arrangement number' or 'ordering' - is an arrangement of the elements of an ordered list into a one-to-one - mapping with itself. The permutation of a given arrangement is given by - indicating the positions of the elements after re-arrangment [2]_. For - example, if one started with elements [x, y, a, b] (in that order) and - they were reordered as [x, y, b, a] then the permutation would be - [0, 1, 3, 2]. Notice that (in SymPy) the first element is always referred - to as 0 and the permutation uses the indices of the elements in the - original ordering, not the elements (a, b, etc...) themselves. - - >>> from sympy.combinatorics import Permutation - >>> Permutation.print_cyclic = False - - Permutations Notation - ===================== - - Permutations are commonly represented in disjoint cycle or array forms. - - Array Notation and 2-line Form - ------------------------------------ - - In the 2-line form, the elements and their final positions are shown - as a matrix with 2 rows: - - [0 1 2 ... n-1] - [p(0) p(1) p(2) ... p(n-1)] - - Since the first line is always range(n), where n is the size of p, - it is sufficient to represent the permutation by the second line, - referred to as the "array form" of the permutation. This is entered - in brackets as the argument to the Permutation class: - - >>> p = Permutation([0, 2, 1]); p - Permutation([0, 2, 1]) - - Given i in range(p.size), the permutation maps i to i^p - - >>> [i^p for i in range(p.size)] - [0, 2, 1] - - The composite of two permutations p*q means first apply p, then q, so - i^(p*q) = (i^p)^q which is i^p^q according to Python precedence rules: - - >>> q = Permutation([2, 1, 0]) - >>> [i^p^q for i in range(3)] - [2, 0, 1] - >>> [i^(p*q) for i in range(3)] - [2, 0, 1] - - One can use also the notation p(i) = i^p, but then the composition - rule is (p*q)(i) = q(p(i)), not p(q(i)): - - >>> [(p*q)(i) for i in range(p.size)] - [2, 0, 1] - >>> [q(p(i)) for i in range(p.size)] - [2, 0, 1] - >>> [p(q(i)) for i in range(p.size)] - [1, 2, 0] - - Disjoint Cycle Notation - ----------------------- - - In disjoint cycle notation, only the elements that have shifted are - indicated. In the above case, the 2 and 1 switched places. This can - be entered in two ways: - - >>> Permutation(1, 2) == Permutation([[1, 2]]) == p - True - - Only the relative ordering of elements in a cycle matter: - - >>> Permutation(1,2,3) == Permutation(2,3,1) == Permutation(3,1,2) - True - - The disjoint cycle notation is convenient when representing permutations - that have several cycles in them: - - >>> Permutation(1, 2)(3, 5) == Permutation([[1, 2], [3, 5]]) - True - - It also provides some economy in entry when computing products of - permutations that are written in disjoint cycle notation: - - >>> Permutation(1, 2)(1, 3)(2, 3) - Permutation([0, 3, 2, 1]) - >>> _ == Permutation([[1, 2]])*Permutation([[1, 3]])*Permutation([[2, 3]]) - True - - Entering a singleton in a permutation is a way to indicate the size of the - permutation. The ``size`` keyword can also be used. - - Array-form entry: - - >>> Permutation([[1, 2], [9]]) - Permutation([0, 2, 1], size=10) - >>> Permutation([[1, 2]], size=10) - Permutation([0, 2, 1], size=10) - - Cyclic-form entry: - - >>> Permutation(1, 2, size=10) - Permutation([0, 2, 1], size=10) - >>> Permutation(9)(1, 2) - Permutation([0, 2, 1], size=10) - - Caution: no singleton containing an element larger than the largest - in any previous cycle can be entered. This is an important difference - in how Permutation and Cycle handle the __call__ syntax. A singleton - argument at the start of a Permutation performs instantiation of the - Permutation and is permitted: - - >>> Permutation(5) - Permutation([], size=6) - - A singleton entered after instantiation is a call to the permutation - -- a function call -- and if the argument is out of range it will - trigger an error. For this reason, it is better to start the cycle - with the singleton: - - The following fails because there is is no element 3: - - >>> Permutation(1, 2)(3) - Traceback (most recent call last): - ... - IndexError: list index out of range - - This is ok: only the call to an out of range singleton is prohibited; - otherwise the permutation autosizes: - - >>> Permutation(3)(1, 2) - Permutation([0, 2, 1, 3]) - >>> Permutation(1, 2)(3, 4) == Permutation(3, 4)(1, 2) - True - - - Equality testing - ---------------- - - The array forms must be the same in order for permutations to be equal: - - >>> Permutation([1, 0, 2, 3]) == Permutation([1, 0]) - False - - - Identity Permutation - -------------------- - - The identity permutation is a permutation in which no element is out of - place. It can be entered in a variety of ways. All the following create - an identity permutation of size 4: - - >>> I = Permutation([0, 1, 2, 3]) - >>> all(p == I for p in [ - ... Permutation(3), - ... Permutation(list(range(4))), - ... Permutation([], size=4), - ... Permutation(size=4)]) - True - - Watch out for entering the range *inside* a set of brackets (which is - cycle notation): - - >>> I == Permutation([list(range(4))]) - False - - - Permutation Printing - ==================== - - There are a few things to note about how Permutations are printed. - - 1) If you prefer one form (array or cycle) over another, you can set that - with the print_cyclic flag. - - >>> Permutation(1, 2)(4, 5)(3, 4) - Permutation([0, 2, 1, 4, 5, 3]) - >>> p = _ - - >>> Permutation.print_cyclic = True - >>> p - Permutation(1, 2)(3, 4, 5) - >>> Permutation.print_cyclic = False - - 2) Regardless of the setting, a list of elements in the array for cyclic - form can be obtained and either of those can be copied and supplied as - the argument to Permutation: - - >>> p.array_form - [0, 2, 1, 4, 5, 3] - >>> p.cyclic_form - [[1, 2], [3, 4, 5]] - >>> Permutation(_) == p - True - - 3) Printing is economical in that as little as possible is printed while - retaining all information about the size of the permutation: - - >>> Permutation([1, 0, 2, 3]) - Permutation([1, 0, 2, 3]) - >>> Permutation([1, 0, 2, 3], size=20) - Permutation([1, 0], size=20) - >>> Permutation([1, 0, 2, 4, 3, 5, 6], size=20) - Permutation([1, 0, 2, 4, 3], size=20) - - >>> p = Permutation([1, 0, 2, 3]) - >>> Permutation.print_cyclic = True - >>> p - Permutation(3)(0, 1) - >>> Permutation.print_cyclic = False - - The 2 was not printed but it is still there as can be seen with the - array_form and size methods: - - >>> p.array_form - [1, 0, 2, 3] - >>> p.size - 4 - - Short introduction to other methods - =================================== - - The permutation can act as a bijective function, telling what element is - located at a given position - - >>> q = Permutation([5, 2, 3, 4, 1, 0]) - >>> q.array_form[1] # the hard way - 2 - >>> q(1) # the easy way - 2 - >>> dict([(i, q(i)) for i in range(q.size)]) # showing the bijection - {0: 5, 1: 2, 2: 3, 3: 4, 4: 1, 5: 0} - - The full cyclic form (including singletons) can be obtained: - - >>> p.full_cyclic_form - [[0, 1], [2], [3]] - - Any permutation can be factored into transpositions of pairs of elements: - - >>> Permutation([[1, 2], [3, 4, 5]]).transpositions() - [(1, 2), (3, 5), (3, 4)] - >>> Permutation.rmul(*[Permutation([ti], size=6) for ti in _]).cyclic_form - [[1, 2], [3, 4, 5]] - - The number of permutations on a set of n elements is given by n! and is - called the cardinality. - - >>> p.size - 4 - >>> p.cardinality - 24 - - A given permutation has a rank among all the possible permutations of the - same elements, but what that rank is depends on how the permutations are - enumerated. (There are a number of different methods of doing so.) The - lexicographic rank is given by the rank method and this rank is used to - increment a partion with addition/subtraction: - - >>> p.rank() - 6 - >>> p + 1 - Permutation([1, 0, 3, 2]) - >>> p.next_lex() - Permutation([1, 0, 3, 2]) - >>> _.rank() - 7 - >>> p.unrank_lex(p.size, rank=7) - Permutation([1, 0, 3, 2]) - - The product of two permutations p and q is defined as their composition as - functions, (p*q)(i) = q(p(i)) [6]_. - - >>> p = Permutation([1, 0, 2, 3]) - >>> q = Permutation([2, 3, 1, 0]) - >>> list(q*p) - [2, 3, 0, 1] - >>> list(p*q) - [3, 2, 1, 0] - >>> [q(p(i)) for i in range(p.size)] - [3, 2, 1, 0] - - The permutation can be 'applied' to any list-like object, not only - Permutations: - - >>> p(['zero', 'one', 'four', 'two']) - ['one', 'zero', 'four', 'two'] - >>> p('zo42') - ['o', 'z', '4', '2'] - - If you have a list of arbitrary elements, the corresponding permutation - can be found with the from_sequence method: - - >>> Permutation.from_sequence('SymPy') - Permutation([1, 3, 2, 0, 4]) - - See Also - ======== - - Cycle - - References - ========== - - .. [1] Skiena, S. 'Permutations.' 1.1 in Implementing Discrete Mathematics - Combinatorics and Graph Theory with Mathematica. Reading, MA: - Addison-Wesley, pp. 3-16, 1990. - - .. [2] Knuth, D. E. The Art of Computer Programming, Vol. 4: Combinatorial - Algorithms, 1st ed. Reading, MA: Addison-Wesley, 2011. - - .. [3] Wendy Myrvold and Frank Ruskey. 2001. Ranking and unranking - permutations in linear time. Inf. Process. Lett. 79, 6 (September 2001), - 281-284. DOI=10.1016/S0020-0190(01)00141-7 - - .. [4] D. L. Kreher, D. R. Stinson 'Combinatorial Algorithms' - CRC Press, 1999 - - .. [5] Graham, R. L.; Knuth, D. E.; and Patashnik, O. - Concrete Mathematics: A Foundation for Computer Science, 2nd ed. - Reading, MA: Addison-Wesley, 1994. - - .. [6] http://en.wikipedia.org/wiki/Permutation#Product_and_inverse - - .. [7] http://en.wikipedia.org/wiki/Lehmer_code - - """ - - is_Permutation = True - - _array_form = None - _cyclic_form = None - _cycle_structure = None - _size = None - _rank = None - - def __new__(cls, *args, **kwargs): - """ - Constructor for the Permutation object from a list or a - list of lists in which all elements of the permutation may - appear only once. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> Permutation.print_cyclic = False - - Permutations entered in array-form are left unaltered: - - >>> Permutation([0, 2, 1]) - Permutation([0, 2, 1]) - - Permutations entered in cyclic form are converted to array form; - singletons need not be entered, but can be entered to indicate the - largest element: - - >>> Permutation([[4, 5, 6], [0, 1]]) - Permutation([1, 0, 2, 3, 5, 6, 4]) - >>> Permutation([[4, 5, 6], [0, 1], [19]]) - Permutation([1, 0, 2, 3, 5, 6, 4], size=20) - - All manipulation of permutations assumes that the smallest element - is 0 (in keeping with 0-based indexing in Python) so if the 0 is - missing when entering a permutation in array form, an error will be - raised: - - >>> Permutation([2, 1]) - Traceback (most recent call last): - ... - ValueError: Integers 0 through 2 must be present. - - If a permutation is entered in cyclic form, it can be entered without - singletons and the ``size`` specified so those values can be filled - in, otherwise the array form will only extend to the maximum value - in the cycles: - - >>> Permutation([[1, 4], [3, 5, 2]], size=10) - Permutation([0, 4, 3, 5, 1, 2], size=10) - >>> _.array_form - [0, 4, 3, 5, 1, 2, 6, 7, 8, 9] - """ - size = kwargs.pop('size', None) - if size is not None: - size = int(size) - - #a) () - #b) (1) = identity - #c) (1, 2) = cycle - #d) ([1, 2, 3]) = array form - #e) ([[1, 2]]) = cyclic form - #f) (Cycle) = conversion to permutation - #g) (Permutation) = adjust size or return copy - ok = True - if not args: # a - return _af_new(list(range(size or 0))) - elif len(args) > 1: # c - return _af_new(Cycle(*args).list(size)) - if len(args) == 1: - a = args[0] - if isinstance(a, Perm): # g - if size is None or size == a.size: - return a - return Perm(a.array_form, size=size) - if isinstance(a, Cycle): # f - return _af_new(a.list(size)) - if not is_sequence(a): # b - return _af_new(list(range(a + 1))) - if has_variety(is_sequence(ai) for ai in a): - ok = False - else: - ok = False - if not ok: - raise ValueError("Permutation argument must be a list of ints, " - "a list of lists, Permutation or Cycle.") - - - # safe to assume args are valid; this also makes a copy - # of the args - args = list(args[0]) - - is_cycle = args and is_sequence(args[0]) - if is_cycle: # e - args = [[int(i) for i in c] for c in args] - else: # d - args = [int(i) for i in args] - - # if there are n elements present, 0, 1, ..., n-1 should be present - # unless a cycle notation has been provided. A 0 will be added - # for convenience in case one wants to enter permutations where - # counting starts from 1. - - temp = flatten(args) - if has_dups(temp): - if is_cycle: - raise ValueError('there were repeated elements; to resolve ' - 'cycles use Cycle%s.' % ''.join([str(tuple(c)) for c in args])) - else: - raise ValueError('there were repeated elements.') - temp = set(temp) - - if not is_cycle and \ - any(i not in temp for i in range(len(temp))): - raise ValueError("Integers 0 through %s must be present." % - max(temp)) - - if is_cycle: - # it's not necessarily canonical so we won't store - # it -- use the array form instead - c = Cycle() - for ci in args: - c = c(*ci) - aform = c.list() - else: - aform = list(args) - if size and size > len(aform): - # don't allow for truncation of permutation which - # might split a cycle and lead to an invalid aform - # but do allow the permutation size to be increased - aform.extend(list(range(len(aform), size))) - size = len(aform) - obj = Basic.__new__(cls, aform) - obj._array_form = aform - obj._size = size - return obj - - @staticmethod - def _af_new(perm): - """A method to produce a Permutation object from a list; - the list is bound to the _array_form attribute, so it must - not be modified; this method is meant for internal use only; - the list ``a`` is supposed to be generated as a temporary value - in a method, so p = Perm._af_new(a) is the only object - to hold a reference to ``a``:: - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Perm - >>> Perm.print_cyclic = False - >>> a = [2,1,3,0] - >>> p = Perm._af_new(a) - >>> p - Permutation([2, 1, 3, 0]) - - """ - p = Basic.__new__(Perm, perm) - p._array_form = perm - p._size = len(perm) - return p - - def _hashable_content(self): - # the array_form (a list) is the Permutation arg, so we need to - # return a tuple, instead - return tuple(self.array_form) - - @property -
    [docs] def array_form(self): - """ - Return a copy of the attribute _array_form - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> Permutation.print_cyclic = False - >>> p = Permutation([[2,0], [3,1]]) - >>> p.array_form - [2, 3, 0, 1] - >>> Permutation([[2,0,3,1]]).array_form - [3, 2, 0, 1] - >>> Permutation([2,0,3,1]).array_form - [2, 0, 3, 1] - >>> Permutation([[1, 2], [4, 5]]).array_form - [0, 2, 1, 3, 5, 4] - """ - return self._array_form[:] -
    -
    [docs] def list(self, size=None): - """Return the permutation as an explicit list, possibly - trimming unmoved elements if size is less than the maximum - element in the permutation; if this is desired, setting - ``size=-1`` will guarantee such trimming. - - Examples - ======== - >>> from sympy.combinatorics.permutations import Permutation - >>> Permutation.print_cyclic = False - >>> p = Permutation(2, 3)(4, 5) - >>> p.list() - [0, 1, 3, 2, 5, 4] - >>> p.list(10) - [0, 1, 3, 2, 5, 4, 6, 7, 8, 9] - - Passing a length too small will trim trailing, unchanged elements - in the permutation: - - >>> Permutation(2, 4)(1, 2, 4).list(-1) - [0, 2, 1] - >>> Permutation(3).list(-1) - [] - """ - if not self and size is None: - raise ValueError('must give size for empty Cycle') - rv = self.array_form - if size is not None: - if size > self.size: - rv.extend(list(range(self.size, size))) - else: - # find first value from rhs where rv[i] != i - i = self.size - 1 - while rv: - if rv[-1] != i: - break - rv.pop() - i -= 1 - return rv -
    - @property -
    [docs] def cyclic_form(self): - """ - This is used to convert to the cyclic notation - from the canonical notation. Singletons are omitted. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> Permutation.print_cyclic = False - >>> p = Permutation([0, 3, 1, 2]) - >>> p.cyclic_form - [[1, 3, 2]] - >>> Permutation([1, 0, 2, 4, 3, 5]).cyclic_form - [[0, 1], [3, 4]] - - See Also - ======== - - array_form, full_cyclic_form - """ - if self._cyclic_form is not None: - return list(self._cyclic_form) - array_form = self.array_form - unchecked = [True] * len(array_form) - cyclic_form = [] - for i in range(len(array_form)): - if unchecked[i]: - cycle = [] - cycle.append(i) - unchecked[i] = False - j = i - while unchecked[array_form[j]]: - j = array_form[j] - cycle.append(j) - unchecked[j] = False - if len(cycle) > 1: - cyclic_form.append(cycle) - assert cycle == list(minlex(cycle, is_set=True)) - cyclic_form.sort() - self._cyclic_form = cyclic_form[:] - return cyclic_form -
    - @property -
    [docs] def full_cyclic_form(self): - """Return permutation in cyclic form including singletons. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> Permutation([0, 2, 1]).full_cyclic_form - [[0], [1, 2]] - """ - need = set(range(self.size)) - set(flatten(self.cyclic_form)) - rv = self.cyclic_form - rv.extend([[i] for i in need]) - rv.sort() - return rv -
    - @property -
    [docs] def size(self): - """ - Returns the number of elements in the permutation. - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation([[3, 2], [0, 1]]).size - 4 - - See Also - ======== - - cardinality, length, order, rank - """ - return self._size -
    -
    [docs] def support(self): - """Return the elements in permutation, P, for which P[i] != i. - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> p = Permutation([[3, 2], [0, 1], [4]]) - >>> p.array_form - [1, 0, 3, 2, 4] - >>> p.support() - [0, 1, 2, 3] - """ - a = self.array_form - return [i for i, e in enumerate(a) if a[i] != i] -
    - def __add__(self, other): - """Return permutation that is other higher in rank than self. - - The rank is the lexicographical rank, with the identity permutation - having rank of 0. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> Permutation.print_cyclic = False - >>> I = Permutation([0, 1, 2, 3]) - >>> a = Permutation([2, 1, 3, 0]) - >>> I + a.rank() == a - True - - See Also - ======== - - __sub__, inversion_vector - - """ - rank = (self.rank() + other) % self.cardinality - rv = Perm.unrank_lex(self.size, rank) - rv._rank = rank - return rv - - def __sub__(self, other): - """Return the permutation that is other lower in rank than self. - - See Also - ======== - - __add__ - """ - return self.__add__(-other) - - @staticmethod -
    [docs] def rmul(*args): - """ - Return product of Permutations [a, b, c, ...] as the Permutation whose - ith value is a(b(c(i))). - - a, b, c, ... can be Permutation objects or tuples. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import _af_rmul, Permutation - >>> Permutation.print_cyclic = False - - >>> a, b = [1, 0, 2], [0, 2, 1] - >>> a = Permutation(a); b = Permutation(b) - >>> list(Permutation.rmul(a, b)) - [1, 2, 0] - >>> [a(b(i)) for i in range(3)] - [1, 2, 0] - - This handles the operands in reverse order compared to the ``*`` operator: - - >>> a = Permutation(a); b = Permutation(b) - >>> list(a*b) - [2, 0, 1] - >>> [b(a(i)) for i in range(3)] - [2, 0, 1] - - Notes - ===== - - All items in the sequence will be parsed by Permutation as - necessary as long as the first item is a Permutation: - - >>> Permutation.rmul(a, [0, 2, 1]) == Permutation.rmul(a, b) - True - - The reverse order of arguments will raise a TypeError. - - """ - rv = args[0] - for i in range(1, len(args)): - rv = args[i]*rv - return rv -
    - @staticmethod -
    [docs] def rmul_with_af(*args): - """ - same as rmul, but the elements of args are Permutation objects - which have _array_form - """ - a = [x._array_form for x in args] - rv = _af_new(_af_rmuln(*a)) - return rv -
    -
    [docs] def mul_inv(self, other): - """ - other*~self, self and other have _array_form - """ - a = _af_invert(self._array_form) - b = other._array_form - return _af_new(_af_rmul(a, b)) -
    - def __rmul__(self, other): - """This is needed to coerse other to Permutation in rmul.""" - return Perm(other)*self - - def __mul__(self, other): - """ - Return the product a*b as a Permutation; the ith value is b(a(i)). - - Examples - ======== - - >>> from sympy.combinatorics.permutations import _af_rmul, Permutation - >>> Permutation.print_cyclic = False - - >>> a, b = [1, 0, 2], [0, 2, 1] - >>> a = Permutation(a); b = Permutation(b) - >>> list(a*b) - [2, 0, 1] - >>> [b(a(i)) for i in range(3)] - [2, 0, 1] - - This handles operands in reverse order compared to _af_rmul and rmul: - - >>> al = list(a); bl = list(b) - >>> _af_rmul(al, bl) - [1, 2, 0] - >>> [al[bl[i]] for i in range(3)] - [1, 2, 0] - - It is acceptable for the arrays to have different lengths; the shorter - one will be padded to match the longer one: - - >>> b*Permutation([1, 0]) - Permutation([1, 2, 0]) - >>> Permutation([1, 0])*b - Permutation([2, 0, 1]) - - It is also acceptable to allow coercion to handle conversion of a - single list to the left of a Permutation: - - >>> [0, 1]*a # no change: 2-element identity - Permutation([1, 0, 2]) - >>> [[0, 1]]*a # exchange first two elements - Permutation([0, 1, 2]) - - You cannot use more than 1 cycle notation in a product of cycles - since coercion can only handle one argument to the left. To handle - multiple cycles it is convenient to use Cycle instead of Permutation: - - >>> [[1, 2]]*[[2, 3]]*Permutation([]) # doctest: +SKIP - >>> from sympy.combinatorics.permutations import Cycle - >>> Cycle(1, 2)(2, 3) - Cycle(1, 3, 2) - - """ - a = self.array_form - # __rmul__ makes sure the other is a Permutation - b = other.array_form - if not b: - perm = a - else: - b.extend(list(range(len(b), len(a)))) - perm = [b[i] for i in a] + b[len(a):] - return _af_new(perm) - -
    [docs] def commutes_with(self, other): - """ - Checks if the elements are commuting. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> a = Permutation([1,4,3,0,2,5]) - >>> b = Permutation([0,1,2,3,4,5]) - >>> a.commutes_with(b) - True - >>> b = Permutation([2,3,5,4,1,0]) - >>> a.commutes_with(b) - False - """ - a = self.array_form - b = other.array_form - return _af_commutes_with(a, b) -
    - def __pow__(self, n): - """ - Routine for finding powers of a permutation. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> Permutation.print_cyclic = False - >>> p = Permutation([2,0,3,1]) - >>> p.order() - 4 - >>> p**4 - Permutation([0, 1, 2, 3]) - """ - if type(n) == Perm: - raise NotImplementedError( - 'p**p is not defined; do you mean p^p (conjugate)?') - n = int(n) - return _af_new(_af_pow(self.array_form, n)) - - def __rxor__(self, i): - """Return self(i) when ``i`` is an int. - - Examples - ======== - >>> from sympy.combinatorics import Permutation - >>> p = Permutation(1, 2, 9) - >>> 2^p == p(2) == 9 - True - """ - if int(i) == i: - return self(i) - else: - raise NotImplementedError( - "i^p = p(i) when i is an integer, not %s." % i) - - def __xor__(self, h): - """Return the conjugate permutation ``~h*self*h` `. - - If ``a`` and ``b`` are conjugates, ``a = h*b*~h`` and - ``b = ~h*a*h`` and both have the same cycle structure. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> Permutation.print_cyclic = True - >>> p = Permutation(1, 2, 9) - >>> q = Permutation(6, 9, 8) - >>> p*q != q*p - True - - Calculate and check properties of the conjugate: - - >>> c = p^q - >>> c == ~q*p*q and p == q*c*~q - True - - The expression q^p^r is equivalent to q^(p*r): - - >>> r = Permutation(9)(4,6,8) - >>> q^p^r == q^(p*r) - True - - If the term to the left of the conjugate operator, i, is an integer - then this is interpreted as selecting the ith element from the - permutation to the right: - - >>> all(i^p == p(i) for i in range(p.size)) - True - - Note that the * operator as higher precedence than the ^ operator: - - >>> q^r*p^r == q^(r*p)^r == Permutation(9)(1, 6, 4) - True - - Notes - ===== - - In Python the precedence rule is p^q^r = (p^q)^r which differs - in general from p^(q^r) - - >>> q^p^r - Permutation(9)(1, 4, 8) - >>> q^(p^r) - Permutation(9)(1, 8, 6) - - For a given r and p, both of the following are conjugates of p: - ~r*p*r and r*p*~r. But these are not necessarily the same: - - >>> ~r*p*r == r*p*~r - True - - >>> p = Permutation(1, 2, 9)(5, 6) - >>> ~r*p*r == r*p*~r - False - - The conjugate ~r*p*r was chosen so that ``p^q^r`` would be equivalent - to ``p^(q*r)`` rather than ``p^(r*q)``. To obtain r*p*~r, pass ~r to - this method: - - >>> p^~r == r*p*~r - True - """ - - if self.size != h.size: - raise ValueError("The permutations must be of equal size.") - a = [None]*self.size - h = h._array_form - p = self._array_form - for i in range(self.size): - a[h[i]] = h[p[i]] - return _af_new(a) - -
    [docs] def transpositions(self): - """ - Return the permutation decomposed into a list of transpositions. - - It is always possible to express a permutation as the product of - transpositions, see [1] - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> p = Permutation([[1, 2, 3], [0, 4, 5, 6, 7]]) - >>> t = p.transpositions() - >>> t - [(0, 7), (0, 6), (0, 5), (0, 4), (1, 3), (1, 2)] - >>> print(''.join(str(c) for c in t)) - (0, 7)(0, 6)(0, 5)(0, 4)(1, 3)(1, 2) - >>> Permutation.rmul(*[Permutation([ti], size=p.size) for ti in t]) == p - True - - References - ========== - - 1. http://en.wikipedia.org/wiki/Transposition_%28mathematics%29#Properties - - """ - a = self.cyclic_form - res = [] - for x in a: - nx = len(x) - if nx == 2: - res.append(tuple(x)) - elif nx > 2: - first = x[0] - for y in x[nx - 1:0:-1]: - res.append((first, y)) - return res -
    - @classmethod -
    [docs] def from_sequence(self, i, key=None): - """Return the permutation needed to obtain ``i`` from the sorted - elements of ``i``. If custom sorting is desired, a key can be given. - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation.print_cyclic = True - - >>> Permutation.from_sequence('SymPy') - Permutation(4)(0, 1, 3) - >>> _(sorted("SymPy")) - ['S', 'y', 'm', 'P', 'y'] - >>> Permutation.from_sequence('SymPy', key=lambda x: x.lower()) - Permutation(4)(0, 2)(1, 3) - """ - ic = list(zip(i, list(range(len(i))))) - if key: - ic.sort(key=lambda x: key(x[0])) - else: - ic.sort() - return ~Permutation([i[1] for i in ic]) -
    - def __invert__(self): - """ - Return the inverse of the permutation. - - A permutation multiplied by its inverse is the identity permutation. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> p = Permutation([[2,0], [3,1]]) - >>> ~p - Permutation([2, 3, 0, 1]) - >>> _ == p**-1 - True - >>> p*~p == ~p*p == Permutation([0, 1, 2, 3]) - True - """ - return _af_new(_af_invert(self._array_form)) - - def __iter__(self): - """Yield elements from array form. - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> list(Permutation(list(range(3)))) - [0, 1, 2] - """ - for i in self.array_form: - yield i - - def __call__(self, *i): - """ - Allows applying a permutation instance as a bijective function. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> p = Permutation([[2,0], [3,1]]) - >>> p.array_form - [2, 3, 0, 1] - >>> [p(i) for i in range(4)] - [2, 3, 0, 1] - - If an array is given then the permutation selects the items - from the array (i.e. the permutation is applied to the array): - - >>> from sympy.abc import x - >>> p([x, 1, 0, x**2]) - [0, x**2, x, 1] - """ - # list indices can be Integer or int; leave this - # as it is (don't test or convert it) because this - # gets called a lot and should be fast - if len(i) == 1: - i = i[0] - try: - # P(1) - return self._array_form[i] - except TypeError: - try: - # P([a, b, c]) - return [i[j] for j in self._array_form] - except: - raise TypeError('unrecognized argument') - else: - # P(1, 2, 3) - return self*Permutation(Cycle(*i), size=self.size) - -
    [docs] def atoms(self): - """ - Returns all the elements of a permutation - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation([0, 1, 2, 3, 4, 5]).atoms() - set([0, 1, 2, 3, 4, 5]) - >>> Permutation([[0, 1], [2, 3], [4, 5]]).atoms() - set([0, 1, 2, 3, 4, 5]) - """ - return set(self.array_form) -
    -
    [docs] def next_lex(self): - """ - Returns the next permutation in lexicographical order. - If self is the last permutation in lexicographical order - it returns None. - See [4] section 2.4. - - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> p = Permutation([2, 3, 1, 0]) - >>> p = Permutation([2, 3, 1, 0]); p.rank() - 17 - >>> p = p.next_lex(); p.rank() - 18 - - See Also - ======== - - rank, unrank_lex - """ - perm = self.array_form[:] - n = len(perm) - i = n - 2 - while perm[i + 1] < perm[i]: - i -= 1 - if i == -1: - return None - else: - j = n - 1 - while perm[j] < perm[i]: - j -= 1 - perm[j], perm[i] = perm[i], perm[j] - i += 1 - j = n - 1 - while i < j: - perm[j], perm[i] = perm[i], perm[j] - i += 1 - j -= 1 - return _af_new(perm) -
    - @classmethod -
    [docs] def unrank_nonlex(self, n, r): - """ - This is a linear time unranking algorithm that does not - respect lexicographic order [3]. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> Permutation.print_cyclic = False - >>> Permutation.unrank_nonlex(4, 5) - Permutation([2, 0, 3, 1]) - >>> Permutation.unrank_nonlex(4, -1) - Permutation([0, 1, 2, 3]) - - See Also - ======== - - next_nonlex, rank_nonlex - """ - def _unrank1(n, r, a): - if n > 0: - a[n - 1], a[r % n] = a[r % n], a[n - 1] - _unrank1(n - 1, r//n, a) - - id_perm = list(range(n)) - n = int(n) - r = r % ifac(n) - _unrank1(n, r, id_perm) - return _af_new(id_perm) -
    -
    [docs] def rank_nonlex(self, inv_perm=None): - """ - This is a linear time ranking algorithm that does not - enforce lexicographic order [3]. - - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> p = Permutation([0,1,2,3]) - >>> p.rank_nonlex() - 23 - - See Also - ======== - - next_nonlex, unrank_nonlex - """ - def _rank1(n, perm, inv_perm): - if n == 1: - return 0 - s = perm[n - 1] - t = inv_perm[n - 1] - perm[n - 1], perm[t] = perm[t], s - inv_perm[n - 1], inv_perm[s] = inv_perm[s], t - return s + n*_rank1(n - 1, perm, inv_perm) - - if inv_perm is None: - inv_perm = (~self).array_form - if not inv_perm: - return 0 - perm = self.array_form[:] - r = _rank1(len(perm), perm, inv_perm) - return r -
    -
    [docs] def next_nonlex(self): - """ - Returns the next permutation in nonlex order [3]. - If self is the last permutation in this order it returns None. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> Permutation.print_cyclic = False - >>> p = Permutation([2, 0, 3, 1]); p.rank_nonlex() - 5 - >>> p = p.next_nonlex(); p - Permutation([3, 0, 1, 2]) - >>> p.rank_nonlex() - 6 - - See Also - ======== - - rank_nonlex, unrank_nonlex - """ - r = self.rank_nonlex() - if r == ifac(self.size) - 1: - return None - return Perm.unrank_nonlex(self.size, r + 1) -
    -
    [docs] def rank(self): - """ - Returns the lexicographic rank of the permutation. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> p = Permutation([0, 1, 2, 3]) - >>> p.rank() - 0 - >>> p = Permutation([3, 2, 1, 0]) - >>> p.rank() - 23 - - See Also - ======== - - next_lex, unrank_lex, cardinality, length, order, size - """ - if not self._rank is None: - return self._rank - rank = 0 - rho = self.array_form[:] - n = self.size - 1 - size = n + 1 - psize = int(ifac(n)) - for j in range(size - 1): - rank += rho[j]*psize - for i in range(j + 1, size): - if rho[i] > rho[j]: - rho[i] -= 1 - psize //= n - n -= 1 - self._rank = rank - return rank -
    - @property -
    [docs] def cardinality(self): - """ - Returns the number of all possible permutations. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> p = Permutation([0,1,2,3]) - >>> p.cardinality - 24 - - See Also - ======== - - length, order, rank, size - """ - return int(ifac(self.size)) -
    -
    [docs] def parity(self): - """ - Computes the parity of a permutation. - - The parity of a permutation reflects the parity of the - number of inversions in the permutation, i.e., the - number of pairs of x and y such that ``x > y`` but ``p[x] < p[y]``. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> p = Permutation([0,1,2,3]) - >>> p.parity() - 0 - >>> p = Permutation([3,2,0,1]) - >>> p.parity() - 1 - - See Also - ======== - - _af_parity - """ - if self._cyclic_form is not None: - return (self.size - self.cycles) % 2 - - return _af_parity(self.array_form) -
    - @property -
    [docs] def is_even(self): - """ - Checks if a permutation is even. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> p = Permutation([0,1,2,3]) - >>> p.is_even - True - >>> p = Permutation([3,2,1,0]) - >>> p.is_even - True - - See Also - ======== - - is_odd - """ - return not self.is_odd -
    - @property -
    [docs] def is_odd(self): - """ - Checks if a permutation is odd. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> p = Permutation([0,1,2,3]) - >>> p.is_odd - False - >>> p = Permutation([3,2,0,1]) - >>> p.is_odd - True - - See Also - ======== - - is_even - """ - return bool(self.parity() % 2) -
    - @property -
    [docs] def is_Singleton(self): - """ - Checks to see if the permutation contains only one number and is - thus the only possible permutation of this set of numbers - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation([0]).is_Singleton - True - >>> Permutation([0, 1]).is_Singleton - False - - See Also - ======== - - is_Empty - """ - return self.size == 1 -
    - @property -
    [docs] def is_Empty(self): - """ - Checks to see if the permutation is a set with zero elements - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation([]).is_Empty - True - >>> Permutation([0]).is_Empty - False - - See Also - ======== - - is_Singleton - """ - return self.size == 0 -
    - @property -
    [docs] def is_Identity(self): - """ - Returns True if the Permutation is an identity permutation. - - Examples - ======== - >>> from sympy.combinatorics.permutations import Permutation - >>> p = Permutation([]) - >>> p.is_Identity - True - >>> p = Permutation([[0], [1], [2]]) - >>> p.is_Identity - True - >>> p = Permutation([0, 1, 2]) - >>> p.is_Identity - True - >>> p = Permutation([0, 2, 1]) - >>> p.is_Identity - False - - See Also - ======== - - order - """ - af = self.array_form - return not af or all(i == af[i] for i in range(self.size)) -
    -
    [docs] def ascents(self): - """ - Returns the positions of ascents in a permutation, ie, the location - where p[i] < p[i+1] - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> p = Permutation([4,0,1,3,2]) - >>> p.ascents() - [1, 2] - - See Also - ======== - - descents, inversions, min, max - """ - a = self.array_form - pos = [i for i in range(len(a) - 1) if a[i] < a[i + 1]] - return pos -
    -
    [docs] def descents(self): - """ - Returns the positions of descents in a permutation, ie, the location - where p[i] > p[i+1] - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> p = Permutation([4,0,1,3,2]) - >>> p.descents() - [0, 3] - - See Also - ======== - - ascents, inversions, min, max - """ - a = self.array_form - pos = [i for i in range(len(a) - 1) if a[i] > a[i + 1]] - return pos -
    -
    [docs] def max(self): - """ - The maximum element moved by the permutation. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> p = Permutation([1,0,2,3,4]) - >>> p.max() - 1 - - See Also - ======== - - min, descents, ascents, inversions - """ - max = 0 - a = self.array_form - for i in range(len(a)): - if a[i] != i and a[i] > max: - max = a[i] - return max -
    -
    [docs] def min(self): - """ - The minimum element moved by the permutation. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> p = Permutation([0,1,4,3,2]) - >>> p.min() - 2 - - See Also - ======== - - max, descents, ascents, inversions - """ - a = self.array_form - min = len(a) - for i in range(len(a)): - if a[i] != i and a[i] < min: - min = a[i] - return min -
    -
    [docs] def inversions(self): - """ - Computes the number of inversions of a permutation. - - An inversion is where i > j but p[i] < p[j]. - - For small length of p, it iterates over all i and j - values and calculates the number of inversions. - For large length of p, it uses a variation of merge - sort to calculate the number of inversions. - - References - ========== - - [1] http://www.cp.eng.chula.ac.th/~piak/teaching/algo/algo2008/count-inv.htm - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> p = Permutation([0,1,2,3,4,5]) - >>> p.inversions() - 0 - >>> Permutation([3,2,1,0]).inversions() - 6 - - See Also - ======== - - descents, ascents, min, max - """ - inversions = 0 - a = self.array_form - n = len(a) - if n < 130: - for i in range(n - 1): - b = a[i] - for c in a[i + 1:]: - if b > c: - inversions += 1 - else: - k = 1 - right = 0 - arr = a[:] - temp = a[:] - while k < n: - i = 0 - while i + k < n: - right = i + k * 2 - 1 - if right >= n: - right = n - 1 - inversions += _merge(arr, temp, i, i + k, right) - i = i + k * 2 - k = k * 2 - return inversions -
    -
    [docs] def commutator(self, x): - """Return the commutator of self and x: ``~x*~self*x*self`` - - If f and g are part of a group, G, then the commutator of f and g - is the group identity iff f and g commute, i.e. fg == gf. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> Permutation.print_cyclic = False - >>> p = Permutation([0, 2, 3, 1]) - >>> x = Permutation([2, 0, 3, 1]) - >>> c = p.commutator(x); c - Permutation([2, 1, 3, 0]) - >>> c == ~x*~p*x*p - True - - >>> I = Permutation(3) - >>> p = [I + i for i in range(6)] - >>> for i in range(len(p)): - ... for j in range(len(p)): - ... c = p[i].commutator(p[j]) - ... if p[i]*p[j] == p[j]*p[i]: - ... assert c == I - ... else: - ... assert c != I - ... - - References - ========== - - http://en.wikipedia.org/wiki/Commutator - """ - - a = self.array_form - b = x.array_form - n = len(a) - if len(b) != n: - raise ValueError("The permutations must be of equal size.") - inva = [None]*n - for i in range(n): - inva[a[i]] = i - invb = [None]*n - for i in range(n): - invb[b[i]] = i - return _af_new([a[b[inva[i]]] for i in invb]) -
    -
    [docs] def signature(self): - """ - Gives the signature of the permutation needed to place the - elements of the permutation in canonical order. - - The signature is calculated as (-1)^<number of inversions> - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> p = Permutation([0,1,2]) - >>> p.inversions() - 0 - >>> p.signature() - 1 - >>> q = Permutation([0,2,1]) - >>> q.inversions() - 1 - >>> q.signature() - -1 - - See Also - ======== - - inversions - """ - if self.is_even: - return 1 - return -1 -
    -
    [docs] def order(self): - """ - Computes the order of a permutation. - - When the permutation is raised to the power of its - order it equals the identity permutation. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> Permutation.print_cyclic = False - >>> p = Permutation([3, 1, 5, 2, 4, 0]) - >>> p.order() - 4 - >>> (p**(p.order())) - Permutation([], size=6) - - See Also - ======== - - identity, cardinality, length, rank, size - """ - - return reduce(lcm, [len(cycle) for cycle in self.cyclic_form], 1) -
    -
    [docs] def length(self): - """ - Returns the number of integers moved by a permutation. - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation([0, 3, 2, 1]).length() - 2 - >>> Permutation([[0, 1], [2, 3]]).length() - 4 - - See Also - ======== - - min, max, suppport, cardinality, order, rank, size - """ - - return len(self.support()) -
    - @property -
    [docs] def cycle_structure(self): - """Return the cycle structure of the permutation as a dictionary - indicating the multiplicity of each cycle length. - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation.print_cyclic = True - >>> Permutation(3).cycle_structure - {1: 4} - >>> Permutation(0, 4, 3)(1, 2)(5, 6).cycle_structure - {2: 2, 3: 1} - """ - if self._cycle_structure: - rv = self._cycle_structure - else: - rv = defaultdict(int) - singletons = self.size - for c in self.cyclic_form: - rv[len(c)] += 1 - singletons -= len(c) - if singletons: - rv[1] = singletons - self._cycle_structure = rv - return dict(rv) # make a copy -
    - @property -
    [docs] def cycles(self): - """ - Returns the number of cycles contained in the permutation - (including singletons). - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation([0, 1, 2]).cycles - 3 - >>> Permutation([0, 1, 2]).full_cyclic_form - [[0], [1], [2]] - >>> Permutation(0, 1)(2, 3).cycles - 2 - """ - return len(self.full_cyclic_form) -
    -
    [docs] def index(self): - """ - Returns the index of a permutation. - - The index of a permutation is the sum of all subscripts j such - that p[j] is greater than p[j+1]. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> p = Permutation([3, 0, 2, 1, 4]) - >>> p.index() - 2 - """ - a = self.array_form - - return sum([j for j in range(len(a) - 1) if a[j] > a[j + 1]]) -
    -
    [docs] def runs(self): - """ - Returns the runs of a permutation. - - An ascending sequence in a permutation is called a run [5]. - - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> p = Permutation([2,5,7,3,6,0,1,4,8]) - >>> p.runs() - [[2, 5, 7], [3, 6], [0, 1, 4, 8]] - >>> q = Permutation([1,3,2,0]) - >>> q.runs() - [[1, 3], [2], [0]] - """ - return runs(self.array_form) -
    -
    [docs] def inversion_vector(self): - """Return the inversion vector of the permutation. - - The inversion vector consists of elements whose value - indicates the number of elements in the permutation - that are lesser than it and lie on its right hand side. - - The inversion vector is the same as the Lehmer encoding of a - permutation. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> p = Permutation([4, 8, 0, 7, 1, 5, 3, 6, 2]) - >>> p.inversion_vector() - [4, 7, 0, 5, 0, 2, 1, 1] - >>> p = Permutation([3, 2, 1, 0]) - >>> p.inversion_vector() - [3, 2, 1] - - The inversion vector increases lexicographically with the rank - of the permutation, the -ith element cycling through 0..i. - - >>> p = Permutation(2) - >>> while p: - ... print(p, p.inversion_vector(), p.rank()) - ... p = p.next_lex() - ... - Permutation([0, 1, 2]) [0, 0] 0 - Permutation([0, 2, 1]) [0, 1] 1 - Permutation([1, 0, 2]) [1, 0] 2 - Permutation([1, 2, 0]) [1, 1] 3 - Permutation([2, 0, 1]) [2, 0] 4 - Permutation([2, 1, 0]) [2, 1] 5 - - See Also - ======== - from_inversion_vector - """ - self_array_form = self.array_form - n = len(self_array_form) - inversion_vector = [0] * (n - 1) - - for i in range(n - 1): - val = 0 - for j in range(i + 1, n): - if self_array_form[j] < self_array_form[i]: - val += 1 - inversion_vector[i] = val - return inversion_vector -
    -
    [docs] def rank_trotterjohnson(self): - """ - Returns the Trotter Johnson rank, which we get from the minimal - change algorithm. See [4] section 2.4. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> p = Permutation([0,1,2,3]) - >>> p.rank_trotterjohnson() - 0 - >>> p = Permutation([0,2,1,3]) - >>> p.rank_trotterjohnson() - 7 - - See Also - ======== - - unrank_trotterjohnson, next_trotterjohnson - """ - if self.array_form == [] or self.is_Identity: - return 0 - if self.array_form == [1, 0]: - return 1 - perm = self.array_form - n = self.size - rank = 0 - for j in range(1, n): - k = 1 - i = 0 - while perm[i] != j: - if perm[i] < j: - k += 1 - i += 1 - j1 = j + 1 - if rank % 2 == 0: - rank = j1*rank + j1 - k - else: - rank = j1*rank + k - 1 - return rank -
    - @classmethod -
    [docs] def unrank_trotterjohnson(self, size, rank): - """ - Trotter Johnson permutation unranking. See [4] section 2.4. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> Permutation.unrank_trotterjohnson(5, 10) - Permutation([0, 3, 1, 2, 4]) - - See Also - ======== - - rank_trotterjohnson, next_trotterjohnson - """ - perm = [0]*size - r2 = 0 - n = ifac(size) - pj = 1 - for j in range(2, size + 1): - pj *= j - r1 = (rank * pj) // n - k = r1 - j*r2 - if r2 % 2 == 0: - for i in range(j - 1, j - k - 1, -1): - perm[i] = perm[i - 1] - perm[j - k - 1] = j - 1 - else: - for i in range(j - 1, k, -1): - perm[i] = perm[i - 1] - perm[k] = j - 1 - r2 = r1 - return _af_new(perm) -
    -
    [docs] def next_trotterjohnson(self): - """ - Returns the next permutation in Trotter-Johnson order. - If self is the last permutation it returns None. - See [4] section 2.4. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> Permutation.print_cyclic = False - >>> p = Permutation([3, 0, 2, 1]) - >>> p.rank_trotterjohnson() - 4 - >>> p = p.next_trotterjohnson(); p - Permutation([0, 3, 2, 1]) - >>> p.rank_trotterjohnson() - 5 - - See Also - ======== - - rank_trotterjohnson, unrank_trotterjohnson - """ - pi = self.array_form[:] - n = len(pi) - st = 0 - rho = pi[:] - done = False - m = n-1 - while m > 0 and not done: - d = rho.index(m) - for i in range(d, m): - rho[i] = rho[i + 1] - par = _af_parity(rho[:m]) - if par == 1: - if d == m: - m -= 1 - else: - pi[st + d], pi[st + d + 1] = pi[st + d + 1], pi[st + d] - done = True - else: - if d == 0: - m -= 1 - st += 1 - else: - pi[st + d], pi[st + d - 1] = pi[st + d - 1], pi[st + d] - done = True - if m == 0: - return None - return _af_new(pi) -
    -
    [docs] def get_precedence_matrix(self): - """ - Gets the precedence matrix. This is used for computing the - distance between two permutations. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> p = Permutation.josephus(3,6,1) - >>> p - Permutation([2, 5, 3, 1, 4, 0]) - >>> p.get_precedence_matrix() - [0, 0, 0, 0, 0, 0] - [1, 0, 0, 0, 1, 0] - [1, 1, 0, 1, 1, 1] - [1, 1, 0, 0, 1, 0] - [1, 0, 0, 0, 0, 0] - [1, 1, 0, 1, 1, 0] - - See Also - ======== - - get_precedence_distance, get_adjacency_matrix, get_adjacency_distance - """ - m = zeros(self.size) - perm = self.array_form - for i in range(m.rows): - for j in range(i + 1, m.cols): - m[perm[i], perm[j]] = 1 - return m -
    -
    [docs] def get_precedence_distance(self, other): - """ - Computes the precedence distance between two permutations. - - Suppose p and p' represent n jobs. The precedence metric - counts the number of times a job j is prededed by job i - in both p and p'. This metric is commutative. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> p = Permutation([2, 0, 4, 3, 1]) - >>> q = Permutation([3, 1, 2, 4, 0]) - >>> p.get_precedence_distance(q) - 7 - >>> q.get_precedence_distance(p) - 7 - - See Also - ======== - - get_precedence_matrix, get_adjacency_matrix, get_adjacency_distance - """ - if self.size != other.size: - raise ValueError("The permutations must be of equal size.") - self_prec_mat = self.get_precedence_matrix() - other_prec_mat = other.get_precedence_matrix() - n_prec = 0 - for i in range(self.size): - for j in range(self.size): - if i == j: - continue - if self_prec_mat[i, j] * other_prec_mat[i, j] == 1: - n_prec += 1 - d = self.size * (self.size - 1)//2 - n_prec - return d -
    -
    [docs] def get_adjacency_matrix(self): - """ - Computes the adjacency matrix of a permutation. - - If job i is adjacent to job j in a permutation p - then we set m[i, j] = 1 where m is the adjacency - matrix of p. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> p = Permutation.josephus(3,6,1) - >>> p.get_adjacency_matrix() - [0, 0, 0, 0, 0, 0] - [0, 0, 0, 0, 1, 0] - [0, 0, 0, 0, 0, 1] - [0, 1, 0, 0, 0, 0] - [1, 0, 0, 0, 0, 0] - [0, 0, 0, 1, 0, 0] - - >>> q = Permutation([0, 1, 2, 3]) - >>> q.get_adjacency_matrix() - [0, 1, 0, 0] - [0, 0, 1, 0] - [0, 0, 0, 1] - [0, 0, 0, 0] - - See Also - ======== - - get_precedence_matrix, get_precedence_distance, get_adjacency_distance - """ - m = zeros(self.size) - perm = self.array_form - for i in range(self.size - 1): - m[perm[i], perm[i + 1]] = 1 - return m -
    -
    [docs] def get_adjacency_distance(self, other): - """ - Computes the adjacency distance between two permutations. - - This metric counts the number of times a pair i,j of jobs is - adjacent in both p and p'. If n_adj is this quantity then - the adjacency distance is n - n_adj - 1 [1] - - [1] Reeves, Colin R. Landscapes, Operators and Heuristic search, Annals - of Operational Research, 86, pp 473-490. (1999) - - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> p = Permutation([0, 3, 1, 2, 4]) - >>> q = Permutation.josephus(4, 5, 2) - >>> p.get_adjacency_distance(q) - 3 - >>> r = Permutation([0, 2, 1, 4, 3]) - >>> p.get_adjacency_distance(r) - 4 - - See Also - ======== - - get_precedence_matrix, get_precedence_distance, get_adjacency_matrix - """ - if self.size != other.size: - raise ValueError("The permutations must be of the same size.") - self_adj_mat = self.get_adjacency_matrix() - other_adj_mat = other.get_adjacency_matrix() - n_adj = 0 - for i in range(self.size): - for j in range(self.size): - if i == j: - continue - if self_adj_mat[i, j] * other_adj_mat[i, j] == 1: - n_adj += 1 - d = self.size - n_adj - 1 - return d -
    -
    [docs] def get_positional_distance(self, other): - """ - Computes the positional distance between two permutations. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> p = Permutation([0, 3, 1, 2, 4]) - >>> q = Permutation.josephus(4, 5, 2) - >>> r = Permutation([3, 1, 4, 0, 2]) - >>> p.get_positional_distance(q) - 12 - >>> p.get_positional_distance(r) - 12 - - See Also - ======== - - get_precedence_distance, get_adjacency_distance - """ - a = self.array_form - b = other.array_form - if len(a) != len(b): - raise ValueError("The permutations must be of the same size.") - return sum([abs(a[i] - b[i]) for i in range(len(a))]) -
    - @classmethod -
    [docs] def josephus(self, m, n, s=1): - """Return as a permutation the shuffling of range(n) using the Josephus - scheme in which every m-th item is selected until all have been chosen. - The returned permutation has elements listed by the order in which they - were selected. - - The parameter ``s`` stops the selection process when there are ``s`` - items remaining and these are selected by countinuing the selection, - counting by 1 rather than by ``m``. - - Consider selecting every 3rd item from 6 until only 2 remain:: - - choices chosen - ======== ====== - 012345 - 01 345 2 - 01 34 25 - 01 4 253 - 0 4 2531 - 0 25314 - 253140 - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation.josephus(3, 6, 2).array_form - [2, 5, 3, 1, 4, 0] - - References - ========== - - 1. http://en.wikipedia.org/wiki/Flavius_Josephus - 2. http://en.wikipedia.org/wiki/Josephus_problem - 3. http://www.wou.edu/~burtonl/josephus.html - - """ - from collections import deque - m -= 1 - Q = deque(list(range(n))) - perm = [] - while len(Q) > max(s, 1): - for dp in range(m): - Q.append(Q.popleft()) - perm.append(Q.popleft()) - perm.extend(list(Q)) - return Perm(perm) -
    - @classmethod -
    [docs] def from_inversion_vector(self, inversion): - """ - Calculates the permutation from the inversion vector. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> Permutation.print_cyclic = False - >>> Permutation.from_inversion_vector([3, 2, 1, 0, 0]) - Permutation([3, 2, 1, 0, 4, 5]) - - """ - size = len(inversion) - N = list(range(size + 1)) - perm = [] - try: - for k in range(size): - val = N[inversion[k]] - perm.append(val) - N.remove(val) - except IndexError: - raise ValueError("The inversion vector is not valid.") - perm.extend(N) - return _af_new(perm) -
    - @classmethod -
    [docs] def random(self, n): - """ - Generates a random permutation of length ``n``. - - Uses the underlying Python psuedo-random number generator. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> Permutation.random(2) in (Permutation([1, 0]), Permutation([0, 1])) - True - - """ - perm_array = list(range(n)) - random.shuffle(perm_array) - return _af_new(perm_array) -
    - @classmethod -
    [docs] def unrank_lex(self, size, rank): - """ - Lexicographic permutation unranking. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> Permutation.print_cyclic = False - >>> a = Permutation.unrank_lex(5, 10) - >>> a.rank() - 10 - >>> a - Permutation([0, 2, 4, 1, 3]) - - See Also - ======== - - rank, next_lex - """ - perm_array = [0] * size - psize = 1 - for i in range(size): - new_psize = psize*(i + 1) - d = (rank % new_psize) // psize - rank -= d*psize - perm_array[size - i - 1] = d - for j in range(size - i, size): - if perm_array[j] > d - 1: - perm_array[j] += 1 - psize = new_psize - return _af_new(perm_array) - - # global flag to control how permutations are printed - # when True, Permutation([0, 2, 1, 3]) -> Cycle(1, 2) - # when False, Permutation([0, 2, 1, 3]) -> Permutation([0, 2, 1])
    - print_cyclic = True - -
    -def _merge(arr, temp, left, mid, right): - """ - Merges two sorted arrays and calculates the inversion count. - - Helper function for calculating inversions. This method is - for internal use only. - """ - i = k = left - j = mid - inv_count = 0 - while i < mid and j <= right: - if arr[i] < arr[j]: - temp[k] = arr[i] - k += 1 - i += 1 - else: - temp[k] = arr[j] - k += 1 - j += 1 - inv_count += (mid -i) - while i < mid: - temp[k] = arr[i] - k += 1 - i += 1 - if j <= right: - k += right - j + 1 - j += right - j + 1 - arr[left:k + 1] = temp[left:k + 1] - else: - arr[left:right + 1] = temp[left:right + 1] - return inv_count - -Perm = Permutation -_af_new = Perm._af_new -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/combinatorics/polyhedron.html b/dev-py3k/_modules/sympy/combinatorics/polyhedron.html deleted file mode 100644 index 141360e56f6..00000000000 --- a/dev-py3k/_modules/sympy/combinatorics/polyhedron.html +++ /dev/null @@ -1,936 +0,0 @@ - - - - - - - - - - sympy.combinatorics.polyhedron — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.combinatorics.polyhedron

    -from sympy.core import Basic, Tuple, FiniteSet
    -from sympy.core.compatibility import as_int
    -from sympy.combinatorics import Permutation as Perm
    -from sympy.combinatorics.perm_groups import PermutationGroup
    -from sympy.utilities.iterables import (minlex, unflatten, flatten)
    -
    -rmul = Perm.rmul
    -
    -
    -
    [docs]class Polyhedron(Basic): - """ - Represents the polyhedral symmetry group (PSG). - - The PSG is one of the symmetry groups of the Platonic solids. - There are three polyhedral groups: the tetrahedral group - of order 12, the octahedral group of order 24, and the - icosahedral group of order 60. - - All doctests have been given in the docstring of the - constructor of the object. - - References - ========== - - http://mathworld.wolfram.com/PolyhedralGroup.html - """ - _edges = None - - def __new__(cls, corners, faces=[], pgroup=[]): - """ - The constructor of the Polyhedron group object. - - It takes up to three parameters: the corners, faces, and - allowed transformations. - - The corners/vertices are entered as a list of arbitrary - expressions that are used to identify each vertex. - - The faces are entered as a list of tuples of indices; a tuple - of indices identifies the vertices which define the face. They - should be entered in a cw or ccw order; they will be standardized - by reversal and rotation to be give the lowest lexical ordering. - If no faces are given then no edges will be computed. - - >>> from sympy.combinatorics.polyhedron import Polyhedron - >>> Polyhedron(list('abc'), [(1, 2, 0)]).faces - {(0, 1, 2)} - >>> Polyhedron(list('abc'), [(1, 0, 2)]).faces - {(0, 1, 2)} - - The allowed transformations are entered as allowable permutations - of the vertices for the polyhedron. Instance of Permutations - (as with faces) should refer to the supplied vertices by index. - These permutation are stored as a PermutationGroup. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> Permutation.print_cyclic = False - >>> from sympy.abc import w, x, y, z - - Here we construct the Polyhedron object for a tetrahedron. - - >>> corners = [w, x, y, z] - >>> faces = [(0,1,2), (0,2,3), (0,3,1), (1,2,3)] - - Next, allowed transformations of the polyhedron must be given. This - is given as permutations of vertices. - - Although the vertices of a tetrahedron can be numbered in 24 (4!) - different ways, there are only 12 different orientations for a - physical tetrahedron. The following permutations, applied once or - twice, will generate all 12 of the orientations. (The identity - permutation, Permutation(range(4)), is not included since it does - not change the orientation of the vertices.) - - >>> pgroup = [Permutation([[0,1,2], [3]]), \ - Permutation([[0,1,3], [2]]), \ - Permutation([[0,2,3], [1]]), \ - Permutation([[1,2,3], [0]]), \ - Permutation([[0,1], [2,3]]), \ - Permutation([[0,2], [1,3]]), \ - Permutation([[0,3], [1,2]])] - - The Polyhedron is now constructed and demonstrated: - - >>> tetra = Polyhedron(corners, faces, pgroup) - >>> tetra.size - 4 - >>> tetra.edges - {(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)} - >>> tetra.corners - (w, x, y, z) - - It can be rotated with an arbitrary permutation of vertices, e.g. - the following permutation is not in the pgroup: - - >>> tetra.rotate(Permutation([0, 1, 3, 2])) - >>> tetra.corners - (w, x, z, y) - - An allowed permutation of the vertices can be constructed by - repeatedly applying permutations from the pgroup to the vertices. - Here is a demonstration that applying p and p**2 for every p in - pgroup generates all the orientations of a tetrahedron and no others: - - >>> all = ( (w, x, y, z), \ - (x, y, w, z), \ - (y, w, x, z), \ - (w, z, x, y), \ - (z, w, y, x), \ - (w, y, z, x), \ - (y, z, w, x), \ - (x, z, y, w), \ - (z, y, x, w), \ - (y, x, z, w), \ - (x, w, z, y), \ - (z, x, w, y) ) - - >>> got = [] - >>> for p in (pgroup + [p**2 for p in pgroup]): - ... h = Polyhedron(corners) - ... h.rotate(p) - ... got.append(h.corners) - ... - >>> set(got) == set(all) - True - - The make_perm method of a PermutationGroup will randomly pick - permutations, multiply them together, and return the permutation that - can be applied to the polyhedron to give the orientation produced - by those individual permutations. - - Here, 3 permutations are used: - - >>> tetra.pgroup.make_perm(3) # doctest: +SKIP - Permutation([0, 3, 1, 2]) - - To select the permutations that should be used, supply a list - of indices to the permutations in pgroup in the order they should - be applied: - - >>> use = [0, 0, 2] - >>> p002 = tetra.pgroup.make_perm(3, use) - >>> p002 - Permutation([1, 0, 3, 2]) - - - Apply them one at a time: - - >>> tetra.reset() - >>> for i in use: - ... tetra.rotate(pgroup[i]) - ... - >>> tetra.vertices - (x, w, z, y) - >>> sequentially = tetra.vertices - - Apply the composite permutation: - - >>> tetra.reset() - >>> tetra.rotate(p002) - >>> tetra.corners - (x, w, z, y) - >>> tetra.corners in all and tetra.corners == sequentially - True - - Notes - ===== - - Defining permutation groups - --------------------------- - - It is not necessary to enter any permutations, nor is necessary to - enter a complete set of transforations. In fact, for a polyhedron, - all configurations can be constructed from just two permutations. - For example, the orientations of a tetrahedron can be generated from - an axis passing through a vertex and face and another axis passing - through a different vertex or from an axis passing through the - midpoints of two edges opposite of each other. - - For simplicity of presentation, consider a square -- - not a cube -- with vertices 1, 2, 3, and 4: - - 1-----2 We could think of axes of rotation being: - | | 1) through the face - | | 2) from midpoint 1-2 to 3-4 or 1-3 to 2-4 - 3-----4 3) lines 1-4 or 2-3 - - - To determine how to write the permutations, imagine 4 cameras, - one at each corner, labeled A-D: - - A B A B - 1-----2 1-----3 vertex index: - | | | | 1 0 - | | | | 2 1 - 3-----4 2-----4 3 2 - C D C D 4 3 - - original after rotation - along 1-4 - - A diagonal and a face axis will be chosen for the "permutation group" - from which any orientation can be constructed. - - >>> pgroup = [] - - Imagine a clockwise rotation when viewing 1-4 from camera A. The new - orientation is (in camera-order): 1, 3, 2, 4 so the permutation is - given using the *indices* of the vertices as: - - >>> pgroup.append(Permutation((0, 2, 1, 3))) - - Now imagine rotating clockwise when looking down an axis entering the - center of the square as viewed. The new camera-order would be - 3, 1, 4, 2 so the permutation is (using indices): - - >>> pgroup.append(Permutation((2, 0, 3, 1))) - - The square can now be constructed: - ** use real-world labels for the vertices, entering them in - camera order - ** for the faces we use zero-based indices of the vertices - in *edge-order* as the face is traversed; neither the - direction nor the starting point matter -- the faces are - only used to define edges (if so desired). - - >>> square = Polyhedron((1, 2, 3, 4), [(0, 1, 3, 2)], pgroup) - - To rotate the square with a single permutation we can do: - - >>> square.rotate(square.pgroup[0]); square.corners - (1, 3, 2, 4) - - To use more than one permutation (or to use one permutation more - than once) it is more convenient to use the make_perm method: - - >>> p011 = square.pgroup.make_perm([0,1,1]) # diag flip + 2 rotations - >>> square.reset() # return to initial orientation - >>> square.rotate(p011); square.corners - (4, 2, 3, 1) - - Thinking outside the box - ------------------------ - - Although the Polyhedron object has a direct physical meaning, it - actually has broader application. In the most general sense it is - just a decorated PermutationGroup, allowing one to connect the - permutations to something physical. For example, a Rubik's cube is - not a proper polyhedron, but the Polyhedron class can be used to - represent it in a way that helps to visualize the Rubik's cube. - - >>> from sympy.utilities.iterables import flatten, unflatten - >>> from sympy import symbols - >>> from sympy.combinatorics import RubikGroup - >>> facelets = flatten([symbols(s+'1:5') for s in 'UFRBLD']) - >>> def show(): - ... pairs = unflatten(r2.corners, 2) - ... print(pairs[::2]) - ... print(pairs[1::2]) - ... - >>> r2 = Polyhedron(facelets, pgroup=RubikGroup(2)) - >>> show() - [(U1, U2), (F1, F2), (R1, R2), (B1, B2), (L1, L2), (D1, D2)] - [(U3, U4), (F3, F4), (R3, R4), (B3, B4), (L3, L4), (D3, D4)] - >>> r2.rotate(0) # cw rotation of F - >>> show() - [(U1, U2), (F3, F1), (U3, R2), (B1, B2), (L1, D1), (R3, R1)] - [(L4, L2), (F4, F2), (U4, R4), (B3, B4), (L3, D2), (D3, D4)] - - Predefined Polyhedra - ==================== - - For convenience, the vertices and faces are defined for the following - standard solids along with a permutation group for transformations. - When the polyhedron is oriented as indicated below, the vertices in - a given horizontal plane are numbered in ccw direction, starting from - the vertex that will give the lowest indices in a given face. (In the - net of the vertices, indices preceded by "-" indicate replication of - the lhs index in the net.) - - tetrahedron, tetrahedron_faces - ------------------------------ - - 4 vertices (vertex up) net: - - 0 0-0 - 1 2 3-1 - - 4 faces: - - (0,1,2) (0,2,3) (0,3,1) (1,2,3) - - cube, cube_faces - ---------------- - - 8 vertices (face up) net: - - 0 1 2 3-0 - 4 5 6 7-4 - - 6 faces: - - (0,1,2,3) - (0,1,5,4) (1,2,6,5) (2,3,7,6) (0,3,7,4) - (4,5,6,7) - - octahedron, octahedron_faces - ---------------------------- - - 6 vertices (vertex up) net: - - 0 0 0-0 - 1 2 3 4-1 - 5 5 5-5 - - 8 faces: - - (0,1,2) (0,2,3) (0,3,4) (0,1,4) - (1,2,5) (2,3,5) (3,4,5) (1,4,5) - - dodecahedron, dodecahedron_faces - -------------------------------- - - 20 vertices (vertex up) net: - - 0 1 2 3 4 -0 - 5 6 7 8 9 -5 - 14 10 11 12 13-14 - 15 16 17 18 19-15 - - 12 faces: - - (0,1,2,3,4) - (0,1,6,10,5) (1,2,7,11,6) (2,3,8,12,7) (3,4,9,13,8) (0,4,9,14,5) - (5,10,16,15,14) ( - 6,10,16,17,11) (7,11,17,18,12) (8,12,18,19,13) (9,13,19,15,14) - (15,16,17,18,19) - - icosahedron, icosahedron_faces - ------------------------------ - - 12 vertices (face up) net: - - 0 0 0 0 -0 - 1 2 3 4 5 -1 - 6 7 8 9 10 -6 - 11 11 11 11 -11 - - 20 faces: - - (0,1,2) (0,2,3) (0,3,4) (0,4,5) (0,1,5) - (1,2,6) (2,3,7) (3,4,8) (4,5,9) (1,5,10) - (2,6,7) (3,7,8) (4,8,9) (5,9,10) (1,6,10) - (6,7,11,) (7,8,11) (8,9,11) (9,10,11) (6,10,11) - - >>> from sympy.combinatorics.polyhedron import cube - >>> cube.edges - {(0, 1), (0, 3), (0, 4), '...', (4, 7), (5, 6), (6, 7)} - - If you want to use letters or other names for the corners you - can still use the pre-calculated faces: - - >>> corners = list('abcdefgh') - >>> Polyhedron(corners, cube.faces).corners - (a, b, c, d, e, f, g, h) - - References - ========== - - [1] www.ocf.berkeley.edu/~wwu/articles/platonicsolids.pdf - - """ - faces = [minlex(f, directed=False, is_set=True) for f in faces] - corners, faces, pgroup = args = \ - [Tuple(*a) for a in (corners, faces, pgroup)] - obj = Basic.__new__(cls, *args) - obj._corners = tuple(corners) # in order given - obj._faces = FiniteSet(faces) - if pgroup and pgroup[0].size != len(corners): - raise ValueError("Permutation size unequal to number of corners.") - # use the identity permutation if none are given - obj._pgroup = PermutationGroup(( - pgroup or [Perm(list(range(len(corners))))] )) - return obj - - @property -
    [docs] def corners(self): - """ - Get the corners of the Polyhedron. - - The method ``vertices`` is an alias for ``corners``. - - Examples - ======== - - >>> from sympy.combinatorics import Polyhedron - >>> from sympy.abc import a, b, c, d - >>> p = Polyhedron(list('abcd')) - >>> p.corners == p.vertices == (a, b, c, d) - True - - See Also - ======== - - array_form, cyclic_form - """ - return self._corners
    - vertices = corners - - @property -
    [docs] def array_form(self): - """Return the indices of the corners. - - The indices are given relative to the original position of corners. - - Examples - ======== - - >>> from sympy.combinatorics import Permutation, Cycle - >>> from sympy.combinatorics.polyhedron import tetrahedron - >>> tetrahedron.array_form - [0, 1, 2, 3] - - >>> tetrahedron.rotate(0) - >>> tetrahedron.array_form - [0, 2, 3, 1] - >>> tetrahedron.pgroup[0].array_form - [0, 2, 3, 1] - - See Also - ======== - - corners, cyclic_form - """ - corners = list(self.args[0]) - return [corners.index(c) for c in self.corners] -
    - @property -
    [docs] def cyclic_form(self): - """Return the indices of the corners in cyclic notation. - - The indices are given relative to the original position of corners. - - See Also - ======== - - corners, array_form - """ - return Perm._af_new(self.array_form).cyclic_form -
    - @property -
    [docs] def size(self): - """ - Get the number of corners of the Polyhedron. - """ - return len(self._corners) -
    - @property -
    [docs] def faces(self): - """ - Get the faces of the Polyhedron. - """ - return self._faces -
    - @property -
    [docs] def pgroup(self): - """ - Get the permutations of the Polyhedron. - """ - return self._pgroup -
    - @property -
    [docs] def edges(self): - """ - Given the faces of the polyhedra we can get the edges. - - Examples - ======== - - >>> from sympy.combinatorics import Polyhedron - >>> from sympy.abc import a, b, c - >>> corners = (a, b, c) - >>> faces = [(0, 1, 2)] - >>> Polyhedron(corners, faces).edges - {(0, 1), (0, 2), (1, 2)} - - """ - if self._edges is None: - output = set() - for face in self.faces: - for i in range(len(face)): - edge = tuple(sorted([face[i], face[i - 1]])) - output.add(edge) - self._edges = FiniteSet(*output) - return self._edges -
    -
    [docs] def rotate(self, perm): - """ - Apply a permutation to the polyhedron *in place*. The permutation - may be given as a Permutation instance or an integer indicating - which permutation from pgroup of the Polyhedron should be - applied. - - This is an operation that is analogous to rotation about - an axis by a fixed increment. - - Notes - ===== - - When a Permutation is applied, no check is done to see if that - is a valid permutation for the Polyhedron. For example, a cube - could be given a permutation which effectively swaps only 2 - vertices. A valid permutation (that rotates the object in a - physical way) will be obtained if one only uses - permutations from the ``pgroup`` of the Polyhedron. On the other - hand, allowing arbitrary rotations (applications of permutations) - gives a way to follow named elements rather than indices since - Polyhedron allows vertices to be named while Permutation works - only with indices. - - Examples - ======== - - >>> from sympy.combinatorics import Polyhedron, Permutation - >>> from sympy.combinatorics.polyhedron import cube - >>> cube.corners - (0, 1, 2, 3, 4, 5, 6, 7) - >>> cube.rotate(0) - >>> cube.corners - (1, 2, 3, 0, 5, 6, 7, 4) - - A non-physical "rotation" that is not prohibited by this method: - - >>> cube.reset() - >>> cube.rotate(Permutation([[1,2]], size=8)) - >>> cube.corners - (0, 2, 1, 3, 4, 5, 6, 7) - - Polyhedron can be used to follow elements of set that are - identified by letters instead of integers: - - >>> shadow = h5 = Polyhedron(list('abcde')) - >>> p = Permutation([3, 0, 1, 2, 4]) - >>> h5.rotate(p) - >>> h5.corners - (d, a, b, c, e) - >>> _ == shadow.corners - True - >>> copy = h5.copy() - >>> h5.rotate(p) - >>> h5.corners == copy.corners - False - """ - if not isinstance(perm, Perm): - perm = self.pgroup[perm] - # and we know it's valid - else: - if perm.size != self.size: - raise ValueError('Polyhedron and Permutation sizes differ.') - a = perm.array_form - corners = [self.corners[a[i]] for i in range(len(self.corners))] - self._corners = tuple(corners) -
    -
    [docs] def reset(self): - """Return corners to their original positions. - - Examples - ======== - - >>> from sympy.combinatorics.polyhedron import tetrahedron as T - >>> T.corners - (0, 1, 2, 3) - >>> T.rotate(0) - >>> T.corners - (0, 2, 3, 1) - >>> T.reset() - >>> T.corners - (0, 1, 2, 3) - """ - self._corners = self.args[0] - -
    -def _pgroup_calcs(): - """Return the permutation groups for each of the polyhedra and the face - definitions: tetrahedron, cube, octahedron, dodecahedron, icosahedron, - tetrahedron_faces, cube_faces, octahedron_faces, dodecahedron_faces, - icosahedron_faces - - (This author didn't find and didn't know of a better way to do it though - there likely is such a way.) - - Although only 2 permutations are needed for a polyhedron in order to - generate all the possible orientations, a group of permutations is - provided instead. A set of permutations is called a "group" if:: - - a*b = c (for any pair of permutations in the group, a and b, their - product, c, is in the group) - - a*(b*c) = (a*b)*c (for any 3 permutations in the group associativity holds) - - there is an identity permutation, I, such that I*a = a*I for all elements - in the group - - a*b = I (the inverse of each permutation is also in the group) - - None of the polyhedron groups defined follow these definitions of a group. - Instead, they are selected to contain those permutations whose powers - alone will construct all orientations of the polyhedron, i.e. for - permutations ``a``, ``b``, etc... in the group, ``a, a**2, ..., a**o_a``, - ``b, b**2, ..., b**o_b``, etc... (where ``o_i`` is the order of - permutation ``i``) generate all permutations of the polyhedron instead of - mixed products like ``a*b``, ``a*b**2``, etc.... - - Note that for a polyhedron with n vertices, the valid permutations of the - vertices exclude those that do not maintain its faces. e.g. the - permutation BCDE of a square's four corners, ABCD, is a valid - permutation while CBDE is not (because this would twist the square). - - Examples - ======== - - The is_group checks for: closure, the presence of the Identity permutation, - and the presence of the inverse for each of the elements in the group. This - confirms that none of the polyhedra are true groups: - - >>> from sympy.combinatorics.polyhedron import ( - ... tetrahedron, cube, octahedron, dodecahedron, icosahedron) - ... - >>> polyhedra = (tetrahedron, cube, octahedron, dodecahedron, icosahedron) - >>> [h.pgroup.is_group() for h in polyhedra] - ... - [False, False, False, False, False] - - Although tests in polyhedron's test suite check that powers of the - permutations in the groups generate all permutations of the vertices - of the polyhedron, here we also demonstrate the powers of the given - permutations create a complete group for the tetrahedron: - - >>> from sympy.combinatorics import Permutation, PermutationGroup - >>> for h in polyhedra[:1]: - ... G = h.pgroup - ... perms = set() - ... for g in G: - ... for e in range(g.order()): - ... p = tuple((g**e).array_form) - ... perms.add(p) - ... - ... perms = [Permutation(p) for p in perms] - ... assert PermutationGroup(perms).is_group() - - In addition to doing the above, the tests in the suite confirm that the - faces are all present after the application of each permutation. - - References - ========== - - http://dogschool.tripod.com/trianglegroup.html - """ - def _pgroup_of_double(polyh, ordered_faces, pgroup): - n = len(ordered_faces[0]) - # the vertices of the double which sits inside a give polyhedron - # can be found by tracking the faces of the outer polyhedron. - # A map between face and the vertex of the double is made so that - # after rotation the position of the vertices can be located - fmap = dict(list(zip(ordered_faces, - list(range(len(ordered_faces)))))) - flat_faces = flatten(ordered_faces) - new_pgroup = [] - for i, p in enumerate(pgroup): - h = polyh.copy() - h.rotate(p) - c = h.corners - # reorder corners in the order they should appear when - # enumerating the faces - reorder = unflatten([c[j] for j in flat_faces], n) - # make them canonical - reorder = [tuple(map(as_int, - minlex(f, directed=False, is_set=True))) - for f in reorder] - # map face to vertex: the resulting list of vertices are the - # permutation that we seek for the double - new_pgroup.append(Perm([fmap[f] for f in reorder])) - return new_pgroup - - tetrahedron_faces = [ - (0, 1, 2), (0, 2, 3), (0, 3, 1), # upper 3 - (1, 2, 3), # bottom - ] - - # cw from top - # - _t_pgroup = [ - Perm([[1, 2, 3], [0]]), # cw from top - Perm([[0, 1, 2], [3]]), # cw from front face - Perm([[0, 3, 2], [1]]), # cw from back right face - Perm([[0, 3, 1], [2]]), # cw from back left face - Perm([[0, 1], [2, 3]]), # through front left edge - Perm([[0, 2], [1, 3]]), # through front right edge - Perm([[0, 3], [1, 2]]), # through back edge - ] - - tetrahedron = Polyhedron( - list(range(4)), - tetrahedron_faces, - _t_pgroup) - - cube_faces = [ - (0, 1, 2, 3), # upper - (0, 1, 5, 4), (1, 2, 6, 5), (2, 3, 7, 6), (0, 3, 7, 4), # middle 4 - (4, 5, 6, 7), # lower - ] - - # U, D, F, B, L, R = up, down, front, back, left, right - _c_pgroup = [Perm(p) for p in - [ - [1, 2, 3, 0, 5, 6, 7, 4], # cw from top, U - [4, 0, 3, 7, 5, 1, 2, 6], # cw from F face - [4, 5, 1, 0, 7, 6, 2, 3], # cw from R face - - [1, 0, 4, 5, 2, 3, 7, 6], # cw through UF edge - [6, 2, 1, 5, 7, 3, 0, 4], # cw through UR edge - [6, 7, 3, 2, 5, 4, 0, 1], # cw through UB edge - [3, 7, 4, 0, 2, 6, 5, 1], # cw through UL edge - [4, 7, 6, 5, 0, 3, 2, 1], # cw through FL edge - [6, 5, 4, 7, 2, 1, 0, 3], # cw through FR edge - - [0, 3, 7, 4, 1, 2, 6, 5], # cw through UFL vertex - [5, 1, 0, 4, 6, 2, 3, 7], # cw through UFR vertex - [5, 6, 2, 1, 4, 7, 3, 0], # cw through UBR vertex - [7, 4, 0, 3, 6, 5, 1, 2], # cw through UBL - ]] - - cube = Polyhedron( - list(range(8)), - cube_faces, - _c_pgroup) - - octahedron_faces = [ - (0, 1, 2), (0, 2, 3), (0, 3, 4), (0, 1, 4), # top 4 - (1, 2, 5), (2, 3, 5), (3, 4, 5), (1, 4, 5), # bottom 4 - ] - - octahedron = Polyhedron( - list(range(6)), - octahedron_faces, - _pgroup_of_double(cube, cube_faces, _c_pgroup)) - - dodecahedron_faces = [ - (0, 1, 2, 3, 4), # top - (0, 1, 6, 10, 5), (1, 2, 7, 11, 6), (2, 3, 8, 12, 7), # upper 5 - (3, 4, 9, 13, 8), (0, 4, 9, 14, 5), - (5, 10, 16, 15, 14), (6, 10, 16, 17, 11), (7, 11, 17, 18, - 12), # lower 5 - (8, 12, 18, 19, 13), (9, 13, 19, 15, 14), - (15, 16, 17, 18, 19) # bottom - ] - - def _string_to_perm(s): - rv = [Perm(list(range(20)))] - p = None - for si in s: - if si not in '01': - count = int(si) - 1 - else: - count = 1 - if si == '0': - p = _f0 - elif si == '1': - p = _f1 - rv.extend([p]*count) - return Perm.rmul(*rv) - - # top face cw - _f0 = Perm([ - 1, 2, 3, 4, 0, 6, 7, 8, 9, 5, 11, - 12, 13, 14, 10, 16, 17, 18, 19, 15]) - # front face cw - _f1 = Perm([ - 5, 0, 4, 9, 14, 10, 1, 3, 13, 15, - 6, 2, 8, 19, 16, 17, 11, 7, 12, 18]) - # the strings below, like 0104 are shorthand for F0*F1*F0**4 and are - # the remaining 4 face rotations, 15 edge permutations, and the - # 10 vertex rotations. - _dodeca_pgroup = [_f0, _f1] + [_string_to_perm(s) for s in ''' - 0104 140 014 0410 - 010 1403 03104 04103 102 - 120 1304 01303 021302 03130 - 0412041 041204103 04120410 041204104 041204102 - 10 01 1402 0140 04102 0412 1204 1302 0130 03120'''.strip().split()] - - dodecahedron = Polyhedron( - list(range(20)), - dodecahedron_faces, - _dodeca_pgroup) - - icosahedron_faces = [ - [0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 1, 5], - [1, 6, 7], [1, 2, 7], [2, 7, 8], [2, 3, 8], [3, 8, 9 ], - [3, 4, 9], [4, 9, 10 ], [4, 5, 10], [5, 6, 10], [1, 5, 6 ], - [6, 7, 11], [7, 8, 11], [8, 9, 11], [9, 10, 11], [6, 10, 11]] - - icosahedron = Polyhedron( - list(range(12)), - icosahedron_faces, - _pgroup_of_double( - dodecahedron, dodecahedron_faces, _dodeca_pgroup)) - - return (tetrahedron, cube, octahedron, dodecahedron, icosahedron, - tetrahedron_faces, cube_faces, octahedron_faces, - dodecahedron_faces, icosahedron_faces) - -(tetrahedron, cube, octahedron, dodecahedron, icosahedron, -tetrahedron_faces, cube_faces, octahedron_faces, -dodecahedron_faces, icosahedron_faces) = _pgroup_calcs() -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/combinatorics/prufer.html b/dev-py3k/_modules/sympy/combinatorics/prufer.html deleted file mode 100644 index 55e0cc8f086..00000000000 --- a/dev-py3k/_modules/sympy/combinatorics/prufer.html +++ /dev/null @@ -1,551 +0,0 @@ - - - - - - - - - - sympy.combinatorics.prufer — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.combinatorics.prufer

    -from sympy.core import Basic
    -from sympy.core.compatibility import iterable, as_int
    -from sympy.utilities.iterables import flatten
    -
    -from collections import defaultdict
    -
    -
    -
    [docs]class Prufer(Basic): - """ - The Prufer correspondence is an algorithm that describes the - bijection between labeled trees and the Prufer code. A Prufer - code of a labeled tree is unique up to isomorphism and has - a length of n - 2. - - Prufer sequences were first used by Heinz Prufer to give a - proof of Cayley's formula. - - References - ========== - - .. [1] http://mathworld.wolfram.com/LabeledTree.html - - """ - _prufer_repr = None - _tree_repr = None - _nodes = None - _rank = None - - @property -
    [docs] def prufer_repr(self): - """Returns Prufer sequence for the Prufer object. - - This sequence is found by removing the highest numbered vertex, - recording the node it was attached to, and continuuing until only - two verices remain. The Prufer sequence is the list of recorded nodes. - - Examples - ======== - - >>> from sympy.combinatorics.prufer import Prufer - >>> Prufer([[0, 3], [1, 3], [2, 3], [3, 4], [4, 5]]).prufer_repr - [3, 3, 3, 4] - >>> Prufer([1, 0, 0]).prufer_repr - [1, 0, 0] - - See Also - ======== - - to_prufer - - """ - if self._prufer_repr is None: - self._prufer_repr = self.to_prufer(self._tree_repr[:], self.nodes) - return self._prufer_repr -
    - @property -
    [docs] def tree_repr(self): - """Returns the tree representation of the Prufer object. - - Examples - ======== - - >>> from sympy.combinatorics.prufer import Prufer - >>> Prufer([[0, 3], [1, 3], [2, 3], [3, 4], [4, 5]]).tree_repr - [[0, 3], [1, 3], [2, 3], [3, 4], [4, 5]] - >>> Prufer([1, 0, 0]).tree_repr - [[1, 2], [0, 1], [0, 3], [0, 4]] - - See Also - ======== - - to_tree - - """ - if self._tree_repr is None: - self._tree_repr = self.to_tree(self._prufer_repr[:]) - return self._tree_repr -
    - @property -
    [docs] def nodes(self): - """Returns the number of nodes in the tree. - - Examples - ======== - - >>> from sympy.combinatorics.prufer import Prufer - >>> Prufer([[0, 3], [1, 3], [2, 3], [3, 4], [4, 5]]).nodes - 6 - >>> Prufer([1, 0, 0]).nodes - 5 - - """ - return self._nodes -
    - @property -
    [docs] def rank(self): - """Returns the rank of the Prufer sequence. - - Examples - ======== - - >>> from sympy.combinatorics.prufer import Prufer - >>> p = Prufer([[0, 3], [1, 3], [2, 3], [3, 4], [4, 5]]) - >>> p.rank - 778 - >>> p.next(1).rank - 779 - >>> p.prev().rank - 777 - - See Also - ======== - - prufer_rank, next, prev, size - - """ - if self._rank is None: - self._rank = self.prufer_rank() - return self._rank -
    - @property -
    [docs] def size(self): - """Return the number of possible trees of this Prufer object. - - Examples - ======== - - >>> from sympy.combinatorics.prufer import Prufer - >>> Prufer([0]*4).size == Prufer([6]*4).size == 1296 - True - - See Also - ======== - - prufer_rank, rank, next, prev - - """ - return self.prev(self.rank).prev().rank + 1 -
    - @staticmethod -
    [docs] def to_prufer(tree, n): - """Return the Prufer sequence for a tree given as a list of edges where - ``n`` is the number of nodes in the tree. - - Examples - ======== - - >>> from sympy.combinatorics.prufer import Prufer - >>> a = Prufer([[0, 1], [0, 2], [0, 3]]) - >>> a.prufer_repr - [0, 0] - >>> Prufer.to_prufer([[0, 1], [0, 2], [0, 3]], 4) - [0, 0] - - See Also - ======== - prufer_repr: returns Prufer sequence of a Prufer object. - - """ - d = defaultdict(int) - L = [] - for edge in tree: - # Increment the value of the corresponding - # node in the degree list as we encounter an - # edge involving it. - d[edge[0]] += 1 - d[edge[1]] += 1 - for i in range(n - 2): - # find the smallest leaf - for x in range(n): - if d[x] == 1: - break - # find the node it was connected to - y = None - for edge in tree: - if x == edge[0]: - y = edge[1] - elif x == edge[1]: - y = edge[0] - if y is not None: - break - # record and update - L.append(y) - for j in (x, y): - d[j] -= 1 - if not d[j]: - d.pop(j) - tree.remove(edge) - return L -
    - @staticmethod -
    [docs] def to_tree(prufer): - """Return the tree (as a list of edges) of the given Prufer sequence. - - Examples - ======== - - >>> from sympy.combinatorics.prufer import Prufer - >>> a = Prufer([0, 2], 4) - >>> a.tree_repr - [[0, 1], [0, 2], [2, 3]] - >>> Prufer.to_tree([0, 2]) - [[0, 1], [0, 2], [2, 3]] - - References - ========== - - - http://hamberg.no/erlend/2010/11/06/prufer-sequence/ - - See Also - ======== - tree_repr: returns tree representation of a Prufer object. - - """ - tree = [] - last = [] - n = len(prufer) + 2 - d = defaultdict(lambda: 1) - for p in prufer: - d[p] += 1 - for i in prufer: - for j in range(n): - # find the smallest leaf (degree = 1) - if d[j] == 1: - break - # (i, j) is the new edge that we append to the tree - # and remove from the degree dictionary - d[i] -= 1 - d[j] -= 1 - tree.append(sorted([i, j])) - last = [i for i in range(n) if d[i] == 1] or [0, 1] - tree.append(last) - - return tree -
    - @staticmethod -
    [docs] def edges(*runs): - """Return a list of edges and the number of nodes from the given runs - that connect nodes in an integer-labelled tree. - - All node numbers will be shifted so that the minimum node is 0. It is - not a problem if edges are repeated in the runs; only unique edges are - returned. There is no assumption made about what the range of the node - labels should be, but all nodes from the smallest through the largest - must be present. - - Examples - ======== - - >>> from sympy.combinatorics.prufer import Prufer - >>> Prufer.edges([1, 2, 3], [2, 4, 5]) # a T - ([[0, 1], [3, 4], [1, 2], [1, 3]], 5) - - Duplicate edges are removed: - - >>> Prufer.edges([0, 1, 2, 3], [1, 4, 5], [1, 4, 6]) # a K - ([[0, 1], [1, 2], [4, 6], [4, 5], [1, 4], [2, 3]], 7) - - """ - e = set() - nmin = runs[0][0] - for r in runs: - for i in range(len(r) - 1): - a, b = r[i: i + 2] - if b < a: - a, b = b, a - e.add((a, b)) - rv = [] - got = set() - nmin = nmax = None - while e: - ei = e.pop() - for i in ei: - got.add(i) - nmin = min(ei[0], nmin) if nmin is not None else ei[0] - nmax = max(ei[1], nmax) if nmax is not None else ei[1] - rv.append(list(ei)) - missing = set(range(nmin, nmax + 1)) - got - if missing: - missing = [i + nmin for i in missing] - if len(missing) == 1: - msg = 'Node %s is missing.' % missing.pop() - else: - msg = 'Nodes %s are missing.' % list(sorted(missing)) - raise ValueError(msg) - if nmin != 0: - for i, ei in enumerate(rv): - rv[i] = [n - nmin for n in ei] - nmax -= nmin - return rv, nmax + 1 -
    -
    [docs] def prufer_rank(self): - """Computes the rank of a Prufer sequence. - - Examples - ======== - - >>> from sympy.combinatorics.prufer import Prufer - >>> a = Prufer([[0, 1], [0, 2], [0, 3]]) - >>> a.prufer_rank() - 0 - - See Also - ======== - - rank, next, prev, size - - """ - r = 0 - p = 1 - for i in range(self.nodes - 3, -1, -1): - r += p*self.prufer_repr[i] - p *= self.nodes - return r -
    - @classmethod -
    [docs] def unrank(self, rank, n): - """Finds the unranked Prufer sequence. - - Examples - ======== - - >>> from sympy.combinatorics.prufer import Prufer - >>> Prufer.unrank(0, 4) - Prufer([0, 0]) - - """ - n, rank = as_int(n), as_int(rank) - L = defaultdict(int) - for i in range(n - 3, -1, -1): - L[i] = rank % n - rank = (rank - L[i])//n - return Prufer([L[i] for i in range(len(L))]) -
    - def __new__(cls, *args, **kw_args): - """The constructor for the Prufer object. - - Examples - ======== - - >>> from sympy.combinatorics.prufer import Prufer - - A Prufer object can be constructed from a list of edges: - - >>> a = Prufer([[0, 1], [0, 2], [0, 3]]) - >>> a.prufer_repr - [0, 0] - - If the number of nodes is given, no checking of the nodes will - be performed; it will be assumed that nodes 0 through n - 1 are - present: - - >>> Prufer([[0, 1], [0, 2], [0, 3]], 4) - Prufer([[0, 1], [0, 2], [0, 3]], 4) - - A Prufer object can be constructed from a Prufer sequence: - - >>> b = Prufer([1, 3]) - >>> b.tree_repr - [[0, 1], [1, 3], [2, 3]] - - """ - ret_obj = Basic.__new__(cls, *args, **kw_args) - args = [list(args[0])] - if args[0] and iterable(args[0][0]): - if not args[0][0]: - raise ValueError( - 'Prufer expects at least one edge in the tree.') - if len(args) > 1: - nnodes = args[1] - else: - nodes = set(flatten(args[0])) - nnodes = max(nodes) + 1 - if nnodes != len(nodes): - missing = set(range(nnodes)) - nodes - if len(missing) == 1: - msg = 'Node %s is missing.' % missing.pop() - else: - msg = 'Nodes %s are missing.' % list(sorted(missing)) - raise ValueError(msg) - ret_obj._tree_repr = [list(i) for i in args[0]] - ret_obj._nodes = nnodes - else: - ret_obj._prufer_repr = args[0] - ret_obj._nodes = len(ret_obj._prufer_repr) + 2 - return ret_obj - -
    [docs] def next(self, delta=1): - """Generates the Prufer sequence that is delta beyond the current one. - - Examples - ======== - - >>> from sympy.combinatorics.prufer import Prufer - >>> a = Prufer([[0, 1], [0, 2], [0, 3]]) - >>> b = a.next(1) # == a.next() - >>> b.tree_repr - [[0, 2], [0, 1], [1, 3]] - >>> b.rank - 1 - - See Also - ======== - - prufer_rank, rank, prev, size - - """ - return Prufer.unrank(self.rank + delta, self.nodes) -
    -
    [docs] def prev(self, delta=1): - """Generates the Prufer sequence that is -delta before the current one. - - Examples - ======== - - >>> from sympy.combinatorics.prufer import Prufer - >>> a = Prufer([[0, 1], [1, 2], [2, 3], [1, 4]]) - >>> a.rank - 36 - >>> b = a.prev() - >>> b - Prufer([1, 2, 0]) - >>> b.rank - 35 - - See Also - ======== - - prufer_rank, rank, next, size - - """ - return Prufer.unrank(self.rank - delta, self.nodes)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/combinatorics/subsets.html b/dev-py3k/_modules/sympy/combinatorics/subsets.html deleted file mode 100644 index c934beca3ff..00000000000 --- a/dev-py3k/_modules/sympy/combinatorics/subsets.html +++ /dev/null @@ -1,637 +0,0 @@ - - - - - - - - - - sympy.combinatorics.subsets — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.combinatorics.subsets

    -from sympy.core import Basic
    -from sympy.combinatorics.graycode import GrayCode
    -
    -from sympy.core.compatibility import bin, combinations
    -
    -
    -
    [docs]class Subset(Basic): - """ - Represents a basic subset object. - - We generate subsets using essentially two techniques, - binary enumeration and lexicographic enumeration. - The Subset class takes two arguments, the first one - describes the initial subset to consider and the second - describes the superset. - - Examples - ======== - - >>> from sympy.combinatorics.subsets import Subset - >>> a = Subset(['c','d'], ['a','b','c','d']) - >>> a.next_binary().subset - ['b'] - >>> a.prev_binary().subset - ['c'] - """ - - _rank_binary = None - _rank_lex = None - _rank_graycode = None - _subset = None - _superset = None - - def __new__(cls, subset, superset): - """ - Default constructor. - - It takes the subset and its superset as its parameters. - - Examples - ======== - - >>> from sympy.combinatorics.subsets import Subset - >>> a = Subset(['c','d'], ['a','b','c','d']) - >>> a.subset - ['c', 'd'] - >>> a.superset - ['a', 'b', 'c', 'd'] - >>> a.size - 2 - """ - if len(subset) > len(superset): - raise ValueError('Invalid arguments have been provided. The superset must be larger than the subset.') - for elem in subset: - if elem not in superset: - raise ValueError('The superset provided is invalid as it does not contain the element %i' % elem) - obj = Basic.__new__(cls) - obj._subset = subset - obj._superset = superset - return obj - -
    [docs] def iterate_binary(self, k): - """ - This is a helper function. It iterates over the - binary subsets by k steps. This variable can be - both positive or negative. - - Examples - ======== - - >>> from sympy.combinatorics.subsets import Subset - >>> a = Subset(['c','d'], ['a','b','c','d']) - >>> a.iterate_binary(-2).subset - ['d'] - >>> a = Subset(['a','b','c'], ['a','b','c','d']) - >>> a.iterate_binary(2).subset - [] - - See Also - ======== - next_binary, prev_binary - """ - bin_list = Subset.bitlist_from_subset(self.subset, self.superset) - n = (int(''.join(bin_list), 2) + k) % 2**self.superset_size - bits = bin(n)[2:].rjust(self.superset_size, '0') - return Subset.subset_from_bitlist(self.superset, bits) -
    -
    [docs] def next_binary(self): - """ - Generates the next binary ordered subset. - - Examples - ======== - - >>> from sympy.combinatorics.subsets import Subset - >>> a = Subset(['c','d'], ['a','b','c','d']) - >>> a.next_binary().subset - ['b'] - >>> a = Subset(['a','b','c','d'], ['a','b','c','d']) - >>> a.next_binary().subset - [] - - See Also - ======== - prev_binary, iterate_binary - """ - return self.iterate_binary(1) -
    -
    [docs] def prev_binary(self): - """ - Generates the previous binary ordered subset. - - Examples - ======== - - >>> from sympy.combinatorics.subsets import Subset - >>> a = Subset([], ['a','b','c','d']) - >>> a.prev_binary().subset - ['a', 'b', 'c', 'd'] - >>> a = Subset(['c','d'], ['a','b','c','d']) - >>> a.prev_binary().subset - ['c'] - - See Also - ======== - next_binary, iterate_binary - """ - return self.iterate_binary(-1) -
    -
    [docs] def next_lexicographic(self): - """ - Generates the next lexicographically ordered subset. - - NOT IMPLEMENTED - """ - raise NotImplementedError() -
    -
    [docs] def prev_lexicographic(self): - """ - Generates the previous lexicographically ordered subset. - - NOT YET IMPLEMENTED - """ - raise NotImplementedError() -
    -
    [docs] def iterate_graycode(self, k): - """ - Helper function used for prev_gray and next_gray. - It performs k step overs to get the respective Gray codes. - - Examples - ======== - - >>> from sympy.combinatorics.subsets import Subset - >>> a = Subset([1,2,3], [1,2,3,4]) - >>> a.iterate_graycode(3).subset - [1, 4] - >>> a.iterate_graycode(-2).subset - [1, 2, 4] - - See Also - ======== - next_gray, prev_gray - """ - unranked_code = GrayCode.unrank(self.superset_size, - (self.rank_gray + k) % self.cardinality) - return Subset.subset_from_bitlist(self.superset, - unranked_code) -
    -
    [docs] def next_gray(self): - """ - Generates the next Gray code ordered subset. - - Examples - ======== - - >>> from sympy.combinatorics.subsets import Subset - >>> a = Subset([1,2,3], [1,2,3,4]) - >>> a.next_gray().subset - [1, 3] - - See Also - ======== - iterate_graycode, prev_gray - """ - return self.iterate_graycode(1) -
    -
    [docs] def prev_gray(self): - """ - Generates the previous Gray code ordered subset. - - Examples - ======== - - >>> from sympy.combinatorics.subsets import Subset - >>> a = Subset([2,3,4], [1,2,3,4,5]) - >>> a.prev_gray().subset - [2, 3, 4, 5] - - See Also - ======== - iterate_graycode, next_gray - """ - return self.iterate_graycode(-1) -
    - @property -
    [docs] def rank_binary(self): - """ - Computes the binary ordered rank. - - Examples - ======== - - >>> from sympy.combinatorics.subsets import Subset - >>> a = Subset([], ['a','b','c','d']) - >>> a.rank_binary - 0 - >>> a = Subset(['c','d'], ['a','b','c','d']) - >>> a.rank_binary - 3 - - See Also - ======== - iterate_binary, unrank_binary - """ - if self._rank_binary is None: - self._rank_binary = int("".join( - Subset.bitlist_from_subset(self.subset, - self.superset)), 2) - return self._rank_binary -
    - @property -
    [docs] def rank_lexicographic(self): - """ - Computes the lexicographic ranking of the subset. - - Examples - ======== - - >>> from sympy.combinatorics.subsets import Subset - >>> a = Subset(['c','d'], ['a','b','c','d']) - >>> a.rank_lexicographic - 14 - >>> a = Subset([2,4,5], [1,2,3,4,5,6]) - >>> a.rank_lexicographic - 43 - """ - if self._rank_lex is None: - def _ranklex(self, subset_index, i, n): - if subset_index == [] or i > n: - return 0 - if i in subset_index: - subset_index.remove(i) - return 1 + _ranklex(self, subset_index, i + 1, n) - return 2**(n - i - 1) + _ranklex(self, subset_index, i + 1, n) - indices = Subset.subset_indices(self.subset, self.superset) - self._rank_lex = _ranklex(self, indices, 0, self.superset_size) - return self._rank_lex -
    - @property -
    [docs] def rank_gray(self): - """ - Computes the Gray code ranking of the subset. - - Examples - ======== - - >>> from sympy.combinatorics.subsets import Subset - >>> a = Subset(['c','d'], ['a','b','c','d']) - >>> a.rank_gray - 2 - >>> a = Subset([2,4,5], [1,2,3,4,5,6]) - >>> a.rank_gray - 27 - - See Also - ======== - iterate_graycode, unrank_gray - """ - if self._rank_graycode is None: - bits = Subset.bitlist_from_subset(self.subset, self.superset) - self._rank_graycode = GrayCode(len(bits), start=bits).rank - return self._rank_graycode -
    - @property -
    [docs] def subset(self): - """ - Gets the subset represented by the current instance. - - Examples - ======== - - >>> from sympy.combinatorics.subsets import Subset - >>> a = Subset(['c','d'], ['a','b','c','d']) - >>> a.subset - ['c', 'd'] - - See Also - ======== - superset, size, superset_size, cardinality - """ - return self._subset -
    - @property -
    [docs] def size(self): - """ - Gets the size of the subset. - - Examples - ======== - - >>> from sympy.combinatorics.subsets import Subset - >>> a = Subset(['c','d'], ['a','b','c','d']) - >>> a.size - 2 - - See Also - ======== - subset, superset, superset_size, cardinality - """ - return len(self.subset) -
    - @property -
    [docs] def superset(self): - """ - Gets the superset of the subset. - - Examples - ======== - - >>> from sympy.combinatorics.subsets import Subset - >>> a = Subset(['c','d'], ['a','b','c','d']) - >>> a.superset - ['a', 'b', 'c', 'd'] - - See Also - ======== - subset, size, superset_size, cardinality - """ - return self._superset -
    - @property -
    [docs] def superset_size(self): - """ - Returns the size of the superset. - - Examples - ======== - - >>> from sympy.combinatorics.subsets import Subset - >>> a = Subset(['c','d'], ['a','b','c','d']) - >>> a.superset_size - 4 - - See Also - ======== - subset, superset, size, cardinality - """ - return len(self.superset) -
    - @property -
    [docs] def cardinality(self): - """ - Returns the number of all possible subsets. - - Examples - ======== - - >>> from sympy.combinatorics.subsets import Subset - >>> a = Subset(['c','d'], ['a','b','c','d']) - >>> a.cardinality - 16 - - See Also - ======== - subset, superset, size, superset_size - """ - return 2**(self.superset_size) -
    - @classmethod -
    [docs] def subset_from_bitlist(self, super_set, bitlist): - """ - Gets the subset defined by the bitlist. - - Examples - ======== - - >>> from sympy.combinatorics.subsets import Subset - >>> Subset.subset_from_bitlist(['a','b','c','d'], '0011').subset - ['c', 'd'] - - See Also - ======== - bitlist_from_subset - """ - if len(super_set) != len(bitlist): - raise ValueError("The sizes of the lists are not equal") - ret_set = [] - for i in range(len(bitlist)): - if bitlist[i] == '1': - ret_set.append(super_set[i]) - return Subset(ret_set, super_set) -
    - @classmethod -
    [docs] def bitlist_from_subset(self, subset, superset): - """ - Gets the bitlist corresponding to a subset. - - Examples - ======== - - >>> from sympy.combinatorics.subsets import Subset - >>> Subset.bitlist_from_subset(['c','d'], ['a','b','c','d']) - '0011' - - See Also - ======== - subset_from_bitlist - """ - bitlist = ['0'] * len(superset) - if type(subset) is Subset: - subset = subset.args[0] - for i in Subset.subset_indices(subset, superset): - bitlist[i] = '1' - return ''.join(bitlist) -
    - @classmethod -
    [docs] def unrank_binary(self, rank, superset): - """ - Gets the binary ordered subset of the specified rank. - - Examples - ======== - - >>> from sympy.combinatorics.subsets import Subset - >>> Subset.unrank_binary(4, ['a','b','c','d']).subset - ['b'] - - See Also - ======== - iterate_binary, rank_binary - """ - bits = bin(rank)[2:].rjust(len(superset), '0') - return Subset.subset_from_bitlist(superset, bits) -
    - @classmethod -
    [docs] def unrank_gray(self, rank, superset): - """ - Gets the Gray code ordered subset of the specified rank. - - Examples - ======== - - >>> from sympy.combinatorics.subsets import Subset - >>> Subset.unrank_gray(4, ['a','b','c']).subset - ['a', 'b'] - >>> Subset.unrank_gray(0, ['a','b','c']).subset - [] - - See Also - ======== - iterate_graycode, rank_gray - """ - graycode_bitlist = GrayCode.unrank(len(superset), rank) - return Subset.subset_from_bitlist(superset, graycode_bitlist) -
    - @classmethod -
    [docs] def subset_indices(self, subset, superset): - """Return indices of subset in superset in a list; the list is empty - if all elements of subset are not in superset. - - Examples:: - - >>> from sympy.combinatorics import Subset - >>> superset = [1, 3, 2, 5, 4] - >>> Subset.subset_indices([3, 2, 1], superset) - [1, 2, 0] - >>> Subset.subset_indices([1, 6], superset) - [] - >>> Subset.subset_indices([], superset) - [] - - """ - a, b = superset, subset - sb = set(b) - d = {} - for i, ai in enumerate(a): - if ai in sb: - d[ai] = i - sb.remove(ai) - if not sb: - break - else: - return list() - return [d[bi] for bi in b] - -
    -def ksubsets(superset, k): - """ - Finds the subsets of size k in lexicographic order. - - This uses the itertools generator. - - Examples - ======== - - >>> from sympy.combinatorics.subsets import ksubsets - >>> list(ksubsets([1,2,3], 2)) - [(1, 2), (1, 3), (2, 3)] - >>> list(ksubsets([1,2,3,4,5], 2)) - [(1, 2), (1, 3), (1, 4), (1, 5), (2, 3), (2, 4), \ - (2, 5), (3, 4), (3, 5), (4, 5)] - - See Also - ======== - class:Subset - """ - return combinations(superset, k) -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/combinatorics/tensor_can.html b/dev-py3k/_modules/sympy/combinatorics/tensor_can.html deleted file mode 100644 index 441cd1c5157..00000000000 --- a/dev-py3k/_modules/sympy/combinatorics/tensor_can.html +++ /dev/null @@ -1,1258 +0,0 @@ - - - - - - - - - - sympy.combinatorics.tensor_can — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.combinatorics.tensor_can

    -from sympy.combinatorics.permutations import Permutation, _af_rmul, _af_rmuln,\
    -  _af_invert, _af_new
    -from sympy.combinatorics.perm_groups import PermutationGroup, _orbit, \
    -  _orbit_transversal
    -from sympy.combinatorics.util import _distribute_gens_by_base, \
    -  _orbits_transversals_from_bsgs
    -
    -"""
    -    References for tensor canonicalization:
    -
    -    [1] R. Portugal "Algorithmic simplification of tensor expressions",
    -        J. Phys. A 32 (1999) 7779-7789
    -
    -    [2] R. Portugal, B.F. Svaiter "Group-theoretic Approach for Symbolic
    -        Tensor Manipulation: I. Free Indices"
    -        arXiv:math-ph/0107031v1
    -
    -    [3] L.R.U. Manssur, R. Portugal "Group-theoretic Approach for Symbolic
    -        Tensor Manipulation: II. Dummy Indices"
    -        arXiv:math-ph/0107032v1
    -
    -    [4] xperm.c part of XPerm written by J. M. Martin-Garcia
    -        http://www.xact.es/index.html
    -"""
    -
    -
    -def dummy_sgs(dummies, sym, n):
    -    """
    -    Return the strong generators for dummy indices
    -
    -    dummies   list of dummy indices,
    -              `dummies[2k], dummies[2k+1]` are paired indices
    -    sym       symmetry under interchange of contracted dummies
    -    sym =     None  no symmetry
    -              0 symmetric
    -              1 antisymmetric
    -    n         number of indices
    -
    -    in base form the dummy indices are always in consecutive positions
    -
    -    Examples
    -    ========
    -    >>> from sympy.combinatorics.tensor_can import dummy_sgs
    -    >>> dummy_sgs([2,3,4,5,6,7], 0, 8)
    -    [[0, 1, 3, 2, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 5, 4, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 7, 6, 8, 9], [0, 1, 4, 5, 2, 3, 6, 7, 8, 9], [0, 1, 2, 3, 6, 7, 4, 5, 8, 9]]
    -    """
    -    assert len(dummies) <= n
    -    res = []
    -    # exchange of contravariant and covariant indices
    -    if sym != None:
    -        for j in dummies[::2]:
    -            a = list(range(n+2))
    -            if sym == 1:
    -                a[n] = n+1
    -                a[n+1] = n
    -            a[j], a[j+1] = a[j+1], a[j]
    -            res.append(a)
    -    # rename dummy indices
    -    for j in dummies[:-3:2]:
    -        a = list(range(n+2))
    -        a[j:j+4] = a[j+2],a[j+3],a[j],a[j+1]
    -        res.append(a)
    -    return res
    -
    -def _min_dummies(dummies, sym, indices):
    -    """
    -    Return list of minima of the orbits of indices in group of dummies
    -    see `double_coset_can_rep` for the description of `dummies` and `sym`
    -    indices is the initial list of dummy indices
    -
    -    Examples
    -    ========
    -    >>> from sympy.combinatorics.tensor_can import _min_dummies
    -    >>> _min_dummies([[2,3,4,5,6,7]], [0], list(range(10)))
    -    [0, 1, 2, 2, 2, 2, 2, 2, 8, 9]
    -    """
    -    num_types = len(sym)
    -    m = []
    -    for dx in dummies:
    -        if dx:
    -            m.append(min(dx))
    -        else:
    -            m.append(None)
    -    res = indices[:]
    -    for i in range(num_types):
    -        for c, i in enumerate(indices):
    -            for j in range(num_types):
    -                if i in dummies[j]:
    -                    res[c] = m[j]
    -                    break
    -    return res
    -
    -
    -def _trace_S(s, j, b, S_cosets):
    -    """
    -    Return the representative h satisfying s[h[b]] == j
    -
    -    If there is not such a representative return None
    -    """
    -    for h in S_cosets[b]:
    -        if s[h[b]] == j:
    -            return h
    -    return None
    -
    -def _trace_D(gj, p_i, Dxtrav):
    -    """
    -    Return the representative h satisfying h[gj] == p_i
    -
    -    If there is not such a representative return None
    -    """
    -    for h in Dxtrav:
    -        if h[gj] == p_i:
    -            return h
    -    return None
    -
    -def _dumx_remove(dumx, dumx_flat, p0):
    -    """
    -    remove p0 from dumx
    -    """
    -    res = []
    -    for dx in dumx:
    -        if p0 not in dx:
    -            res.append(dx)
    -            continue
    -        k = dx.index(p0)
    -        if k % 2 == 0:
    -            p0_paired = dx[k+1]
    -        else:
    -            p0_paired = dx[k-1]
    -        dx.remove(p0)
    -        dx.remove(p0_paired)
    -        dumx_flat.remove(p0)
    -        dumx_flat.remove(p0_paired)
    -        res.append(dx)
    -
    -def transversal2coset(size, base, transversal):
    -    a = []
    -    j = 0
    -    for i in range(size):
    -        if i in base:
    -            a.append(sorted(transversal[j].values()))
    -            j += 1
    -        else:
    -            a.append([list(range(size))])
    -    j = len(a)-1
    -    while a[j] == [list(range(size))]:
    -        j -= 1
    -    return a[:j+1]
    -
    -
    -
    [docs]def double_coset_can_rep(dummies, sym, b_S, sgens, S_transversals, g): - """ - Butler-Portugal algorithm for tensor canonicalization with dummy indices - - dummies - list of lists of dummy indices, - one list for each type of index; - the dummy indices are put in order contravariant, covariant - [d0, -d0, d1,-d1,...]. - - sym - list of the symmetries of the index metric for each type. - - possible symmetries of the metrics - * 0 symmetric - * 1 antisymmetric - * None no symmetry - - b_S - base of a minimal slot symmetry BSGS. - - sgens - generators of the slot symmetry BSGS. - - S_transversals - transversals for the slot BSGS. - - g - permutation representing the tensor. - - Return 0 if the tensor is zero, else return the array form of - the permutation representing the canonical form of the tensor. - - - A tensor with dummy indices can be represented in a number - of equivalent ways which typically grows exponentially with - the number of indices. To be able to establish if two tensors - with many indices are equal becomes computationally very slow - in absence of an efficient algorithm. - - The Butler-Portugal algorithm [3] is an efficient algorithm to - put tensors in canonical form, solving the above problem. - - Portugal observed that a tensor can be represented by a permutation, - and that the class of tensors equivalent to it under slot and dummy - symmetries is equivalent to the double coset `D*g*S` - (Note: in this documentation we use the conventions for multiplication - of permutations p, q with (p*q)(i) = p[q[i]] which is opposite - to the one used in the Permutation class) - - Using the algorithm by Butler to find a representative of the - double coset one can find a canonical form for the tensor. - - To see this correspondence, - let `g` be a permutation in array form; a tensor with indices `ind` - (the indices including both the contravariant and the covariant ones) - can be written as - - `t = T(ind[g[0],..., ind[g[n-1]])`, - - where `n= len(ind)`; - `g` has size `n + 2`, the last two indices for the sign of the tensor - (trick introduced in [4]). - - A slot symmetry transformation `s` is a permutation acting on the slots - `t -> T(ind[(g*s)[0]],..., ind[(g*s)[n-1]])` - - A dummy symmetry transformation acts on `ind` - `t -> T(ind[(d*g)[0]],..., ind[(d*g)[n-1]])` - - Being interested only in the transformations of the tensor under - these symmetries, one can represent the tensor by `g`, which transforms - as - - `g -> d*g*s`, so it belongs to the coset `D*g*S`. - - Let us explain the conventions by an example. - - Given a tensor `T^{d3 d2 d1}{}_{d1 d2 d3}` with the slot symmetries - `T^{a0 a1 a2 a3 a4 a5} = -T^{a2 a1 a0 a3 a4 a5}` - - `T^{a0 a1 a2 a3 a4 a5} = -T^{a4 a1 a2 a3 a0 a5}` - - and symmetric metric, find the tensor equivalent to it which - is the lowest under the ordering of indices: - lexicographic ordering `d1, d2, d3` then and contravariant index - before covariant index; that is the canonical form of the tensor. - - The canonical form is `-T^{d1 d2 d3}{}_{d1 d2 d3}` - obtained using `T^{a0 a1 a2 a3 a4 a5} = -T^{a2 a1 a0 a3 a4 a5}`. - - To convert this problem in the input for this function, - use the following labelling of the index names - (- for covariant for short) `d1, -d1, d2, -d2, d3, -d3` - - `T^{d3 d2 d1}{}_{d1 d2 d3}` corresponds to `g = [4,2,0,1,3,5,6,7]` - where the last two indices are for the sign - - `sgens = [Permutation(0,2)(6,7), Permutation(0,4)(6,7)]` - - sgens[0] is the slot symmetry `-(0,2)` - `T^{a0 a1 a2 a3 a4 a5} = -T^{a2 a1 a0 a3 a4 a5}` - - sgens[1] is the slot symmetry `-(0,4)` - `T^{a0 a1 a2 a3 a4 a5} = -T^{a4 a1 a2 a3 a0 a5}` - - The dummy symmetry group D is generated by the strong base generators - `[(0,1),(2,3),(4,5),(0,1)(2,3),(2,3)(4,5)]` - - The dummy symmetry acts from the left - `d = [1,0,2,3,4,5,6,7]` exchange `d1 -> -d1` - `T^{d3 d2 d1}{}_{d1 d2 d3} == T^{d3 d2}{}_{d1}{}^{d1}{}_{d2 d3}` - - `g=[4,2,0,1,3,5,6,7] -> [4,2,1,0,3,5,6,7] = _af_rmul(d, g)` - which differs from `_af_rmul(g, d)`. - - The slot symmetry acts from the right - `s = [2,1,0,3,4,5,7,6]` exchanges slots 0 and 2 and changes sign - `T^{d3 d2 d1}{}_{d1 d2 d3} == -T^{d1 d2 d3}{}_{d1 d2 d3}` - - `g=[4,2,0,1,3,5,6,7] -> [0,2,4,1,3,5,7,6] = _af_rmul(g, s)` - - Example in which the tensor is zero, same slot symmetries as above: - `T^{d3}{}_{d1,d2}{}^{d1}{}_{d3}{}^{d2}` - - `= -T^{d3}{}_{d1,d3}{}^{d1}{}_{d2}{}^{d2}` under slot symmetry `-(2,4)`; - - `= T_{d3 d1}{}^{d3}{}^{d1}{}_{d2}{}^{d2}` under slot symmetry `-(0,2)`; - - `= T^{d3}{}_{d1 d3}{}^{d1}{}_{d2}{}^{d2}` symmetric metric; - - `= 0` since two of these lines have tensors differ only for the sign. - - The double coset D*g*S consists of permutations `h = d*g*s` corresponding - to equivalent tensors; if there are two `h` which are the same apart - from the sign, return zero; otherwise - choose as representative the tensor with indices - ordered lexicographically according to `[d1, -d1, d2, -d2, d3, -d3]` - that is `rep = min(D*g*S) = min([d*g*s for d in D for s in S])` - - The indices are fixed one by one; first choose the lowest index - for slot 0, then the lowest remaining index for slot 1, etc. - Doing this one obtains a chain of stabilizers - - `S -> S_{b0} -> S_{b0,b1} -> ...` and - `D -> D_{p0} -> D_{p0,p1} -> ...` - - where `[b0, b1, ...] = range(b)` is a base of the symmetric group; - the strong base `b_S` of S is an ordered sublist of it; - therefore it is sufficient to compute once the - strong base generators of S using the Schreier-Sims algorithm; - the stabilizers of the strong base generators are the - strong base generators of the stabilizer subgroup. - - `dbase = [p0,p1,...]` is not in general in lexicographic order, - so that one must recompute the strong base generators each time; - however this is trivial, there is no need to use the Schreier-Sims - algorithm for D. - - The algorithm keeps a TAB of elements `(s_i, d_i, h_i)` - where `h_i = d_i*g*s_i` satisfying `h_i[j] = p_j` for `0 <= j < i` - starting from `s_0 = id, d_0 = id, h_0 = g`. - - The equations `h_0[0] = p_0, h_1[1] = p_1,...` are solved in this order, - choosing each time the lowest possible value of p_i - - For `j < i` - `d_i*g*s_i*S_{b_0,...,b_{i-1}}*b_j = D_{p_0,...,p_{i-1}}*p_j` - so that for dx in `D_{p_0,...,p_{i-1}}` and sx in - `S_{base[0],...,base[i-1]}` one has `dx*d_i*g*s_i*sx*b_j = p_j` - - Search for dx, sx such that this equation holds for `j = i`; - it can be written as `s_i*sx*b_j = J, dx*d_i*g*J = p_j` - `sx*b_j = s_i**-1*J; sx = trace(s_i**-1, S_{b_0,...,b_{i-1}})` - `dx**-1*p_j = d_i*g*J; dx = trace(d_i*g*J, D_{p_0,...,p_{i-1}})` - - `s_{i+1} = s_i*trace(s_i**-1*J, S_{b_0,...,b_{i-1}})` - `d_{i+1} = trace(d_i*g*J, D_{p_0,...,p_{i-1}})**-1*d_i` - `h_{i+1}*b_i = d_{i+1}*g*s_{i+1}*b_i = p_i` - - `h_n*b_j = p_j` for all j, so that `h_n` is the solution. - - Add the found `(s, d, h)` to TAB1. - - At the end of the iteration sort TAB1 with respect to the `h`; - if there are two consecutive `h` in TAB1 which differ only for the - sign, the tensor is zero, so return 0; - if there are two consecutive `h` which are equal, keep only one. - - Then stabilize the slot generators under `i` and the dummy generators - under `p_i`. - - Assign `TAB = TAB1` at the end of the iteration step. - - At the end `TAB` contains a unique `(s, d, h)`, since all the slots - of the tensor `h` have been fixed to have the minimum value according - to the symmetries. The algorithm returns `h`. - - It is important that the slot BSGS has lexicographic minimal base, - otherwise there is an `i` which does not belong to the slot base - for which `p_i` is fixed by the dummy symmetry only, while `i` - is not invariant from the slot stabilizer, so `p_i` is not in - general the minimal value. - - This algorithm differs slightly from the original algorithm [3]: - the canonical form is minimal lexicographically, and - the BSGS has minimal base under lexicographic order. - Equal tensors `h` are eliminated from TAB. - - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> from sympy.combinatorics.tensor_can import double_coset_can_rep, get_transversals - >>> gens = [Permutation(x) for x in [[2,1,0,3,4,5,7,6], [4,1,2,3,0,5,7,6]]] - >>> base = [0, 2] - >>> g = Permutation([4,2,0,1,3,5,6,7]) - >>> transversals = get_transversals(base, gens) - >>> double_coset_can_rep([list(range(6))], [0], base, gens, transversals, g) - [0, 1, 2, 3, 4, 5, 7, 6] - - >>> g = Permutation([4,1,3,0,5,2,6,7]) - >>> double_coset_can_rep([list(range(6))], [0], base, gens, transversals, g) - 0 - """ - size = g.size - g = g.array_form - num_dummies = size - 2 - indices = list(range(num_dummies)) - all_metrics_with_sym = all([_ != None for _ in sym]) - num_types = len(sym) - dumx = dummies[:] - dumx_flat = [] - for dx in dumx: - dumx_flat.extend(dx) - b_S = b_S[:] - sgensx = [h._array_form for h in sgens] - if b_S: - S_transversals = transversal2coset(size, b_S, S_transversals) - # strong generating set for D - dsgsx = [] - for i in range(num_types): - dsgsx.extend(dummy_sgs(dumx[i], sym[i], num_dummies)) - ginv = _af_invert(g) - idn = list(range(size)) - # TAB = list of entries (s, d, h) where h = _af_rmuln(d,g,s) - # for short, in the following d*g*s means _af_rmuln(d,g,s) - TAB = [(idn, idn, g)] - for i in range(size - 2): - b = i - testb = b in b_S and sgensx - if testb: - sgensx1 = [_af_new(_) for _ in sgensx] - deltab = _orbit(size, sgensx1, b) - else: - deltab = set([b]) - # p1 = min(IMAGES) = min(Union D_p*h*deltab for h in TAB) - if all_metrics_with_sym: - md = _min_dummies(dumx, sym, indices) - else: - md = [min(_orbit(size, [_af_new(ddx) for ddx in dsgsx], ii)) for ii in range(size-2)] - - p_i = min([min([md[h[x]] for x in deltab]) for s,d,h in TAB]) - dsgsx1 = [_af_new(_) for _ in dsgsx] - Dxtrav = _orbit_transversal(size, dsgsx1, p_i, False, af=True) \ - if dsgsx else None - if Dxtrav: - Dxtrav = [_af_invert(x) for x in Dxtrav] - # compute the orbit of p_i - for ii in range(num_types): - if p_i in dumx[ii]: - # the orbit is made by all the indices in dum[ii] - if sym[ii] != None: - deltap = dumx[ii] - else: - # the orbit is made by all the even indices if p_i - # is even, by all the odd indices if p_i is odd - p_i_index = dumx[ii].index(p_i)%2 - deltap = dumx[ii][p_i_index::2] - break - else: - deltap = [p_i] - TAB1 = [] - nTAB = len(TAB) - while TAB: - s, d, h = TAB.pop() - if min([md[h[x]] for x in deltab]) != p_i: - continue - deltab1 = [x for x in deltab if md[h[x]] == p_i] - # NEXT = s*deltab1 intersection (d*g)**-1*deltap - dg = _af_rmul(d, g) - dginv = _af_invert(dg) - sdeltab = [s[x] for x in deltab1] - gdeltap = [dginv[x] for x in deltap] - NEXT = [x for x in sdeltab if x in gdeltap] - # d, s satisfy - # d*g*s*base[i-1] = p_{i-1}; using the stabilizers - # d*g*s*S_{base[0],...,base[i-1]}*base[i-1] = - # D_{p_0,...,p_{i-1}}*p_{i-1} - # so that to find d1, s1 satisfying d1*g*s1*b = p_i - # one can look for dx in D_{p_0,...,p_{i-1}} and - # sx in S_{base[0],...,base[i-1]} - # d1 = dx*d; s1 = s*sx - # d1*g*s1*b = dx*d*g*s*sx*b = p_i - for j in NEXT: - if testb: - # solve s1*b = j with s1 = s*sx for some element sx - # of the stabilizer of ..., base[i-1] - # sx*b = s**-1*j; sx = _trace_S(s, j,...) - # s1 = s*trace_S(s**-1*j,...) - s1 = _trace_S(s, j, b, S_transversals) - if not s1: - continue - else: - s1 = [s[ix] for ix in s1] - else: - s1 = s - #assert s1[b] == j # invariant - # solve d1*g*j = p_i with d1 = dx*d for some element dg - # of the stabilizer of ..., p_{i-1} - # dx**-1*p_i = d*g*j; dx**-1 = trace_D(d*g*j,...) - # d1 = trace_D(d*g*j,...)**-1*d - # to save an inversion in the inner loop; notice we did - # Dxtrav = [perm_af_invert(x) for x in Dxtrav] out of the loop - if Dxtrav: - d1 = _trace_D(dg[j], p_i, Dxtrav) - if not d1: - continue - else: - if p_i != dg[j]: - continue - d1 = idn - assert d1[dg[j]] == p_i # invariant - d1 = [d1[ix] for ix in d] - h1 = [d1[g[ix]] for ix in s1] - #assert h1[b] == p_i # invariant - TAB1.append((s1, d1, h1)) - - # if TAB contains equal permutations, keep only one of them; - # if TAB contains equal permutations up to the sign, return 0 - TAB1.sort(key=lambda x: x[-1]) - nTAB1 = len(TAB1) - prev = [0]*size - while TAB1: - s, d, h = TAB1.pop() - if h[:-2] == prev[:-2]: - if h[-1] != prev[-1]: - return 0 - else: - TAB.append((s, d, h)) - prev = h - - # stabilize the SGS - sgensx = [h for h in sgensx if h[b] == b] - if b in b_S: - b_S.remove(b) - _dumx_remove(dumx, dumx_flat, p_i) - dsgsx = [] - for i in range(num_types): - dsgsx.extend(dummy_sgs(dumx[i], sym[i], num_dummies)) - return TAB[0][-1] - -
    -def canonical_free(base, gens, g, num_free): - """ - canonicalization of a tensor with respect to free indices - choosing the minimum with respect to lexicographical ordering - - base, gens BSGS for slot permutation group - g permutation representing the tensor - num_free number of free indices - The indices must be ordered with first the free indices - - see explanation in double_coset_can_rep - The algorithm is given in [2] - - Examples - ======== - >>> from sympy.combinatorics import Permutation - >>> from sympy.combinatorics.tensor_can import canonical_free - >>> gens = [[1,0,2,3,5,4], [2,3,0,1,4,5],[0,1,3,2,5,4]] - >>> gens = [Permutation(h) for h in gens] - >>> base = [0, 2] - >>> g = Permutation([2, 1, 0, 3, 4, 5]) - >>> canonical_free(base, gens, g, 4) - [0, 3, 1, 2, 5, 4] - - Consider the product of Riemann tensors - `T = R^{a}_{d0}^{d1,d2}*R_{d2,d1}^{d0,b}` - The order of the indices is [a,b,d0,-d0,d1,-d1,d2,-d2] - The permutation corresponding to the tensor is - g = [0,3,4,6,7,5,2,1,8,9] - Use the slot symmetries to get `T` is the form which is the minimum - in lexicographic order - `R^{a}_{d0}^{d1,d2}*R^{b,d0}_{d1,d2}` corresponding to - `[0, 3, 4, 6, 1, 2, 5, 7, 8, 9]` - >>> from sympy.combinatorics.tensor_can import riemann_bsgs, tensor_gens - >>> base, gens = riemann_bsgs - >>> size, sbase, sgens = tensor_gens(base, gens, [[],[]], 0) - >>> g = Permutation([0,3,4,6,7,5,2,1,8,9]) - >>> canonical_free(sbase, [Permutation(h) for h in sgens], g, 2) - [0, 3, 4, 6, 1, 2, 5, 7, 8, 9] - """ - g = g.array_form - size = len(g) - if not base: - return g[:] - - transversals = get_transversals(base, gens) - cosets = transversal2coset(size, base, transversals) - K = [h._array_form for h in gens] - m = len(base) - for x in sorted(g[:-2]): - if x not in base: - base.append(x) - lambd = g - K1 = [] - for i in range(m): - b = base[i] - delta = [x[b] for x in cosets[b]] - delta1 = [lambd[x] for x in delta] - delta1min = min(delta1) - k = delta1.index(delta1min) - p = delta[k] - for omega in cosets[b]: - if omega[b] == p: - break - lambd = _af_rmul(lambd, omega) - K = [px for px in K if px[b] == b] - return lambd - - -def _get_map_slots(size, fixed_slots): - res = list(range(size)) - pos = 0 - for i in range(size): - if i in fixed_slots: - continue - res[i] = pos - pos += 1 - return res - -def _lift_sgens(size, fixed_slots, free, s): - a = [] - j = k = 0 - fd = list(zip(fixed_slots, free)) - fd = [y for x,y in sorted(fd)] - num_free = len(free) - for i in range(size): - if i in fixed_slots: - a.append(fd[k]) - k += 1 - else: - a.append(s[j] + num_free) - j += 1 - return a - -
    [docs]def canonicalize(g, dummies, msym, *v): - """ - canonicalize tensor formed by tensors - - g permutation representing the tensor - - dummies list representing the dummy indices - it can be a list of dummy indices of the same type - or a list of lists of dummy indices, one list for each - type of index; - the dummy indices must come after the free indices, - and put in order contravariant, covariant - [d0, -d0, d1,-d1,...] - - msym symmetry of the metric(s) - it can be an integer or a list; - in the first case it is the symmetry of the dummy index metric; - in the second case it is the list of the symmetries of the - index metric for each type - - v is a list of (base_i, gens_i, n_i, sym_i) for tensors of type `i` - base_i, gens_i BSGS for tensors of this type. - - The BSGS should have minimal base under lexicographic ordering; - if not, an attempt is made do get the minimal BSGS; - in case of failure, - canonicalize_naive is used, which is much slower. - - n_i number ot tensors of type `i`. - - sym_i symmetry under exchange of component tensors of type `i`. - - Both for msym and sym_i the cases are - * None no symmetry - * 0 commuting - * 1 anticommuting - - Return 0 if the tensor is zero, else return the array form of - the permutation representing the canonical form of the tensor. - - Algorithm - ========= - - First one uses canonical_free to get the minimum tensor under - lexicographic order, using only the slot symmetries. - If the component tensors have not minimal BSGS, it is attempted - to find it; if the attempt fails canonicalize_naive - is used instead. - - Compute the residual slot symmetry keeping fixed the free indices - using tensor_gens(base, gens, list_free_indices, sym). - - Reduce the problem eliminating the free indices. - - Then use double_coset_can_rep and lift back the result reintroducing - the free indices. - - Examples - ======== - - one type of index with commuting metric; - - `A_{a b}` and `B_{a b}` antisymmetric and commuting - - `T = A_{d0 d1} * B^{d0}{}_{d2} * B^{d2 d1}` - - `ord = [d0,-d0,d1,-d1,d2,-d2]` order of the indices - - g = [1,3,0,5,4,2,6,7] - - T_c = 0 - - >>> from sympy.combinatorics.tensor_can import get_symmetric_group_sgs, canonicalize, bsgs_direct_product - >>> from sympy.combinatorics import Permutation - >>> base2a, gens2a = get_symmetric_group_sgs(2, 1) - >>> t0 = (base2a, gens2a, 1, 0) - >>> t1 = (base2a, gens2a, 2, 0) - >>> g = Permutation([1,3,0,5,4,2,6,7]) - >>> canonicalize(g, list(range(6)), 0, t0, t1) - 0 - - same as above, but with `B_{a b}` anticommuting - - `T_c = -A^{d0 d1} * B_{d0}{}^{d2} * B_{d1 d2}` - - `can = [0,2,1,4,3,5,7,6]` - - >>> t1 = (base2a, gens2a, 2, 1) - >>> canonicalize(g, list(range(6)), 0, t0, t1) - [0, 2, 1, 4, 3, 5, 7, 6] - - two types of indices `[a,b,c,d,e,f]` and `[m,n]`, in this order, - both with commuting metric - - `f^{a b c}` antisymmetric, commuting - - `A_{m a}` no symmetry, commuting - - `T = f^c{}_{d a} * f^f{}_{e b} * A_m{}^d * A^{m b} * A_n{}^a * A^{n e}` - - ord = [c,f,a,-a,b,-b,d,-d,e,-e,m,-m,n,-n] - - g = [0,7,3, 1,9,5, 11,6, 10,4, 13,2, 12,8, 14,15] - - The canonical tensor is - `T_c = -f^{c a b} * f^{f d e} * A^m{}_a * A_{m d} * A^n{}_b * A_{n e}` - - can = [0,2,4, 1,6,8, 10,3, 11,7, 12,5, 13,9, 15,14] - - >>> base_f, gens_f = get_symmetric_group_sgs(3, 1) - >>> base1, gens1 = get_symmetric_group_sgs(1) - >>> base_A, gens_A = bsgs_direct_product(base1, gens1, base1, gens1) - >>> t0 = (base_f, gens_f, 2, 0) - >>> t1 = (base_A, gens_A, 4, 0) - >>> dummies = [list(range(2, 10)), list(range(10, 14))] - >>> g = Permutation([0,7,3,1,9,5,11,6,10,4,13,2,12,8,14,15]) - >>> canonicalize(g, dummies, [0, 0], t0, t1) - [0, 2, 4, 1, 6, 8, 10, 3, 11, 7, 12, 5, 13, 9, 15, 14] - """ - from sympy.combinatorics.testutil import canonicalize_naive - if not isinstance(msym, list): - if not msym in [0, 1, None]: - raise ValueError('msym must be 0, 1 or None') - num_types = 1 - else: - num_types = len(msym) - if not all(msymx in [0,1,None] for msymx in msym): - raise ValueError('msym entries must be 0, 1 or None') - if len(dummies) != num_types: - raise ValueError('dummies and msym must have the same number of elements') - size = g.size - num_tensors = 0 - v1 = [] - for i in range(len(v)): - base_i, gens_i, n_i, sym_i = v[i] - # check that the BSGS is minimal; - # this property is used in double_coset_can_rep; - # if it is not minimal use canonicalize_naive - if not _is_minimal_bsgs(base_i, gens_i): - mbsgs = get_minimal_bsgs(base_i, gens_i) - if not mbsgs: - can = canonicalize_naive(g, dummies, msym, *v) - return can - base_i, gens_i = mbsgs - v1.append((base_i, gens_i, [[]]*n_i, sym_i)) - num_tensors += n_i - - if num_types == 1: - dummies = [dummies] - msym = [msym] - flat_dummies = [] - for dumx in dummies: - flat_dummies.extend(dumx) - - if flat_dummies and flat_dummies != list(range(flat_dummies[0], flat_dummies[-1] + 1)): - raise ValueError('dummies is not valid') - - # slot symmetry of the tensor - size1, sbase, sgens = gens_products(*v1) - if size != size1: - raise ValueError('g has size %d, generators have size %d' %(size, size1)) - free = [i for i in range(size-2) if i not in flat_dummies] - num_free = len(free) - - # g1 minimal tensor under slot symmetry - g1 = canonical_free(sbase, sgens, g, num_free) - if not flat_dummies: - return g1 - # save the sign of g1 - sign = 0 if g1[-1] == size-1 else 1 - - # the free indices are kept fixed. - # Determine free_i, the list of slots of tensors which are fixed - # since they are occupied by free indices, which are fixed. - start = 0 - for i in range(len(v)): - free_i = [] - base_i, gens_i, n_i, sym_i = v[i] - len_tens = gens_i[0].size - 2 - # for each component tensor get a list od fixed islots - for j in range(n_i): - # get the elements corresponding to the component tensor - h = g1[start:(start + len_tens)] - fr = [] - # get the positions of the fixed elements in h - for k in free: - if k in h: - fr.append(h.index(k)) - free_i.append(fr) - start += len_tens - v1[i] = (base_i, gens_i, free_i, sym_i) - # BSGS of the tensor with fixed free indices - # if tensor_gens fails in gens_product, use canonicalize_naive - size, sbase, sgens = gens_products(*v1) - - # reduce the permutations getting rid of the free indices - pos_dummies = [g1.index(x) for x in flat_dummies] - pos_free = [g1.index(x) for x in range(num_free)] - size_red = size - num_free - g1_red = [x - num_free for x in g1 if x in flat_dummies] - if sign: - g1_red.extend([size_red - 1, size_red - 2]) - else: - g1_red.extend([size_red - 2, size_red - 1]) - map_slots = _get_map_slots(size, pos_free) - sbase_red = [map_slots[i] for i in sbase if i not in pos_free] - sgens_red = [_af_new([map_slots[i] for i in y._array_form if i not in pos_free]) for y in sgens] - dummies_red = [[x - num_free for x in y] for y in dummies] - transv_red = get_transversals(sbase_red, sgens_red) - g1_red = _af_new(g1_red) - g2 = double_coset_can_rep(dummies_red, msym, sbase_red, sgens_red, transv_red, g1_red) - if g2 == 0: - return 0 - # lift to the case with the free indices - g3 = _lift_sgens(size, pos_free, free, g2) - return g3 - -
    -def perm_af_direct_product(gens1, gens2, signed=True): - """ - direct products of the generators gens1 and gens2 - - Examples - ======== - >>> from sympy.combinatorics.tensor_can import perm_af_direct_product - >>> gens1 = [[1,0,2,3], [0,1,3,2]] - >>> gens2 = [[1,0]] - >>> perm_af_direct_product(gens1, gens2, False) - [[1, 0, 2, 3, 4, 5], [0, 1, 3, 2, 4, 5], [0, 1, 2, 3, 5, 4]] - >>> gens1 = [[1,0,2,3,5,4], [0,1,3,2,4,5]] - >>> gens2 = [[1,0,2,3]] - >>> perm_af_direct_product(gens1, gens2, True) - [[1, 0, 2, 3, 4, 5, 7, 6], [0, 1, 3, 2, 4, 5, 6, 7], [0, 1, 2, 3, 5, 4, 6, 7]] - """ - gens1 = [list(x) for x in gens1] - gens2 = [list(x) for x in gens2] - s = 2 if signed else 0 - n1 = len(gens1[0]) - s - n2 = len(gens2[0]) - s - start = list(range(n1)) - end = list(range(n1, n1 + n2)) - if signed: - gens1 = [gen[:-2] + end + [gen[-2]+n2, gen[-1]+n2] for gen in gens1] - gens2 = [start + [x + n1 for x in gen] for gen in gens2] - else: - gens1 = [gen + end for gen in gens1] - gens2 = [start + [x + n1 for x in gen] for gen in gens2] - - res = gens1 + gens2 - - return res - -
    [docs]def bsgs_direct_product(base1, gens1, base2, gens2, signed=True): - """ - direct product of two BSGS - - base1 base of the first BSGS. - - gens1 strong generating sequence of the first BSGS. - - base2, gens2 similarly for the second BSGS. - - signed flag for signed permutations. - - Examples - ======== - >>> from sympy.combinatorics import Permutation - >>> from sympy.combinatorics.tensor_can import (get_symmetric_group_sgs, bsgs_direct_product) - >>> Permutation.print_cyclic = True - >>> base1, gens1 = get_symmetric_group_sgs(1) - >>> base2, gens2 = get_symmetric_group_sgs(2) - >>> bsgs_direct_product(base1, gens1, base2, gens2) - ([1], [Permutation(4)(1, 2)]) - """ - s = 2 if signed else 0 - n1 = gens1[0].size - s - base = base1[:] - base += [x + n1 for x in base2] - gens1 = [h._array_form for h in gens1] - gens2 = [h._array_form for h in gens2] - gens = perm_af_direct_product(gens1, gens2, signed) - size = len(gens[0]) - id_af = list(range(size)) - gens = [h for h in gens if h != id_af] - if not gens: - gens = [id_af] - return base, [_af_new(h) for h in gens] -
    -
    [docs]def get_symmetric_group_sgs(n, sym=0): - """ - Return base, gens of the minimal BSGS for (anti)symmetric group - with n elements - - Examples - ======== - >>> from sympy.combinatorics import Permutation - >>> from sympy.combinatorics.tensor_can import get_symmetric_group_sgs - >>> Permutation.print_cyclic = True - >>> get_symmetric_group_sgs(3) - ([0, 1], [Permutation(4)(0, 1), Permutation(4)(1, 2)]) - """ - if n == 1: - return [], [_af_new(list(range(3)))] - gens = [Permutation(n-1)(i, i+1)._array_form for i in range(n-1)] - if sym == 0: - gens = [x + [n, n+1] for x in gens] - else: - gens = [x + [n+1, n] for x in gens] - base = list(range(n-1)) - return base, [_af_new(h) for h in gens] -
    -riemann_bsgs = [0, 2], [Permutation(0,1)(4,5), Permutation(2,3)(4,5), \ - Permutation(5)(0,2)(1,3)] - -def get_transversals(base, gens): - """ - Return transversals for the group with BSGS base, gens - """ - if not base: - return [] - stabs = _distribute_gens_by_base(base, gens) - orbits, transversals = _orbits_transversals_from_bsgs(base, stabs) - transversals = [dict((x, h._array_form) for x, h in list(y.items())) for y in \ - transversals] - return transversals - - -def _is_minimal_bsgs(base, gens): - """ - Check if the BSGS has minimal base under lexigographic order. - - base, gens BSGS - - Examples - ======== - >>> from sympy.combinatorics import Permutation - >>> from sympy.combinatorics.tensor_can import riemann_bsgs, _is_minimal_bsgs - >>> _is_minimal_bsgs(*riemann_bsgs) - True - >>> riemann_bsgs1 = ([2, 0], ([Permutation(5)(0,1)(4,5), Permutation(5)(0,2)(1,3)])) - >>> _is_minimal_bsgs(*riemann_bsgs1) - False - """ - base1 = [] - sgs1 = gens[:] - size = gens[0].size - for i in range(size): - if not all(h._array_form[i] == i for h in sgs1): - base1.append(i) - sgs1 = [h for h in sgs1 if h._array_form[i] == i] - return base1 == base - -def get_minimal_bsgs(base, gens): - """ - Compute a minimal GSGS - - base, gens BSGS - - If base, gens is a minimal BSGS return it; else return a minimal BSGS - if it fails in finding one, it returns None - - TODO: use baseswap in the case in which if it fails in finding a - minimal BSGS - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> from sympy.combinatorics.tensor_can import get_minimal_bsgs - >>> Permutation.print_cyclic = True - >>> riemann_bsgs1 = ([2, 0], ([Permutation(5)(0,1)(4,5), Permutation(5)(0,2)(1,3)])) - >>> get_minimal_bsgs(*riemann_bsgs1) - ([0, 2], [Permutation(0, 1)(4, 5), Permutation(5)(0, 2)(1, 3), Permutation(2, 3)(4, 5)]) - """ - G = PermutationGroup(gens) - base, gens = G.schreier_sims_incremental() - if not _is_minimal_bsgs(base, gens): - return None - return base, gens - -def tensor_gens(base, gens, list_free_indices, sym=0): - """ - Returns size, res_base, res_gens BSGS for n tensors of the same type - - base, gens BSGS for tensors of this type - list_free_indices list of the slots occupied by fixed indices - for each of the tensors - - sym symmetry under commutation of two tensors - sym None no symmetry - sym 0 commuting - sym 1 anticommuting - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> from sympy.combinatorics.tensor_can import tensor_gens, get_symmetric_group_sgs - >>> Permutation.print_cyclic = True - - two symmetric tensors with 3 indices without free indices - >>> base, gens = get_symmetric_group_sgs(3) - >>> tensor_gens(base, gens, [[], []]) - (8, [0, 1, 3, 4], [Permutation(7)(0, 1), Permutation(7)(1, 2), Permutation(7)(3, 4), Permutation(7)(4, 5), Permutation(7)(0, 3)(1, 4)(2, 5)]) - - two symmetric tensors with 3 indices with free indices in slot 1 and 0 - >>> tensor_gens(base, gens, [[1],[0]]) - (8, [0, 4], [Permutation(7)(0, 2), Permutation(7)(4, 5)]) - - four symmetric tensors with 3 indices, two of which with free indices - - """ - def _get_bsgs(G, base, gens, free_indices): - """ - return the BSGS for G.pointwise_stabilizer(free_indices) - """ - if not free_indices: - return base[:], gens[:] - else: - H = G.pointwise_stabilizer(free_indices) - base, sgs = H.schreier_sims_incremental() - return base, sgs - - # if not base there is no slot symmetry for the component tensors - # if list_free_indices.count([]) < 2 there is no commutation symmetry - # so there is no resulting slot symmetry - if not base and list_free_indices.count([]) < 2: - n = len(list_free_indices) - size = gens[0].size - size = n*(gens[0].size - 2) + 2 - return size, [], [_af_new(list(range(size)))] - - # if any(list_free_indices) one needs to compute the pointwise - # stabilizer, so G is needed - if any(list_free_indices): - G = PermutationGroup(gens) - else: - G = None - - # no_free list of lists of indices for component tensors without fixed - # indices - no_free = [] - size = gens[0].size - id_af = list(range(size)) - num_indices = size - 2 - if not list_free_indices[0]: - no_free.append(list(range(num_indices))) - res_base, res_gens = _get_bsgs(G, base, gens, list_free_indices[0]) - for i in range(1, len(list_free_indices)): - base1, gens1 = _get_bsgs(G, base, gens, list_free_indices[i]) - res_base, res_gens = bsgs_direct_product(res_base, res_gens, - base1, gens1, 1) - if not list_free_indices[i]: - no_free.append(list(range(size-2, size - 2 + num_indices))) - size += num_indices - nr = size - 2 - res_gens = [h for h in res_gens if h._array_form != id_af] - # if sym there are no commuting tensors stop here - if sym == None or not no_free: - if not res_gens: - res_gens = [_af_new(id_af)] - return size, res_base, res_gens - - # if the component tensors have moinimal BSGS, so is their direct - # product P; the slot symmetry group is S = P*C, where C is the group - # to (anti)commute the component tensors with no free indices - # a stabilizer has the property S_i = P_i*C_i; - # the BSGS of P*C has SGS_P + SGS_C and the base is - # the ordered union of the bases of P and C. - # If P has minimal BSGS, so has S with this base. - base_comm = [] - for i in range(len(no_free) - 1): - ind1 = no_free[i] - ind2 = no_free[i + 1] - a = list(range(ind1[0])) - a.extend(ind2) - a.extend(ind1) - base_comm.append(ind1[0]) - a.extend(list(range(ind2[-1]+1, nr))) - if sym == 0: - a.extend([nr, nr + 1]) - else: - a.extend([nr + 1, nr]) - res_gens.append(_af_new(a)) - # each base is ordered; order the union of the two bases - for i in base_comm: - if i not in res_base: - res_base.append(i) - res_base.sort() - if not res_gens: - res_gens = [_af_new(id_af)] - - return size, res_base, res_gens - - -def gens_products(*v): - """ - Returns size, res_base, res_gens BSGS for n tensors of different types - - v is a sequence of (base_i, gens_i, free_i, sym_i) - where - base_i, gens_i BSGS of tensor of type `i` - free_i list of the fixed slots for each of the tensors - of type `i`; if there are `n_i` tensors of type `i` - and none of them have fixed slots, `free = [[]]*n_i` - sym 0 (1) if the tensors of type `i` (anti)commute among themselves - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> from sympy.combinatorics.tensor_can import get_symmetric_group_sgs, gens_products - >>> Permutation.print_cyclic = True - >>> base, gens = get_symmetric_group_sgs(2) - >>> gens_products((base,gens,[[],[]],0)) - (6, [0, 2], [Permutation(5)(0, 1), Permutation(5)(2, 3), Permutation(5)(0, 2)(1, 3)]) - >>> gens_products((base,gens,[[1],[]],0)) - (6, [2], [Permutation(5)(2, 3)]) - """ - res_size, res_base, res_gens = tensor_gens(*v[0]) - for i in range(1, len(v)): - size, base, gens = tensor_gens(*v[i]) - res_base, res_gens = bsgs_direct_product(res_base, res_gens, base, - gens, 1) - res_size = res_gens[0].size - id_af = list(range(res_size)) - res_gens = [h for h in res_gens if h != id_af] - if not res_gens: - res_gens = [id_af] - return res_size, res_base, res_gens -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/combinatorics/testutil.html b/dev-py3k/_modules/sympy/combinatorics/testutil.html deleted file mode 100644 index a3c97ef990c..00000000000 --- a/dev-py3k/_modules/sympy/combinatorics/testutil.html +++ /dev/null @@ -1,449 +0,0 @@ - - - - - - - - - - sympy.combinatorics.testutil — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.combinatorics.testutil

    -from sympy.combinatorics.util import _distribute_gens_by_base
    -from sympy.combinatorics import Permutation
    -
    -rmul = Permutation.rmul
    -
    -
    -
    [docs]def _cmp_perm_lists(first, second): - """ - Compare two lists of permutations as sets. - - This is used for testing purposes. Since the array form of a - permutation is currently a list, Permutation is not hashable - and cannot be put into a set. - - Examples - ======== - - >>> from sympy.combinatorics.permutations import Permutation - >>> from sympy.combinatorics.testutil import _cmp_perm_lists - >>> a = Permutation([0, 2, 3, 4, 1]) - >>> b = Permutation([1, 2, 0, 4, 3]) - >>> c = Permutation([3, 4, 0, 1, 2]) - >>> ls1 = [a, b, c] - >>> ls2 = [b, c, a] - >>> _cmp_perm_lists(ls1, ls2) - True - - """ - return set([tuple(a) for a in first]) == \ - set([tuple(a) for a in second]) - -
    -
    [docs]def _naive_list_centralizer(self, other, af=False): - from sympy.combinatorics.perm_groups import PermutationGroup - """ - Return a list of elements for the centralizer of a subgroup/set/element. - - This is a brute-force implementation that goes over all elements of the - group and checks for membership in the centralizer. It is used to - test ``.centralizer()`` from ``sympy.combinatorics.perm_groups``. - - Examples - ======== - >>> from sympy.combinatorics.testutil import _naive_list_centralizer - >>> from sympy.combinatorics.named_groups import DihedralGroup - >>> D = DihedralGroup(4) - >>> _naive_list_centralizer(D, D) - [Permutation([0, 1, 2, 3]), Permutation([2, 3, 0, 1])] - - See Also - ======== - - sympy.combinatorics.perm_groups.centralizer - - """ - from sympy.combinatorics.permutations import _af_commutes_with - if hasattr(other, 'generators'): - elements = list(self.generate_dimino(af=True)) - gens = [x._array_form for x in other.generators] - commutes_with_gens = lambda x: all(_af_commutes_with(x, gen) for gen in gens) - centralizer_list = [] - if not af: - for element in elements: - if commutes_with_gens(element): - centralizer_list.append(Permutation._af_new(element)) - else: - for element in elements: - if commutes_with_gens(element): - centralizer_list.append(element) - return centralizer_list - elif hasattr(other, 'getitem'): - return _naive_list_centralizer(self, PermutationGroup(other), af) - elif hasattr(other, 'array_form'): - return _naive_list_centralizer(self, PermutationGroup([other]), af) -
    -
    [docs]def _verify_bsgs(group, base, gens): - """ - Verify the correctness of a base and strong generating set. - - This is a naive implementation using the definition of a base and a strong - generating set relative to it. There are other procedures for - verifying a base and strong generating set, but this one will - serve for more robust testing. - - Examples - ======== - - >>> from sympy.combinatorics.named_groups import AlternatingGroup - >>> from sympy.combinatorics.testutil import _verify_bsgs - >>> A = AlternatingGroup(4) - >>> A.schreier_sims() - >>> _verify_bsgs(A, A.base, A.strong_gens) - True - - See Also - ======== - - sympy.combinatorics.perm_groups.PermutationGroup.schreier_sims - - """ - from sympy.combinatorics.perm_groups import PermutationGroup - strong_gens_distr = _distribute_gens_by_base(base, gens) - current_stabilizer = group - for i in range(len(base)): - candidate = PermutationGroup(strong_gens_distr[i]) - if current_stabilizer.order() != candidate.order(): - return False - current_stabilizer = current_stabilizer.stabilizer(base[i]) - if current_stabilizer.order() != 1: - return False - return True - -
    -
    [docs]def _verify_centralizer(group, arg, centr=None): - """ - Verify the centralizer of a group/set/element inside another group. - - This is used for testing ``.centralizer()`` from - ``sympy.combinatorics.perm_groups`` - - Examples - ======== - - >>> from sympy.combinatorics.named_groups import (SymmetricGroup, - ... AlternatingGroup) - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> from sympy.combinatorics.permutations import Permutation - >>> from sympy.combinatorics.testutil import _verify_centralizer - >>> S = SymmetricGroup(5) - >>> A = AlternatingGroup(5) - >>> centr = PermutationGroup([Permutation([0, 1, 2, 3, 4])]) - >>> _verify_centralizer(S, A, centr) - True - - See Also - ======== - - _naive_list_centralizer, - sympy.combinatorics.perm_groups.PermutationGroup.centralizer, - _cmp_perm_lists - - """ - if centr is None: - centr = group.centralizer(arg) - centr_list = list(centr.generate_dimino(af=True)) - centr_list_naive = _naive_list_centralizer(group, arg, af=True) - return _cmp_perm_lists(centr_list, centr_list_naive) - -
    -
    [docs]def _verify_normal_closure(group, arg, closure=None): - from sympy.combinatorics.perm_groups import PermutationGroup - """ - Verify the normal closure of a subgroup/subset/element in a group. - - This is used to test - sympy.combinatorics.perm_groups.PermutationGroup.normal_closure - - Examples - ======== - - >>> from sympy.combinatorics.named_groups import (SymmetricGroup, - ... AlternatingGroup) - >>> from sympy.combinatorics.testutil import _verify_normal_closure - >>> S = SymmetricGroup(3) - >>> A = AlternatingGroup(3) - >>> _verify_normal_closure(S, A, closure=A) - True - - See Also - ======== - - sympy.combinatorics.perm_groups.PermutationGroup.normal_closure - - """ - if closure is None: - closure = group.normal_closure(arg) - conjugates = set() - if hasattr(arg, 'generators'): - subgr_gens = arg.generators - elif hasattr(arg, '__getitem__'): - subgr_gens = arg - elif hasattr(arg, 'array_form'): - subgr_gens = [arg] - for el in group.generate_dimino(): - for gen in subgr_gens: - conjugates.add(gen^el) - naive_closure = PermutationGroup(list(conjugates)) - return closure.is_subgroup(naive_closure) -
    -def canonicalize_naive(g, dummies, sym, *v): - """ - canonicalize tensor formed by tensors of the different types - - g permutation representing the tensor - dummies list of dummy indices - msym symmetry of the metric - - v is a list of (base_i, gens_i, n_i, sym_i) for tensors of type `i` - base_i, gens_i BSGS for tensors of this type - n_i number ot tensors of type `i` - - sym_i symmetry under exchange of two component tensors of type `i` - None no symmetry - 0 commuting - 1 anticommuting - - Return 0 if the tensor is zero, else return the array form of - the permutation representing the canonical form of the tensor. - - Examples - ======== - >>> from sympy.combinatorics.testutil import canonicalize_naive - >>> from sympy.combinatorics.tensor_can import get_symmetric_group_sgs - >>> from sympy.combinatorics import Permutation, PermutationGroup - >>> g = Permutation([1,3,2,0,4,5]) - >>> base2, gens2 = get_symmetric_group_sgs(2) - >>> canonicalize_naive(g, [2, 3], 0, (base2, gens2, 2, 0)) - [0, 2, 1, 3, 4, 5] - """ - from sympy.combinatorics.perm_groups import PermutationGroup - from sympy.combinatorics.tensor_can import gens_products, dummy_sgs - from sympy.combinatorics.permutations import Permutation, _af_rmul - v1 = [] - for i in range(len(v)): - base_i, gens_i, n_i, sym_i = v[i] - v1.append((base_i, gens_i, [[]]*n_i, sym_i)) - size, sbase, sgens = gens_products(*v1) - dgens = dummy_sgs(dummies, sym, size-2) - if isinstance(sym, int): - num_types = 1 - dummies = [dummies] - sym = [sym] - else: - num_types = len(sym) - dgens = [] - for i in range(num_types): - dgens.extend(dummy_sgs(dummies[i], sym[i], size - 2)) - S = PermutationGroup(sgens) - D = PermutationGroup([Permutation(x) for x in dgens]) - dlist = list(D.generate(af=True)) - g = g.array_form - st = set() - for s in S.generate(af=True): - h = _af_rmul(g, s) - for d in dlist: - q = tuple(_af_rmul(d, h)) - st.add(q) - a = list(st) - a.sort() - prev = (0,)*size - for h in a: - if h[:-2] == prev[:-2]: - if h[-1] != prev[-1]: - return 0 - prev = h - return list(a[0]) - -def graph_certificate(gr): - """ - Return a certificate for the graph - - gr adjacency list - - The graph is assumed to be unoriented and without - external lines. - - Associate to each vertex of the graph a symmetric tensor with - number of indices equal to the degree of the vertex; indices - are contracted when they correspond to the same line of the graph. - The canonical form of the tensor gives a certificate for the graph. - - This is not an efficient algorithm to get the certificate of a graph. - - Examples - ======== - - >>> from sympy.combinatorics.testutil import graph_certificate - >>> gr1 = {0:[1,2,3,5], 1:[0,2,4], 2:[0,1,3,4], 3:[0,2,4], 4:[1,2,3,5], 5:[0,4]} - >>> gr2 = {0:[1,5], 1:[0,2,3,4], 2:[1,3,5], 3:[1,2,4,5], 4:[1,3,5], 5:[0,2,3,4]} - >>> c1 = graph_certificate(gr1) - >>> c2 = graph_certificate(gr2) - >>> c1 - [0, 2, 4, 6, 1, 8, 10, 12, 3, 14, 16, 18, 5, 9, 15, 7, 11, 17, 13, 19, 20, 21] - >>> c1 == c2 - True - """ - from sympy.combinatorics.permutations import _af_invert - from sympy.combinatorics.tensor_can import get_symmetric_group_sgs, canonicalize - items = list(gr.items()) - items.sort(key=lambda x: len(x[1]), reverse=True) - pvert = [x[0] for x in items] - pvert = _af_invert(pvert) - - # the indices of the tensor are twice the number of lines of the graph - num_indices = 0 - for v, neigh in items: - num_indices += len(neigh) - # associate to each vertex its indices; for each line - # between two vertices assign the - # even index to the vertex which comes first in items, - # the odd index to the other vertex - vertices = [[] for i in items] - i = 0 - for v, neigh in items: - for v2 in neigh: - if pvert[v] < pvert[v2]: - vertices[pvert[v]].append(i) - vertices[pvert[v2]].append(i+1) - i += 2 - g = [] - for v in vertices: - g.extend(v) - assert len(g) == num_indices - g += [num_indices, num_indices + 1] - size = num_indices + 2 - assert sorted(g) == list(range(size)) - g = Permutation(g) - vlen = [0]*(len(vertices[0])+1) - for neigh in vertices: - vlen[len(neigh)] += 1 - v = [] - for i in range(len(vlen)): - n = vlen[i] - if n: - base, gens = get_symmetric_group_sgs(i) - v.append((base, gens, n, 0)) - v.reverse() - dummies = list(range(num_indices)) - can = canonicalize(g, dummies, 0, *v) - return can -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/combinatorics/util.html b/dev-py3k/_modules/sympy/combinatorics/util.html deleted file mode 100644 index 19ac1e22d53..00000000000 --- a/dev-py3k/_modules/sympy/combinatorics/util.html +++ /dev/null @@ -1,639 +0,0 @@ - - - - - - - - - - sympy.combinatorics.util — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.combinatorics.util

    -from sympy.ntheory import isprime
    -from sympy.combinatorics.permutations import Permutation, _af_invert, _af_rmul
    -
    -rmul = Permutation.rmul
    -_af_new = Permutation._af_new
    -
    -############################################
    -###
    -### Utilities for computational group theory
    -###
    -############################################
    -
    -
    -
    [docs]def _base_ordering(base, degree): - r""" - Order `\{0, 1, ..., n-1\}` so that base points come first and in order. - - Parameters - ========== - - ``base`` - the base - ``degree`` - the degree of the associated permutation group - - Returns - ======= - - A list ``base_ordering`` such that ``base_ordering[point]`` is the - number of ``point`` in the ordering. - Examples - ======== - - >>> from sympy.combinatorics.named_groups import SymmetricGroup - >>> from sympy.combinatorics.util import _base_ordering - >>> S = SymmetricGroup(4) - >>> S.schreier_sims() - >>> _base_ordering(S.base, S.degree) - [0, 1, 2, 3] - - Notes - ===== - - This is used in backtrack searches, when we define a relation `<<` on - the underlying set for a permutation group of degree `n`, - `\{0, 1, ..., n-1\}`, so that if `(b_1, b_2, ..., b_k)` is a base we - have `b_i << b_j` whenever `i<j` and `b_i << a` for all - `i\in\{1,2, ..., k\}` and `a` is not in the base. The idea is developed - and applied to backtracking algorithms in [1], pp.108-132. The points - that are not in the base are taken in increasing order. - - References - ========== - - [1] Holt, D., Eick, B., O'Brien, E. - "Handbook of computational group theory" - - """ - base_len = len(base) - ordering = [0]*degree - for i in range(base_len): - ordering[base[i]] = i - current = base_len - for i in range(degree): - if i not in base: - ordering[i] = current - current += 1 - return ordering - -
    -
    [docs]def _check_cycles_alt_sym(perm): - """ - Checks for cycles of prime length p with n/2 < p < n-2. - - Here `n` is the degree of the permutation. This is a helper function for - the function is_alt_sym from sympy.combinatorics.perm_groups. - - Examples - ======== - - >>> from sympy.combinatorics.util import _check_cycles_alt_sym - >>> from sympy.combinatorics.permutations import Permutation - >>> a = Permutation([[0,1,2,3,4,5,6,7,8,9,10], [11, 12]]) - >>> _check_cycles_alt_sym(a) - False - >>> b = Permutation([[0,1,2,3,4,5,6], [7,8,9,10]]) - >>> _check_cycles_alt_sym(b) - True - - See Also - ======== - - sympy.combinatorics.perm_groups.PermutationGroup.is_alt_sym - - """ - n = perm.size - af = perm.array_form - current_len = 0 - total_len = 0 - used = set() - for i in range(n//2): - if not i in used and i < n//2 - total_len: - current_len = 1 - used.add(i) - j = i - while(af[j] != i): - current_len += 1 - j = af[j] - used.add(j) - total_len += current_len - if current_len > n//2 and current_len < n - 2 and isprime(current_len): - return True - return False - -
    -
    [docs]def _distribute_gens_by_base(base, gens): - """ - Distribute the group elements ``gens`` by membership in basic stabilizers. - - Notice that for a base `(b_1, b_2, ..., b_k)`, the basic stabilizers - are defined as `G^{(i)} = G_{b_1, ..., b_{i-1}}` for - `i \in\{1, 2, ..., k\}`. - - Parameters - ========== - - ``base`` - a sequence of points in `\{0, 1, ..., n-1\}` - ``gens`` - a list of elements of a permutation group of degree `n`. - - Returns - ======= - - List of length `k`, where `k` is - the length of ``base``. The `i`-th entry contains those elements in - ``gens`` which fix the first `i` elements of ``base`` (so that the - `0`-th entry is equal to ``gens`` itself). If no element fixes the first - `i` elements of ``base``, the `i`-th element is set to a list containing - the identity element. - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation.print_cyclic = True - >>> from sympy.combinatorics.named_groups import DihedralGroup - >>> from sympy.combinatorics.util import _distribute_gens_by_base - >>> D = DihedralGroup(3) - >>> D.schreier_sims() - >>> D.strong_gens - [Permutation(0, 1, 2), Permutation(0, 2), Permutation(1, 2)] - >>> D.base - [0, 1] - >>> _distribute_gens_by_base(D.base, D.strong_gens) - [[Permutation(0, 1, 2), Permutation(0, 2), Permutation(1, 2)], - [Permutation(1, 2)]] - - See Also - ======== - - _strong_gens_from_distr, _orbits_transversals_from_bsgs, - _handle_precomputed_bsgs - - """ - base_len = len(base) - degree = gens[0].size - stabs = [[] for _ in range(base_len)] - max_stab_index = 0 - for gen in gens: - j = 0 - while j < base_len - 1 and gen._array_form[base[j]] == base[j]: - j += 1 - if j > max_stab_index: - max_stab_index = j - for k in range(j + 1): - stabs[k].append(gen) - for i in range(max_stab_index + 1, base_len): - stabs[i].append(_af_new(list(range(degree)))) - return stabs -
    -
    [docs]def _handle_precomputed_bsgs(base, strong_gens, transversals=None, - basic_orbits=None, strong_gens_distr=None): - """ - Calculate BSGS-related structures from those present. - - The base and strong generating set must be provided; if any of the - transversals, basic orbits or distributed strong generators are not - provided, they will be calculated from the base and strong generating set. - - Parameters - ========== - - ``base`` - the base - ``strong_gens`` - the strong generators - ``transversals`` - basic transversals - ``basic_orbits`` - basic orbits - ``strong_gens_distr`` - strong generators distributed by membership in basic - stabilizers - - Returns - ======= - - ``(transversals, basic_orbits, strong_gens_distr)`` where ``transversals`` - are the basic transversals, ``basic_orbits`` are the basic orbits, and - ``strong_gens_distr`` are the strong generators distributed by membership - in basic stabilizers. - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation.print_cyclic = True - >>> from sympy.combinatorics.named_groups import DihedralGroup - >>> from sympy.combinatorics.util import _handle_precomputed_bsgs - >>> D = DihedralGroup(3) - >>> D.schreier_sims() - >>> _handle_precomputed_bsgs(D.base, D.strong_gens, - ... basic_orbits=D.basic_orbits) - ([{0: Permutation(2), 1: Permutation(0, 1, 2), 2: Permutation(0, 2)}, - {1: Permutation(2), 2: Permutation(1, 2)}], - [[0, 1, 2], [1, 2]], [[Permutation(0, 1, 2), - Permutation(0, 2), - Permutation(1, 2)], - [Permutation(1, 2)]]) - - See Also - ======== - - _orbits_transversals_from_bsgs, distribute_gens_by_base - - """ - if strong_gens_distr is None: - strong_gens_distr = _distribute_gens_by_base(base, strong_gens) - if transversals is None: - if basic_orbits is None: - basic_orbits, transversals = \ - _orbits_transversals_from_bsgs(base, strong_gens_distr) - else: - transversals = \ - _orbits_transversals_from_bsgs(base, strong_gens_distr, - transversals_only=True) - else: - if basic_orbits is None: - base_len = len(base) - basic_orbits = [None]*base_len - for i in range(base_len): - basic_orbits[i] = list(transversals[i].keys()) - return transversals, basic_orbits, strong_gens_distr - -
    -
    [docs]def _orbits_transversals_from_bsgs(base, strong_gens_distr, - transversals_only=False): - """ - Compute basic orbits and transversals from a base and strong generating set. - - The generators are provided as distributed across the basic stabilizers. - If the optional argument ``transversals_only`` is set to True, only the - transversals are returned. - - Parameters - ========== - - ``base`` - the base - ``strong_gens_distr`` - strong generators distributed by membership in basic - stabilizers - ``transversals_only`` - a flag swithing between returning only the - transversals/ both orbits and transversals - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation.print_cyclic = True - >>> from sympy.combinatorics.named_groups import SymmetricGroup - >>> from sympy.combinatorics.util import _orbits_transversals_from_bsgs - >>> from sympy.combinatorics.util import (_orbits_transversals_from_bsgs, - ... _distribute_gens_by_base) - >>> S = SymmetricGroup(3) - >>> S.schreier_sims() - >>> strong_gens_distr = _distribute_gens_by_base(S.base, S.strong_gens) - >>> _orbits_transversals_from_bsgs(S.base, strong_gens_distr) - ([[0, 1, 2], [1, 2]], - [{0: Permutation(2), 1: Permutation(0, 1, 2), 2: Permutation(0, 2, 1)}, - {1: Permutation(2), 2: Permutation(1, 2)}]) - - See Also - ======== - - _distribute_gens_by_base, _handle_precomputed_bsgs - - """ - from sympy.combinatorics.perm_groups import _orbit_transversal - base_len = len(base) - degree = strong_gens_distr[0][0].size - transversals = [None]*base_len - if transversals_only is False: - basic_orbits = [None]*base_len - for i in range(base_len): - transversals[i] = dict(_orbit_transversal(degree, strong_gens_distr[i], - base[i], pairs=True)) - if transversals_only is False: - basic_orbits[i] = list(transversals[i].keys()) - if transversals_only: - return transversals - else: - return basic_orbits, transversals - -
    -
    [docs]def _remove_gens(base, strong_gens, basic_orbits=None, strong_gens_distr=None): - """ - Remove redundant generators from a strong generating set. - - Parameters - ========== - - ``base`` - a base - ``strong_gens`` - a strong generating set relative to ``base`` - ``basic_orbits`` - basic orbits - ``strong_gens_distr`` - strong generators distributed by membership in basic - stabilizers - - Returns - ======= - - A strong generating set with respect to ``base`` which is a subset of - ``strong_gens``. - - Examples - ======== - - >>> from sympy.combinatorics.named_groups import SymmetricGroup - >>> from sympy.combinatorics.perm_groups import PermutationGroup - >>> from sympy.combinatorics.util import _remove_gens - >>> from sympy.combinatorics.testutil import _verify_bsgs - >>> S = SymmetricGroup(15) - >>> base, strong_gens = S.schreier_sims_incremental() - >>> len(strong_gens) - 26 - >>> new_gens = _remove_gens(base, strong_gens) - >>> len(new_gens) - 14 - >>> _verify_bsgs(S, base, new_gens) - True - - Notes - ===== - - This procedure is outlined in [1],p.95. - - References - ========== - - [1] Holt, D., Eick, B., O'Brien, E. - "Handbook of computational group theory" - - """ - from sympy.combinatorics.perm_groups import PermutationGroup, _orbit - base_len = len(base) - degree = strong_gens[0].size - if strong_gens_distr is None: - strong_gens_distr = _distribute_gens_by_base(base, strong_gens) - temp = strong_gens_distr[:] - if basic_orbits is None: - basic_orbits = [] - for i in range(base_len): - basic_orbit = _orbit(degree, strong_gens_distr[i], base[i]) - basic_orbits.append(basic_orbit) - strong_gens_distr.append([]) - res = strong_gens[:] - for i in range(base_len - 1, -1, -1): - gens_copy = strong_gens_distr[i][:] - for gen in strong_gens_distr[i]: - if gen not in strong_gens_distr[i + 1]: - temp_gens = gens_copy[:] - temp_gens.remove(gen) - if temp_gens == []: - continue - temp_orbit = _orbit(degree, temp_gens, base[i]) - if temp_orbit == basic_orbits[i]: - gens_copy.remove(gen) - res.remove(gen) - return res -
    -
    [docs]def _strip(g, base, orbits, transversals): - """ - Attempt to decompose a permutation using a (possibly partial) BSGS - structure. - - This is done by treating the sequence ``base`` as an actual base, and - the orbits ``orbits`` and transversals ``transversals`` as basic orbits and - transversals relative to it. - - This process is called "sifting". A sift is unsuccessful when a certain - orbit element is not found or when after the sift the decomposition - doesn't end with the identity element. - - The argument ``transversals`` is a list of dictionaries that provides - transversal elements for the orbits ``orbits``. - - Parameters - ========== - - ``g`` - permutation to be decomposed - ``base`` - sequence of points - ``orbits`` - a list in which the ``i``-th entry is an orbit of ``base[i]`` - under some subgroup of the pointwise stabilizer of ` - `base[0], base[1], ..., base[i - 1]``. The groups themselves are implicit - in this function since the only infromation we need is encoded in the orbits - and transversals - ``transversals`` - a list of orbit transversals associated with the orbits - ``orbits``. - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation.print_cyclic = True - >>> from sympy.combinatorics.named_groups import SymmetricGroup - >>> from sympy.combinatorics.permutations import Permutation - >>> from sympy.combinatorics.util import _strip - >>> S = SymmetricGroup(5) - >>> S.schreier_sims() - >>> g = Permutation([0, 2, 3, 1, 4]) - >>> _strip(g, S.base, S.basic_orbits, S.basic_transversals) - (Permutation(4), 5) - - Notes - ===== - - The algorithm is described in [1],pp.89-90. The reason for returning - both the current state of the element being decomposed and the level - at which the sifting ends is that they provide important information for - the randomized version of the Schreier-Sims algorithm. - - References - ========== - - [1] Holt, D., Eick, B., O'Brien, E. - "Handbook of computational group theory" - - See Also - ======== - - sympy.combinatorics.perm_groups.PermutationGroup.schreier_sims - - sympy.combinatorics.perm_groups.PermutationGroup.schreier_sims_random - - """ - h = g._array_form - base_len = len(base) - for i in range(base_len): - beta = h[base[i]] - if beta == base[i]: - continue - if beta not in orbits[i]: - return _af_new(h), i + 1 - u = transversals[i][beta]._array_form - h = _af_rmul(_af_invert(u), h) - return _af_new(h), base_len + 1 -
    -def _strip_af(h, base, orbits, transversals, j): - """ - optimized _strip, with h, transversals and result in array form - if the stripped elements is the identity, it returns False, base_len + 1 - - j h[base[i]] == base[i] for i <= j - """ - base_len = len(base) - for i in range(j+1, base_len): - beta = h[base[i]] - if beta == base[i]: - continue - if beta not in orbits[i]: - return h, i + 1 - u = transversals[i][beta] - if h == u: - return False, base_len + 1 - h = _af_rmul(_af_invert(u), h) - return h, base_len + 1 - -
    [docs]def _strong_gens_from_distr(strong_gens_distr): - """ - Retrieve strong generating set from generators of basic stabilizers. - - This is just the union of the generators of the first and second basic - stabilizers. - - Parameters - ========== - - ``strong_gens_distr`` - strong generators distributed by membership in basic - stabilizers - - Examples - ======== - - >>> from sympy.combinatorics import Permutation - >>> Permutation.print_cyclic = True - >>> from sympy.combinatorics.named_groups import SymmetricGroup - >>> from sympy.combinatorics.util import (_strong_gens_from_distr, - ... _distribute_gens_by_base) - >>> S = SymmetricGroup(3) - >>> S.schreier_sims() - >>> S.strong_gens - [Permutation(0, 1, 2), Permutation(2)(0, 1), Permutation(1, 2)] - >>> strong_gens_distr = _distribute_gens_by_base(S.base, S.strong_gens) - >>> _strong_gens_from_distr(strong_gens_distr) - [Permutation(0, 1, 2), Permutation(2)(0, 1), Permutation(1, 2)] - - See Also - ======== - - _distribute_gens_by_base - - """ - if len(strong_gens_distr) == 1: - return strong_gens_distr[0][:] - else: - result = strong_gens_distr[0] - for gen in strong_gens_distr[1]: - if gen not in result: - result.append(gen) - return result
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/concrete/gosper.html b/dev-py3k/_modules/sympy/concrete/gosper.html deleted file mode 100644 index 2ff20abbe6b..00000000000 --- a/dev-py3k/_modules/sympy/concrete/gosper.html +++ /dev/null @@ -1,329 +0,0 @@ - - - - - - - - - - sympy.concrete.gosper — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.concrete.gosper

    -"""Gosper's algorithm for hypergeometric summation. """
    -
    -from sympy.core import S, Dummy, symbols
    -from sympy.core.compatibility import is_sequence
    -from sympy.polys import Poly, parallel_poly_from_expr, factor
    -from sympy.solvers import solve
    -from sympy.simplify import hypersimp
    -
    -
    -
    [docs]def gosper_normal(f, g, n, polys=True): - r""" - Compute the Gosper's normal form of ``f`` and ``g``. - - Given relatively prime univariate polynomials ``f`` and ``g``, - rewrite their quotient to a normal form defined as follows: - - .. math:: - \frac{f(n)}{g(n)} = Z \cdot \frac{A(n) C(n+1)}{B(n) C(n)} - - where ``Z`` is an arbitrary constant and ``A``, ``B``, ``C`` are - monic polynomials in ``n`` with the following properties: - - 1. `\gcd(A(n), B(n+h)) = 1 \forall h \in \mathbb{N}` - 2. `\gcd(B(n), C(n+1)) = 1` - 3. `\gcd(A(n), C(n)) = 1` - - This normal form, or rational factorization in other words, is a - crucial step in Gosper's algorithm and in solving of difference - equations. It can be also used to decide if two hypergeometric - terms are similar or not. - - This procedure will return a tuple containing elements of this - factorization in the form ``(Z*A, B, C)``. - - Examples - ======== - - >>> from sympy.concrete.gosper import gosper_normal - >>> from sympy.abc import n - - >>> gosper_normal(4*n+5, 2*(4*n+1)*(2*n+3), n, polys=False) - (1/4, n + 3/2, n + 1/4) - - """ - (p, q), opt = parallel_poly_from_expr( - (f, g), n, field=True, extension=True) - - a, A = p.LC(), p.monic() - b, B = q.LC(), q.monic() - - C, Z = A.one, a/b - h = Dummy('h') - - D = Poly(n + h, n, h, domain=opt.domain) - - R = A.resultant(B.compose(D)) - roots = set(R.ground_roots().keys()) - - for r in set(roots): - if not r.is_Integer or r < 0: - roots.remove(r) - - for i in sorted(roots): - d = A.gcd(B.shift(+i)) - - A = A.quo(d) - B = B.quo(d.shift(-i)) - - for j in range(1, i + 1): - C *= d.shift(-j) - - A = A.mul_ground(Z) - - if not polys: - A = A.as_expr() - B = B.as_expr() - C = C.as_expr() - - return A, B, C - -
    -
    [docs]def gosper_term(f, n): - r""" - Compute Gosper's hypergeometric term for ``f``. - - Suppose ``f`` is a hypergeometric term such that: - - .. math:: - s_n = \sum_{k=0}^{n-1} f_k - - and `f_k` doesn't depend on `n`. Returns a hypergeometric - term `g_n` such that `g_{n+1} - g_n = f_n`. - - Examples - ======== - - >>> from sympy.concrete.gosper import gosper_term - >>> from sympy.functions import factorial - >>> from sympy.abc import n - - >>> gosper_term((4*n + 1)*factorial(n)/factorial(2*n + 1), n) - (-n - 1/2)/(n + 1/4) - - """ - r = hypersimp(f, n) - - if r is None: - return None # 'f' is *not* a hypergeometric term - - p, q = r.as_numer_denom() - - A, B, C = gosper_normal(p, q, n) - B = B.shift(-1) - - N = S(A.degree()) - M = S(B.degree()) - K = S(C.degree()) - - if (N != M) or (A.LC() != B.LC()): - D = set([K - max(N, M)]) - elif not N: - D = set([K - N + 1, S(0)]) - else: - D = set([K - N + 1, (B.nth(N - 1) - A.nth(N - 1))/A.LC()]) - - for d in set(D): - if not d.is_Integer or d < 0: - D.remove(d) - - if not D: - return None # 'f(n)' is *not* Gosper-summable - - d = max(D) - - coeffs = symbols('c:%s' % (d + 1), cls=Dummy) - domain = A.get_domain().inject(*coeffs) - - x = Poly(coeffs, n, domain=domain) - H = A*x.shift(1) - B*x - C - - solution = solve(H.coeffs(), coeffs) - - if solution is None: - return None # 'f(n)' is *not* Gosper-summable - - x = x.as_expr().subs(solution) - - for coeff in coeffs: - if coeff not in solution: - x = x.subs(coeff, 0) - - if x is S.Zero: - return None # 'f(n)' is *not* Gosper-summable - else: - return B.as_expr()*x/C.as_expr() - -
    -
    [docs]def gosper_sum(f, k): - r""" - Gosper's hypergeometric summation algorithm. - - Given a hypergeometric term ``f`` such that: - - .. math :: - s_n = \sum_{k=0}^{n-1} f_k - - and `f(n)` doesn't depend on `n`, returns `g_{n} - g(0)` where - `g_{n+1} - g_n = f_n`, or ``None`` if `s_n` can not be expressed - in closed form as a sum of hypergeometric terms. - - Examples - ======== - - >>> from sympy.concrete.gosper import gosper_sum - >>> from sympy.functions import factorial - >>> from sympy.abc import i, n, k - - >>> f = (4*k + 1)*factorial(k)/factorial(2*k + 1) - >>> gosper_sum(f, (k, 0, n)) - (-n! + 2*(2*n + 1)!)/(2*n + 1)! - >>> _.subs(n, 2) == sum(f.subs(k, i) for i in [0, 1, 2]) - True - >>> gosper_sum(f, (k, 3, n)) - (-60*n! + (2*n + 1)!)/(60*(2*n + 1)!) - >>> _.subs(n, 5) == sum(f.subs(k, i) for i in [3, 4, 5]) - True - - References - ========== - - .. [1] Marko Petkovsek, Herbert S. Wilf, Doron Zeilberger, A = B, - AK Peters, Ltd., Wellesley, MA, USA, 1997, pp. 73--100 - - """ - indefinite = False - - if is_sequence(k): - k, a, b = k - else: - indefinite = True - - g = gosper_term(f, k) - - if g is None: - return None - - if indefinite: - result = f*g - else: - result = (f*(g + 1)).subs(k, b) - (f*g).subs(k, a) - - return factor(result)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/concrete/products.html b/dev-py3k/_modules/sympy/concrete/products.html deleted file mode 100644 index 0a241cd716e..00000000000 --- a/dev-py3k/_modules/sympy/concrete/products.html +++ /dev/null @@ -1,376 +0,0 @@ - - - - - - - - - - sympy.concrete.products — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.concrete.products

    -from sympy.core import C, Expr, Mul, S, sympify
    -from sympy.functions.elementary.piecewise import piecewise_fold
    -from sympy.polys import quo, roots
    -from sympy.simplify import powsimp
    -
    -
    -
    [docs]class Product(Expr): - """Represents unevaluated product. - - """ - - __slots__ = ['is_commutative'] - - def __new__(cls, function, *symbols, **assumptions): - from sympy.integrals.integrals import _process_limits - - # Any embedded piecewise functions need to be brought out to the - # top level so that integration can go into piecewise mode at the - # earliest possible moment. - function = piecewise_fold(sympify(function)) - - if function is S.NaN: - return S.NaN - - if not symbols: - raise ValueError("Product variables must be given") - - limits, sign = _process_limits(*symbols) - - # Only limits with lower and upper bounds are supported; the indefinite - # Product is not supported - if any(len(l) != 3 or None in l for l in limits): - raise ValueError( - 'Product requires values for lower and upper bounds.') - - obj = Expr.__new__(cls, **assumptions) - arglist = [sign*function] - arglist.extend(limits) - obj._args = tuple(arglist) - obj.is_commutative = function.is_commutative # limits already checked - - return obj - - @property - def term(self): - return self._args[0] - function = term - - @property - def limits(self): - return self._args[1:] - - @property -
    [docs] def variables(self): - """Return a list of the product variables - - >>> from sympy import Product - >>> from sympy.abc import x, i - >>> Product(x**i, (i, 1, 3)).variables - [i] - """ - return [l[0] for l in self.limits] -
    - @property -
    [docs] def free_symbols(self): - """ - This method returns the symbols that will affect the value of - the Product when evaluated. This is useful if one is trying to - determine whether a product depends on a certain symbol or not. - - >>> from sympy import Product - >>> from sympy.abc import x, y - >>> Product(x, (x, y, 1)).free_symbols - set([y]) - """ - from sympy.concrete.summations import _free_symbols - - if self.function.is_zero or self.function == 1: - return set() - return _free_symbols(self.function, self.limits) -
    - @property -
    [docs] def is_zero(self): - """A Product is zero only if its term is zero. - """ - return self.term.is_zero -
    - @property -
    [docs] def is_number(self): - """ - Return True if the Product will result in a number, else False. - - sympy considers anything that will result in a number to have - is_number == True. - - >>> from sympy import log, Product - >>> from sympy.abc import x, y, z - >>> log(2).is_number - True - >>> Product(x, (x, 1, 2)).is_number - True - >>> Product(y, (x, 1, 2)).is_number - False - >>> Product(1, (x, y, z)).is_number - True - >>> Product(2, (x, y, z)).is_number - False - """ - - return self.function.is_zero or self.function == 1 or not self.free_symbols -
    - def doit(self, **hints): - f = g = self.function - for index, limit in enumerate(self.limits): - i, a, b = limit - dif = b - a - if dif.is_Integer and dif < 0: - a, b = b, a - - g = self._eval_product(f, (i, a, b)) - if g is None: - return Product(powsimp(f), *self.limits[index:]) - else: - f = g - - if hints.get('deep', True): - return f.doit(**hints) - else: - return powsimp(f) - - def _eval_adjoint(self): - if self.is_commutative: - return Product(self.function.adjoint(), *self.limits) - return None - - def _eval_conjugate(self): - return Product(self.function.conjugate(), *self.limits) - - def _eval_product(self, term, limits): - from sympy import summation - - (k, a, n) = limits - - if k not in term.free_symbols: - return term**(n - a + 1) - - if a == n: - return term.subs(k, a) - - dif = n - a - if dif.is_Integer: - return Mul(*[term.subs(k, a + i) for i in range(dif + 1)]) - - elif term.is_polynomial(k): - poly = term.as_poly(k) - - A = B = Q = S.One - - all_roots = roots(poly, multiple=True) - - for r in all_roots: - A *= C.RisingFactorial(a - r, n - a + 1) - Q *= n - r - - if len(all_roots) < poly.degree(): - arg = quo(poly, Q.as_poly(k)) - B = Product(arg, (k, a, n)).doit() - - return poly.LC()**(n - a + 1) * A * B - - elif term.is_Add: - p, q = term.as_numer_denom() - - p = self._eval_product(p, (k, a, n)) - q = self._eval_product(q, (k, a, n)) - - return p / q - - elif term.is_Mul: - exclude, include = [], [] - - for t in term.args: - p = self._eval_product(t, (k, a, n)) - - if p is not None: - exclude.append(p) - else: - include.append(t) - - if not exclude: - return None - else: - arg = term._new_rawargs(*include) - A = Mul(*exclude) - B = Product(arg, (k, a, n)).doit() - return A * B - - elif term.is_Pow: - if not term.base.has(k): - s = summation(term.exp, (k, a, n)) - - return term.base**s - elif not term.exp.has(k): - p = self._eval_product(term.base, (k, a, n)) - - if p is not None: - return p**term.exp - - elif isinstance(term, Product): - evaluated = term.doit() - f = self._eval_product(evaluated, limits) - if f is None: - return Product(evaluated, limits) - else: - return f - - def _eval_transpose(self): - if self.is_commutative: - return Product(self.function.transpose(), *self.limits) - return None - -
    -
    [docs]def product(*args, **kwargs): - r""" - Compute the product. - - The notation for symbols is similiar to the notation used in Sum or - Integral. product(f, (i, a, b)) computes the product of f with - respect to i from a to b, i.e., - - :: - - b - _____ - product(f(n), (i, a, b)) = | | f(n) - | | - i = a - - If it cannot compute the product, it returns an unevaluated Product object. - Repeated products can be computed by introducing additional symbols tuples:: - - >>> from sympy import product, symbols - >>> i, n, m, k = symbols('i n m k', integer=True) - - >>> product(i, (i, 1, k)) - k! - >>> product(m, (i, 1, k)) - m**k - >>> product(i, (i, 1, k), (k, 1, n)) - Product(k!, (k, 1, n)) - - """ - - prod = Product(*args, **kwargs) - - if isinstance(prod, Product): - return prod.doit(deep=False) - else: - return prod
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/concrete/summations.html b/dev-py3k/_modules/sympy/concrete/summations.html deleted file mode 100644 index 92f70866684..00000000000 --- a/dev-py3k/_modules/sympy/concrete/summations.html +++ /dev/null @@ -1,705 +0,0 @@ - - - - - - - - - - sympy.concrete.summations — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.concrete.summations

    -from sympy.core import Add, C, Derivative, Dummy, Expr, S, sympify, Wild
    -from sympy.concrete.gosper import gosper_sum
    -from sympy.functions.elementary.piecewise import piecewise_fold
    -from sympy.polys import apart, PolynomialError
    -from sympy.solvers import solve
    -
    -
    -def _free_symbols(function, limits):
    -    """Helper function to return the symbols that appear in a sum-like object
    -    once it is evaluated.
    -    """
    -    isyms = function.free_symbols
    -    for xab in limits:
    -        # take out the target symbol
    -        if xab[0] in isyms:
    -            isyms.remove(xab[0])
    -        # add in the new symbols
    -        for i in xab[1:]:
    -            isyms.update(i.free_symbols)
    -    return isyms
    -
    -
    -
    [docs]class Sum(Expr): - """Represents unevaluated summation.""" - - __slots__ = ['is_commutative'] - - def __new__(cls, function, *symbols, **assumptions): - from sympy.integrals.integrals import _process_limits - - # Any embedded piecewise functions need to be brought out to the - # top level so that integration can go into piecewise mode at the - # earliest possible moment. - function = piecewise_fold(sympify(function)) - - if function is S.NaN: - return S.NaN - - if not symbols: - raise ValueError("Summation variables must be given") - - limits, sign = _process_limits(*symbols) - - # Only limits with lower and upper bounds are supported; the indefinite Sum - # is not supported - if any(len(l) != 3 or None in l for l in limits): - raise ValueError('Sum requires values for lower and upper bounds.') - - obj = Expr.__new__(cls, **assumptions) - arglist = [sign*function] - arglist.extend(limits) - obj._args = tuple(arglist) - obj.is_commutative = function.is_commutative # limits already checked - - return obj - - @property - def function(self): - return self._args[0] - - @property - def limits(self): - return self._args[1:] - - @property -
    [docs] def variables(self): - """Return a list of the summation variables - - >>> from sympy import Sum - >>> from sympy.abc import x, i - >>> Sum(x**i, (i, 1, 3)).variables - [i] - """ - return [l[0] for l in self.limits] -
    - @property -
    [docs] def free_symbols(self): - """ - This method returns the symbols that will exist when the - summation is evaluated. This is useful if one is trying to - determine whether a sum depends on a certain symbol or not. - - >>> from sympy import Sum - >>> from sympy.abc import x, y - >>> Sum(x, (x, y, 1)).free_symbols - set([y]) - """ - if self.function.is_zero: - return set() - return _free_symbols(self.function, self.limits) -
    - @property -
    [docs] def is_zero(self): - """A Sum is only zero if its function is zero or if all terms - cancel out. This only answers whether the summand zero.""" - - return self.function.is_zero -
    - @property -
    [docs] def is_number(self): - """ - Return True if the Sum will result in a number, else False. - - sympy considers anything that will result in a number to have - is_number == True. - - >>> from sympy import log - >>> log(2).is_number - True - - Sums are a special case since they contain symbols that can - be replaced with numbers. Whether the integral can be done or not is - another issue. But answering whether the final result is a number is - not difficult. - - >>> from sympy import Sum - >>> from sympy.abc import x, y - >>> Sum(x, (y, 1, x)).is_number - False - >>> Sum(1, (y, 1, x)).is_number - False - >>> Sum(0, (y, 1, x)).is_number - True - >>> Sum(x, (y, 1, 2)).is_number - False - >>> Sum(x, (y, 1, 1)).is_number - False - >>> Sum(x, (x, 1, 2)).is_number - True - >>> Sum(x*y, (x, 1, 2), (y, 1, 3)).is_number - True - """ - - return self.function.is_zero or not self.free_symbols -
    - def doit(self, **hints): - #if not hints.get('sums', True): - # return self - f = self.function - for limit in self.limits: - i, a, b = limit - dif = b - a - if dif.is_Integer and dif < 0: - a, b = b, a - - f = eval_sum(f, (i, a, b)) - if f is None: - return self - - if hints.get('deep', True): - return f.doit(**hints) - else: - return f - - def _eval_adjoint(self): - return Sum(self.function.adjoint(), *self.limits) - - def _eval_conjugate(self): - return Sum(self.function.conjugate(), *self.limits) - - def _eval_derivative(self, x): - """ - Differentiate wrt x as long as x is not in the free symbols of any of - the upper or lower limits. - - Sum(a*b*x, (x, 1, a)) can be differentiated wrt x or b but not `a` - since the value of the sum is discontinuous in `a`. In a case - involving a limit variable, the unevaluated derivative is returned. - """ - - # diff already confirmed that x is in the free symbols of self, but we - # don't want to differentiate wrt any free symbol in the upper or lower - # limits - # XXX remove this test for free_symbols when the default _eval_derivative is in - if x not in self.free_symbols: - return S.Zero - - # get limits and the function - f, limits = self.function, list(self.limits) - - limit = limits.pop(-1) - - if limits: # f is the argument to a Sum - f = Sum(f, *limits) - - if len(limit) == 3: - _, a, b = limit - if x in a.free_symbols or x in b.free_symbols: - return None - df = Derivative(f, x, **{'evaluate': True}) - rv = Sum(df, limit) - if limit[0] not in df.free_symbols: - rv = rv.doit() - return rv - else: - return NotImplementedError('Lower and upper bound expected.') - - def _eval_summation(self, f, x): - return None - - def _eval_transpose(self): - return Sum(self.function.transpose(), *self.limits) - -
    [docs] def euler_maclaurin(self, m=0, n=0, eps=0, eval_integral=True): - """ - Return an Euler-Maclaurin approximation of self, where m is the - number of leading terms to sum directly and n is the number of - terms in the tail. - - With m = n = 0, this is simply the corresponding integral - plus a first-order endpoint correction. - - Returns (s, e) where s is the Euler-Maclaurin approximation - and e is the estimated error (taken to be the magnitude of - the first omitted term in the tail): - - >>> from sympy.abc import k, a, b - >>> from sympy import Sum - >>> Sum(1/k, (k, 2, 5)).doit().evalf() - 1.28333333333333 - >>> s, e = Sum(1/k, (k, 2, 5)).euler_maclaurin() - >>> s - -log(2) + 7/20 + log(5) - >>> from sympy import sstr - >>> print(sstr((s.evalf(), e.evalf()), full_prec=True)) - (1.26629073187415, 0.0175000000000000) - - The endpoints may be symbolic: - - >>> s, e = Sum(1/k, (k, a, b)).euler_maclaurin() - >>> s - -log(a) + log(b) + 1/(2*b) + 1/(2*a) - >>> e - Abs(-1/(12*b**2) + 1/(12*a**2)) - - If the function is a polynomial of degree at most 2n+1, the - Euler-Maclaurin formula becomes exact (and e = 0 is returned): - - >>> Sum(k, (k, 2, b)).euler_maclaurin() - (b**2/2 + b/2 - 1, 0) - >>> Sum(k, (k, 2, b)).doit() - b**2/2 + b/2 - 1 - - With a nonzero eps specified, the summation is ended - as soon as the remainder term is less than the epsilon. - """ - m = int(m) - n = int(n) - f = self.function - assert len(self.limits) == 1 - i, a, b = self.limits[0] - s = S.Zero - if m: - for k in range(m): - term = f.subs(i, a + k) - if (eps and term and abs(term.evalf(3)) < eps): - return s, abs(term) - s += term - a += m - x = Dummy('x') - I = C.Integral(f.subs(i, x), (x, a, b)) - if eval_integral: - I = I.doit() - s += I - - def fpoint(expr): - if b is S.Infinity: - return expr.subs(i, a), 0 - return expr.subs(i, a), expr.subs(i, b) - fa, fb = fpoint(f) - iterm = (fa + fb)/2 - g = f.diff(i) - for k in range(1, n + 2): - ga, gb = fpoint(g) - term = C.bernoulli(2*k)/C.factorial(2*k)*(gb - ga) - if (eps and term and abs(term.evalf(3)) < eps) or (k > n): - break - s += term - g = g.diff(i, 2) - return s + iterm, abs(term) -
    - def _eval_subs(self, old, new): # XXX this should be the same as Integral's - if any(old == v for v in self.variables): - return self - -
    -
    [docs]def summation(f, *symbols, **kwargs): - r""" - Compute the summation of f with respect to symbols. - - The notation for symbols is similar to the notation used in Integral. - summation(f, (i, a, b)) computes the sum of f with respect to i from a to b, - i.e., - - :: - - b - ____ - \ ` - summation(f, (i, a, b)) = ) f - /___, - i = a - - If it cannot compute the sum, it returns an unevaluated Sum object. - Repeated sums can be computed by introducing additional symbols tuples:: - - >>> from sympy import summation, oo, symbols, log - >>> i, n, m = symbols('i n m', integer=True) - - >>> summation(2*i - 1, (i, 1, n)) - n**2 - >>> summation(1/2**i, (i, 0, oo)) - 2 - >>> summation(1/log(n)**n, (n, 2, oo)) - Sum(log(n)**(-n), (n, 2, oo)) - >>> summation(i, (i, 0, n), (n, 0, m)) - m**3/6 + m**2/2 + m/3 - - >>> from sympy.abc import x - >>> from sympy import factorial - >>> summation(x**n/factorial(n), (n, 0, oo)) - exp(x) - - """ - return Sum(f, *symbols, **kwargs).doit(deep=False) - -
    -def telescopic_direct(L, R, n, limits): - """Returns the direct summation of the terms of a telescopic sum - - L is the term with lower index - R is the term with higher index - n difference between the indexes of L and R - - For example: - - >>> from sympy.concrete.summations import telescopic_direct - >>> from sympy.abc import k, a, b - >>> telescopic_direct(1/k, -1/(k+2), 2, (k, a, b)) - -1/(b + 2) - 1/(b + 1) + 1/(a + 1) + 1/a - - """ - (i, a, b) = limits - s = 0 - for m in range(n): - s += L.subs(i, a + m) + R.subs(i, b - m) - return s - - -def telescopic(L, R, limits): - '''Tries to perform the summation using the telescopic property - - return None if not possible - ''' - (i, a, b) = limits - if L.is_Add or R.is_Add: - return None - - # We want to solve(L.subs(i, i + m) + R, m) - # First we try a simple match since this does things that - # solve doesn't do, e.g. solve(f(k+m)-f(k), m) fails - - k = Wild("k") - sol = (-R).match(L.subs(i, i + k)) - s = None - if sol and k in sol: - s = sol[k] - if not (s.is_Integer and L.subs(i, i + s) == -R): - #sometimes match fail(f(x+2).match(-f(x+k))->{k: -2 - 2x})) - s = None - - # But there are things that match doesn't do that solve - # can do, e.g. determine that 1/(x + m) = 1/(1 - x) when m = 1 - - if s is None: - m = Dummy('m') - try: - sol = solve(L.subs(i, i + m) + R, m) or [] - except NotImplementedError: - return None - sol = [si for si in sol if si.is_Integer and - (L.subs(i, i + si) + R).expand().is_zero] - if len(sol) != 1: - return None - s = sol[0] - - if s < 0: - return telescopic_direct(R, L, abs(s), (i, a, b)) - elif s > 0: - return telescopic_direct(L, R, s, (i, a, b)) - - -def eval_sum(f, limits): - (i, a, b) = limits - if f is S.Zero: - return S.Zero - if i not in f.free_symbols: - return f*(b - a + 1) - if a == b: - return f.subs(i, a) - - dif = b - a - definite = dif.is_Integer - # Doing it directly may be faster if there are very few terms. - if definite and (dif < 100): - return eval_sum_direct(f, (i, a, b)) - # Try to do it symbolically. Even when the number of terms is known, - # this can save time when b-a is big. - # We should try to transform to partial fractions - value = eval_sum_symbolic(f.expand(), (i, a, b)) - if value is not None: - return value - # Do it directly - if definite: - return eval_sum_direct(f, (i, a, b)) - - -def eval_sum_direct(expr, limits): - (i, a, b) = limits - - dif = b - a - return Add(*[expr.subs(i, a + j) for j in range(dif + 1)]) - - -def eval_sum_symbolic(f, limits): - (i, a, b) = limits - if not f.has(i): - return f*(b - a + 1) - - # Linearity - if f.is_Mul: - L, R = f.as_two_terms() - - if not L.has(i): - sR = eval_sum_symbolic(R, (i, a, b)) - if sR: - return L*sR - - if not R.has(i): - sL = eval_sum_symbolic(L, (i, a, b)) - if sL: - return R*sL - - try: - f = apart(f, i) # see if it becomes an Add - except PolynomialError: - pass - - if f.is_Add: - L, R = f.as_two_terms() - lrsum = telescopic(L, R, (i, a, b)) - - if lrsum: - return lrsum - - lsum = eval_sum_symbolic(L, (i, a, b)) - rsum = eval_sum_symbolic(R, (i, a, b)) - - if None not in (lsum, rsum): - return lsum + rsum - - # Polynomial terms with Faulhaber's formula - n = Wild('n') - result = f.match(i**n) - - if result is not None: - n = result[n] - - if n.is_Integer: - if n >= 0: - return ((C.bernoulli(n + 1, b + 1) - C.bernoulli(n + 1, a))/(n + 1)).expand() - elif a.is_Integer and a >= 1: - if n == -1: - return C.harmonic(b) - C.harmonic(a - 1) - else: - return C.harmonic(b, abs(n)) - C.harmonic(a - 1, abs(n)) - - # Geometric terms - c1 = C.Wild('c1', exclude=[i]) - c2 = C.Wild('c2', exclude=[i]) - c3 = C.Wild('c3', exclude=[i]) - - e = f.match(c1**(c2*i + c3)) - - if e is not None: - c1 = c1.subs(e) - c2 = c2.subs(e) - c3 = c3.subs(e) - - # TODO: more general limit handling - return c1**c3 * (c1**(a*c2) - c1**(c2 + b*c2)) / (1 - c1**c2) - - if not (a.has(S.Infinity, S.NegativeInfinity) or - b.has(S.Infinity, S.NegativeInfinity)): - r = gosper_sum(f, (i, a, b)) - - if not r in (None, S.NaN): - return r - - return eval_sum_hyper(f, (i, a, b)) - - -def _eval_sum_hyper(f, i, a): - """ Returns (res, cond). Sums from a to oo. """ - from sympy.functions import hyper - from sympy.simplify import hyperexpand, hypersimp, fraction, simplify - from sympy.polys.polytools import Poly, factor - - if a != 0: - return _eval_sum_hyper(f.subs(i, i + a), i, 0) - - if f.subs(i, 0) == 0: - if simplify(f.subs(i, Dummy('i', integer=True, positive=True))) == 0: - return S(0), True - return _eval_sum_hyper(f.subs(i, i + 1), i, 0) - - hs = hypersimp(f, i) - if hs is None: - return None - - numer, denom = fraction(factor(hs)) - top, topl = numer.as_coeff_mul(i) - bot, botl = denom.as_coeff_mul(i) - ab = [top, bot] - factors = [topl, botl] - params = [[], []] - for k in range(2): - for fac in factors[k]: - mul = 1 - if fac.is_Pow: - mul = fac.exp - fac = fac.base - if not mul.is_Integer: - return None - p = Poly(fac, i) - if p.degree() != 1: - return None - m, n = p.all_coeffs() - ab[k] *= m**mul - params[k] += [n/m]*mul - - # Add "1" to numerator parameters, to account for implicit n! in - # hypergeometric series. - ap = params[0] + [1] - bq = params[1] - x = ab[0]/ab[1] - h = hyper(ap, bq, x) - - return f.subs(i, 0)*hyperexpand(h), h.convergence_statement - - -def eval_sum_hyper(f, xxx_todo_changeme): - (i, a, b) = xxx_todo_changeme - from sympy.functions import Piecewise - from sympy import oo, And - - if b != oo: - if a == -oo: - res = _eval_sum_hyper(f.subs(i, -i), i, -b) - if res is not None: - return Piecewise(res, (Sum(f, (i, a, b)), True)) - else: - res1 = _eval_sum_hyper(f, i, a) - res2 = _eval_sum_hyper(f, i, b + 1) - if res1 is None or res2 is None: - return None - (res1, cond1), (res2, cond2) = res1, res2 - cond = And(cond1, cond2) - if cond is False: - return None - return Piecewise((res1 - res2, cond), (Sum(f, (i, a, b)), True)) - - if a == -oo: - res1 = _eval_sum_hyper(f.subs(i, -i), i, 1) - res2 = _eval_sum_hyper(f, i, 0) - if res1 is None or res2 is None: - return None - res1, cond1 = res1 - res2, cond2 = res2 - cond = And(cond1, cond2) - if cond is False: - return None - return Piecewise((res1 + res2, cond), (Sum(f, (i, a, b)), True)) - - # Now b == oo, a != -oo - res = _eval_sum_hyper(f, i, a) - if res is not None: - return Piecewise(res, (Sum(f, (i, a, b)), True)) -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/core/add.html b/dev-py3k/_modules/sympy/core/add.html deleted file mode 100644 index c0ca56c8b49..00000000000 --- a/dev-py3k/_modules/sympy/core/add.html +++ /dev/null @@ -1,964 +0,0 @@ - - - - - - - - - - sympy.core.add — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.core.add

    -from collections import defaultdict
    -
    -from sympy.core.core import C
    -from sympy.core.singleton import S
    -from sympy.core.operations import AssocOp
    -from sympy.core.cache import cacheit
    -from sympy.core.numbers import ilcm, igcd
    -from sympy.core.expr import Expr
    -from functools import reduce
    -
    -
    -
    [docs]class Add(Expr, AssocOp): - - __slots__ = [] - - is_Add = True - - #identity = S.Zero - # cyclic import, so defined in numbers.py - - @classmethod -
    [docs] def flatten(cls, seq): - """ - Takes the sequence "seq" of nested Adds and returns a flatten list. - - Returns: (commutative_part, noncommutative_part, order_symbols) - - Applies associativity, all terms are commutable with respect to - addition. - - NB: the removal of 0 is already handled by AssocOp.__new__ - - See also - ======== - - sympy.core.mul.Mul.flatten - - """ - rv = None - if len(seq) == 2: - a, b = seq - if b.is_Rational: - a, b = b, a - if a.is_Rational: - if b.is_Mul: - # if it's an unevaluated 2-arg, expand it - c, t = b.as_coeff_Mul() - if t.is_Add: - h, t = t.as_coeff_Add() - bargs = [c*ti for ti in Add.make_args(t)] - bargs.sort(key=hash) - ch = c*h - if ch: - bargs.insert(0, ch) - b = Add._from_args(bargs) - if b.is_Add: - bargs = list(b.args) - if bargs[0].is_Number: - bargs[0] += a - if not bargs[0]: - bargs.pop(0) - else: - bargs.insert(0, a) - rv = bargs, [], None - elif b.is_Mul: - rv = [a, b], [], None - if rv: - if all(s.is_commutative for s in rv[0]): - return rv - return [], rv[0], None - - terms = {} # term -> coeff - # e.g. x**2 -> 5 for ... + 5*x**2 + ... - - coeff = S.Zero # coefficient (Number or zoo) to always be in slot 0 - # e.g. 3 + ... - order_factors = [] - - for o in seq: - - # O(x) - if o.is_Order: - for o1 in order_factors: - if o1.contains(o): - o = None - break - if o is None: - continue - order_factors = [o] + [ - o1 for o1 in order_factors if not o.contains(o1)] - continue - - # 3 or NaN - elif o.is_Number: - if (o is S.NaN or coeff is S.ComplexInfinity and - o.is_bounded is False): - # we know for sure the result will be nan - return [S.NaN], [], None - if coeff.is_Number: - coeff += o - if coeff is S.NaN: - # we know for sure the result will be nan - return [S.NaN], [], None - continue - - elif o is S.ComplexInfinity: - if coeff.is_bounded is False: - # we know for sure the result will be nan - return [S.NaN], [], None - coeff = S.ComplexInfinity - continue - - # Add([...]) - elif o.is_Add: - # NB: here we assume Add is always commutative - seq.extend(o.args) # TODO zerocopy? - continue - - # Mul([...]) - elif o.is_Mul: - c, s = o.as_coeff_Mul() - - # 3*... - # unevaluated 2-arg Mul, but we always unfold it so - # it can combine with other terms (just like is done - # with the Pow below) - if c.is_Number and s.is_Add: - seq.extend([c*a for a in s.args]) - continue - - # check for unevaluated Pow, e.g. 2**3 or 2**(-1/2) - elif o.is_Pow: - b, e = o.as_base_exp() - if b.is_Number and (e.is_Integer or - (e.is_Rational and e.is_negative)): - seq.append(b**e) - continue - c, s = S.One, o - - else: - # everything else - c = S.One - s = o - - # now we have: - # o = c*s, where - # - # c is a Number - # s is an expression with number factor extracted - # let's collect terms with the same s, so e.g. - # 2*x**2 + 3*x**2 -> 5*x**2 - if s in terms: - terms[s] += c - else: - terms[s] = c - - # now let's construct new args: - # [2*x**2, x**3, 7*x**4, pi, ...] - newseq = [] - noncommutative = False - for s, c in list(terms.items()): - # 0*s - if c is S.Zero: - continue - # 1*s - elif c is S.One: - newseq.append(s) - # c*s - else: - if s.is_Mul: - # Mul, already keeps its arguments in perfect order. - # so we can simply put c in slot0 and go the fast way. - cs = s._new_rawargs(*((c,) + s.args)) - newseq.append(cs) - - else: - # alternatively we have to call all Mul's machinery (slow) - newseq.append(Mul(c, s)) - - noncommutative = noncommutative or not s.is_commutative - - # oo, -oo - if coeff is S.Infinity: - newseq = [f for f in newseq if not - (f.is_nonnegative or f.is_real and - (f.is_bounded or f.is_finite or f.is_infinitesimal))] - - elif coeff is S.NegativeInfinity: - newseq = [f for f in newseq if not - (f.is_nonpositive or f.is_real and - (f.is_bounded or f.is_finite or f.is_infinitesimal))] - - if coeff is S.ComplexInfinity: - # zoo might be - # unbounded_real + bounded_im - # bounded_real + unbounded_im - # unbounded_real + unbounded_im - # addition of a bounded real or imaginary number won't be able to - # change the zoo nature; if unbounded a NaN condition could result - # if the unbounded symbol had sign opposite of the unbounded - # portion of zoo, e.g., unbounded_real - unbounded_real. - newseq = [c for c in newseq if not (c.is_bounded and - c.is_real is not None)] - - # process O(x) - if order_factors: - newseq2 = [] - for t in newseq: - for o in order_factors: - # x + O(x) -> O(x) - if o.contains(t): - t = None - break - # x + O(x**2) -> x + O(x**2) - if t is not None: - newseq2.append(t) - newseq = newseq2 + order_factors - # 1 + O(1) -> O(1) - for o in order_factors: - if o.contains(coeff): - coeff = S.Zero - break - - # order args canonically - # Currently we sort things using hashes, as it is quite fast. A better - # solution is not to sort things at all - but this needs some more - # fixing. NOTE: this is used in primitive and Mul.flattten, too, so if - # it changes here it should be changed there. - newseq.sort(key=hash) - - # current code expects coeff to be first - if coeff is not S.Zero: - newseq.insert(0, coeff) - - # we are done - if noncommutative: - return [], newseq, None - else: - return newseq, [], None -
    - @classmethod -
    [docs] def class_key(cls): - """Nice order of classes""" - return 3, 1, cls.__name__ -
    -
    [docs] def as_coefficients_dict(a): - """Return a dictionary mapping terms to their Rational coefficient. - Since the dictionary is a defaultdict, inquiries about terms which - were not present will return a coefficient of 0. If an expression is - not an Add it is considered to have a single term. - - Examples - ======== - - >>> from sympy.abc import a, x - >>> (3*x + a*x + 4).as_coefficients_dict() - {1: 4, x: 3, a*x: 1} - >>> _[a] - 0 - >>> (3*a*x).as_coefficients_dict() - {a*x: 3} - """ - - d = defaultdict(list) - for ai in a.args: - c, m = ai.as_coeff_Mul() - d[m].append(c) - for k, v in d.items(): - if len(v) == 1: - d[k] = v[0] - else: - d[k] = Add(*v) - di = defaultdict(int) - di.update(d) - return di -
    - @cacheit -
    [docs] def as_coeff_add(self, *deps): - """ - Returns a tuple (coeff, args) where self is treated as an Add and coeff - is the Number term and args is a tuple of all other terms. - - Examples - ======== - - >>> from sympy.abc import x, y - >>> (7 + 3*x).as_coeff_add() - (7, (3*x,)) - >>> (7*x).as_coeff_add() - (0, (7*x,)) - """ - if deps: - l1 = [] - l2 = [] - for f in self.args: - if f.has(*deps): - l2.append(f) - else: - l1.append(f) - return self._new_rawargs(*l1), tuple(l2) - coeff, notrat = self.args[0].as_coeff_add() - if coeff is not S.Zero: - return coeff, notrat + self.args[1:] - return S.Zero, self.args -
    -
    [docs] def as_coeff_Add(self): - """Efficiently extract the coefficient of a summation. """ - coeff, args = self.args[0], self.args[1:] - - if coeff.is_Number: - if len(args) == 1: - return coeff, args[0] - else: - return coeff, self._new_rawargs(*args) - else: - return S.Zero, self - - # Note, we intentionally do not implement Add.as_coeff_mul(). Rather, we - # let Expr.as_coeff_mul() just always return (S.One, self) for an Add. See - # issue 2425. -
    - def _eval_derivative(self, s): - return Add(*[f.diff(s) for f in self.args]) - - def _eval_nseries(self, x, n, logx): - terms = [t.nseries(x, n=n, logx=logx) for t in self.args] - return Add(*terms) - - def _matches_simple(self, expr, repl_dict): - # handle (w+3).matches('x+5') -> {w: x+2} - coeff, terms = self.as_coeff_add() - if len(terms) == 1: - return terms[0].matches(expr - coeff, repl_dict) - return - - def matches(self, expr, repl_dict={}, old=False): - return AssocOp._matches_commutative(self, expr, repl_dict, old) - - @staticmethod - def _combine_inverse(lhs, rhs): - """ - Returns lhs - rhs, but treats arguments like symbols, so things like - oo - oo return 0, instead of a nan. - """ - from sympy import oo, I, expand_mul - if lhs == oo and rhs == oo or lhs == oo*I and rhs == oo*I: - return S.Zero - return expand_mul(lhs - rhs) - - @cacheit -
    [docs] def as_two_terms(self): - """Return head and tail of self. - - This is the most efficient way to get the head and tail of an - expression. - - - if you want only the head, use self.args[0]; - - if you want to process the arguments of the tail then use - self.as_coef_add() which gives the head and a tuple containing - the arguments of the tail when treated as an Add. - - if you want the coefficient when self is treated as a Mul - then use self.as_coeff_mul()[0] - - >>> from sympy.abc import x, y - >>> (3*x*y).as_two_terms() - (3, x*y) - """ - if len(self.args) == 1: - return S.Zero, self - return self.args[0], self._new_rawargs(*self.args[1:]) -
    - def as_numer_denom(self): - - # clear rational denominator - content, expr = self.primitive() - ncon, dcon = content.as_numer_denom() - - # collect numerators and denominators of the terms - nd = defaultdict(list) - for f in expr.args: - ni, di = f.as_numer_denom() - nd[di].append(ni) - # put infinity in the numerator - if S.Zero in nd: - n = nd.pop(S.Zero) - assert len(n) == 1 - n = n[0] - nd[S.One].append(n/S.Zero) - - # check for quick exit - if len(nd) == 1: - d, n = nd.popitem() - return Add( - *[_keep_coeff(ncon, ni) for ni in n]), _keep_coeff(dcon, d) - - # sum up the terms having a common denominator - for d, n in nd.items(): - if len(n) == 1: - nd[d] = n[0] - else: - nd[d] = Add(*n) - - # assemble single numerator and denominator - denoms, numers = [list(i) for i in zip(*iter(nd.items()))] - n, d = Add(*[Mul(*(denoms[:i] + [numers[i]] + denoms[i + 1:])) - for i in range(len(numers))]), Mul(*denoms) - - return _keep_coeff(ncon, n), _keep_coeff(dcon, d) - - def _eval_is_polynomial(self, syms): - return all(term._eval_is_polynomial(syms) for term in self.args) - - def _eval_is_rational_function(self, syms): - return all(term._eval_is_rational_function(syms) for term in self.args) - - # assumption methods - _eval_is_real = lambda self: self._eval_template_is_attr( - 'is_real', when_multiple=None) - _eval_is_antihermitian = lambda self: self._eval_template_is_attr( - 'is_antihermitian', when_multiple=None) - _eval_is_bounded = lambda self: self._eval_template_is_attr( - 'is_bounded', when_multiple=None) - _eval_is_hermitian = lambda self: self._eval_template_is_attr( - 'is_hermitian', when_multiple=None) - _eval_is_imaginary = lambda self: self._eval_template_is_attr( - 'is_imaginary', when_multiple=None) - _eval_is_integer = lambda self: self._eval_template_is_attr( - 'is_integer', when_multiple=None) - _eval_is_commutative = lambda self: self._eval_template_is_attr( - 'is_commutative') - - def _eval_is_odd(self): - l = [f for f in self.args if not (f.is_even is True)] - if not l: - return False - if l[0].is_odd: - return self._new_rawargs(*l[1:]).is_even - - def _eval_is_irrational(self): - for t in self.args: - a = t.is_irrational - if a: - others = list(self.args) - others.remove(t) - if all(x.is_rational is True for x in others): - return True - return None - if a is None: - return - return False - - def _eval_is_positive(self): - if self.is_number: - return super(Add, self)._eval_is_positive() - pos = nonneg = nonpos = unknown_sign = False - unbounded = set() - args = [a for a in self.args if not a.is_zero] - if not args: - return False - for a in args: - ispos = a.is_positive - ubound = a.is_unbounded - if ubound: - unbounded.add(ispos) - if len(unbounded) > 1: - return None - if ispos: - pos = True - continue - elif a.is_nonnegative: - nonneg = True - continue - elif a.is_nonpositive: - nonpos = True - continue - elif a.is_zero: - continue - - if ubound is None: - # sign is unknown; if we don't know the boundedness - # we're done: we don't know. That is technically true, - # but the only option is that we have something like - # oo - oo which is NaN and it really doesn't matter - # what sign we apply to that because it (when finally - # computed) will trump any sign. So instead of returning - # None, we pass. - pass - else: - return None - unknown_sign = True - - if unbounded: - return unbounded.pop() - elif unknown_sign: - return None - elif not nonpos and not nonneg and pos: - return True - elif not nonpos and pos: - return True - elif not pos and not nonneg: - return False - - def _eval_is_negative(self): - if self.is_number: - return super(Add, self)._eval_is_negative() - neg = nonpos = nonneg = unknown_sign = False - unbounded = set() - args = [a for a in self.args if not a.is_zero] - if not args: - return False - for a in args: - isneg = a.is_negative - ubound = a.is_unbounded - if ubound: - unbounded.add(isneg) - if len(unbounded) > 1: - return None - if isneg: - neg = True - continue - elif a.is_nonpositive: - nonpos = True - continue - elif a.is_nonnegative: - nonneg = True - continue - elif a.is_zero: - continue - - if ubound is None: - # sign is unknown; if we don't know the boundedness - # we're done: we don't know. That is technically true, - # but the only option is that we have something like - # oo - oo which is NaN and it really doesn't matter - # what sign we apply to that because it (when finally - # computed) will trump any sign. So instead of returning - # None, we pass. - pass - unknown_sign = True - - if unbounded: - return unbounded.pop() - elif unknown_sign: - return None - elif not nonneg and not nonpos and neg: - return True - elif not nonneg and neg: - return True - elif not neg and not nonpos: - return False - - def _eval_subs(self, old, new): - if not old.is_Add: - return None - - coeff_self, terms_self = self.as_coeff_Add() - coeff_old, terms_old = old.as_coeff_Add() - - if coeff_self.is_Rational and coeff_old.is_Rational: - if terms_self == terms_old: # (2 + a).subs( 3 + a, y) -> -1 + y - return Add(new, coeff_self, -coeff_old) - if terms_self == -terms_old: # (2 + a).subs(-3 - a, y) -> -1 - y - return Add(-new, coeff_self, coeff_old) - - if coeff_self.is_Rational and coeff_old.is_Rational \ - or coeff_self == coeff_old: - args_old, args_self = Add.make_args( - terms_old), Add.make_args(terms_self) - if len(args_old) < len(args_self): # (a+b+c).subs(b+c,x) -> a+x - self_set = set(args_self) - old_set = set(args_old) - - if old_set < self_set: - ret_set = self_set - old_set - return Add(new, coeff_self, -coeff_old, - *[s._subs(old, new) for s in ret_set]) - - args_old = Add.make_args( - -terms_old) # (a+b+c+d).subs(-b-c,x) -> a-x+d - old_set = set(args_old) - if old_set < self_set: - ret_set = self_set - old_set - return Add(-new, coeff_self, coeff_old, - *[s._subs(old, new) for s in ret_set]) - - def removeO(self): - args = [a for a in self.args if not a.is_Order] - return self._new_rawargs(*args) - - def getO(self): - args = [a for a in self.args if a.is_Order] - if args: - return self._new_rawargs(*args) - - @cacheit -
    [docs] def extract_leading_order(self, *symbols): - """ - Returns the leading term and it's order. - - Examples - ======== - - >>> from sympy.abc import x - >>> (x + 1 + 1/x**5).extract_leading_order(x) - ((x**(-5), O(x**(-5))),) - >>> (1 + x).extract_leading_order(x) - ((1, O(1)),) - >>> (x + x**2).extract_leading_order(x) - ((x, O(x)),) - - """ - lst = [] - seq = [(f, C.Order(f, *symbols)) for f in self.args] - for ef, of in seq: - for e, o in lst: - if o.contains(of) and o != of: - of = None - break - if of is None: - continue - new_lst = [(ef, of)] - for e, o in lst: - if of.contains(o) and o != of: - continue - new_lst.append((e, o)) - lst = new_lst - return tuple(lst) -
    -
    [docs] def as_real_imag(self, deep=True, **hints): - """ - returns a tuple represeting a complex numbers - - Examples - ======== - - >>> from sympy import I - >>> (7 + 9*I).as_real_imag() - (7, 9) - """ - sargs, terms = self.args, [] - re_part, im_part = [], [] - for term in sargs: - re, im = term.as_real_imag(deep=deep) - re_part.append(re) - im_part.append(im) - return (self.func(*re_part), self.func(*im_part)) -
    - def _eval_as_leading_term(self, x): - from sympy import expand_mul, factor_terms - - old = self - - self = expand_mul(self) - if not self.is_Add: - return self.as_leading_term(x) - - unbounded = [t for t in self.args if t.is_unbounded] - if unbounded: - return Add._from_args(unbounded) - - self = Add(*[t.as_leading_term(x) for t in self.args]).removeO() - if not self: - # simple leading term analysis gave us 0 but we have to send - # back a term, so compute the leading term (via series) - return old.compute_leading_term(x) - elif not self.is_Add: - return self - else: - plain = Add(*[s for s, _ in self.extract_leading_order(x)]) - rv = factor_terms(plain, fraction=False) - rv_fraction = factor_terms(rv, fraction=True) - # if it simplifies to an x-free expression, return that; - # tests don't fail if we don't but it seems nicer to do this - if x not in rv_fraction.free_symbols: - return rv_fraction - return rv - - def _eval_adjoint(self): - return Add(*[t.adjoint() for t in self.args]) - - def _eval_conjugate(self): - return Add(*[t.conjugate() for t in self.args]) - - def _eval_transpose(self): - return Add(*[t.transpose() for t in self.args]) - - def __neg__(self): - return Add(*[-t for t in self.args]) - - def _sage_(self): - s = 0 - for x in self.args: - s += x._sage_() - return s - -
    [docs] def primitive(self): - """ - Return ``(R, self/R)`` where ``R``` is the Rational GCD of ``self```. - - ``R`` is collected only from the leading coefficient of each term. - - Examples - ======== - - >>> from sympy.abc import x, y - - >>> (2*x + 4*y).primitive() - (2, x + 2*y) - - >>> (2*x/3 + 4*y/9).primitive() - (2/9, 3*x + 2*y) - - >>> (2*x/3 + 4.2*y).primitive() - (1/3, 2*x + 12.6*y) - - No subprocessing of term factors is performed: - - >>> ((2 + 2*x)*x + 2).primitive() - (1, x*(2*x + 2) + 2) - - Recursive subprocessing can be done with the as_content_primitive() - method: - - >>> ((2 + 2*x)*x + 2).as_content_primitive() - (2, x*(x + 1) + 1) - - See also: primitive() function in polytools.py - - """ - - terms = [] - inf = False - for a in self.args: - c, m = a.as_coeff_Mul() - if not c.is_Rational: - c = S.One - m = a - inf = inf or m is S.ComplexInfinity - terms.append((c.p, c.q, m)) - - if not inf: - ngcd = reduce(igcd, [t[0] for t in terms], 0) - dlcm = reduce(ilcm, [t[1] for t in terms], 1) - else: - ngcd = reduce(igcd, [t[0] for t in terms if t[1]], 0) - dlcm = reduce(ilcm, [t[1] for t in terms if t[1]], 1) - - if ngcd == dlcm == 1: - return S.One, self - if not inf: - for i, (p, q, term) in enumerate(terms): - terms[i] = _keep_coeff(Rational((p//ngcd)*(dlcm//q)), term) - else: - for i, (p, q, term) in enumerate(terms): - if q: - terms[i] = _keep_coeff(Rational((p//ngcd)*(dlcm//q)), term) - else: - terms[i] = _keep_coeff(Rational(p, q), term) - - # we don't need a complete re-flattening since no new terms will join - # so we just use the same sort as is used in Add.flatten. When the - # coefficient changes, the ordering of terms may change, e.g. - # (3*x, 6*y) -> (2*y, x) - # - # We do need to make sure that term[0] stays in position 0, however. - # - if terms[0].is_Number or terms[0] is S.ComplexInfinity: - c = terms.pop(0) - else: - c = None - terms.sort(key=hash) - if c: - terms.insert(0, c) - return Rational(ngcd, dlcm), self._new_rawargs(*terms) -
    -
    [docs] def as_content_primitive(self, radical=False): - """Return the tuple (R, self/R) where R is the positive Rational - extracted from self. If radical is True (default is False) then - common radicals will be removed and included as a factor of the - primitive expression. - - Examples - ======== - - >>> from sympy import sqrt - >>> (3 + 3*sqrt(2)).as_content_primitive() - (3, 1 + sqrt(2)) - - Radical content can also be factored out of the primitive: - - >>> (2*sqrt(2) + 4*sqrt(10)).as_content_primitive(radical=True) - (2, sqrt(2)*(1 + 2*sqrt(5))) - - See docstring of Expr.as_content_primitive for more examples. - """ - con, prim = Add(*[_keep_coeff(*a.as_content_primitive( - radical=radical)) for a in self.args]).primitive() - if radical and prim.is_Add: - # look for common radicals that can be removed - args = prim.args - rads = [] - common_q = None - for m in args: - term_rads = defaultdict(list) - for ai in Mul.make_args(m): - if ai.is_Pow: - b, e = ai.as_base_exp() - if e.is_Rational and b.is_Integer and b > 0: - term_rads[e.q].append(int(b)**e.p) - if not term_rads: - break - if common_q is None: - common_q = set(term_rads.keys()) - else: - common_q = common_q & set(term_rads.keys()) - if not common_q: - break - rads.append(term_rads) - else: - # process rads - # keep only those in common_q - for r in rads: - for q in list(r.keys()): - if q not in common_q: - r.pop(q) - for q in r: - r[q] = prod(r[q]) - # find the gcd of bases for each q - G = [] - for q in common_q: - g = reduce(igcd, [r[q] for r in rads], 0) - if g != 1: - G.append(g**Rational(1, q)) - if G: - G = Mul(*G) - args = [ai/G for ai in args] - prim = G*Add(*args) - - return con, prim -
    - @property - def _sorted_args(self): - from sympy.core.compatibility import default_sort_key - return sorted(self.args, key=lambda w: default_sort_key(w)) -
    -from .mul import Mul, _keep_coeff, prod -from sympy.core.numbers import Rational -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/core/basic.html b/dev-py3k/_modules/sympy/core/basic.html deleted file mode 100644 index 71bc9836f87..00000000000 --- a/dev-py3k/_modules/sympy/core/basic.html +++ /dev/null @@ -1,1807 +0,0 @@ - - - - - - - - - - sympy.core.basic — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.core.basic

    -"""Base class for all the objects in SymPy"""
    -from copy import copy
    -from sympy.core.assumptions import ManagedProperties
    -from sympy.core.cache import cacheit
    -from sympy.core.core import BasicType, C
    -from sympy.core.sympify import _sympify, sympify, SympifyError
    -from sympy.core.compatibility import (callable, reduce, cmp, iterable,
    -    ordered)
    -from sympy.core.decorators import deprecated
    -from sympy.core.singleton import S
    -import collections
    -from functools import reduce
    -
    -
    -
    [docs]class Basic(object, metaclass=ManagedProperties): - """ - Base class for all objects in sympy. - - Conventions: - - 1) Always use ``.args``, when accessing parameters of some instance: - - >>> from sympy import symbols, cot - >>> from sympy.abc import x, y - - >>> cot(x).args - (x,) - - >>> cot(x).args[0] - x - - >>> (x*y).args - (x, y) - - >>> (x*y).args[1] - y - - - 2) Never use internal methods or variables (the ones prefixed with ``_``): - - >>> cot(x)._args # do not use this, use cot(x).args instead - (x,) - - """ - __slots__ = ['_mhash', # hash value - '_args', # arguments - '_assumptions' - ] - - # To be overridden with True in the appropriate subclasses - is_Atom = False - is_Symbol = False - is_Dummy = False - is_Wild = False - is_Function = False - is_Add = False - is_Mul = False - is_Pow = False - is_Number = False - is_Float = False - is_Rational = False - is_Integer = False - is_NumberSymbol = False - is_Order = False - is_Derivative = False - is_Piecewise = False - is_Poly = False - is_AlgebraicNumber = False - is_Relational = False - is_Equality = False - is_Boolean = False - is_Not = False - is_Matrix = False - - @property - @deprecated(useinstead="is_Float", issue=1721, deprecated_since_version="0.7.0") -
    [docs] def is_Real(self): # pragma: no cover - """Deprecated alias for ``is_Float``""" - # When this is removed, remove the piece of code disabling the warning - # from test_pickling.py - return self.is_Float -
    - def __new__(cls, *args): - obj = object.__new__(cls) - obj._assumptions = cls.default_assumptions - obj._mhash = None # will be set by __hash__ method. - - obj._args = args # all items in args must be Basic objects - return obj - - def copy(self): - return self.func(*self.args) - - def __reduce_ex__(self, proto): - """ Pickling support.""" - return type(self), self.__getnewargs__(), self.__getstate__() - - def __getnewargs__(self): - return self.args - - def __getstate__(self): - return {} - - def __setstate__(self, state): - for k, v in state.items(): - setattr(self, k, v) - - def __hash__(self): - # hash cannot be cached using cache_it because infinite recurrence - # occurs as hash is needed for setting cache dictionary keys - h = self._mhash - if h is None: - h = hash((type(self).__name__,) + self._hashable_content()) - self._mhash = h - return h - - def _hashable_content(self): - """Return a tuple of information about self that can be used to - compute the hash. If a class defines additional attributes, - like ``name`` in Symbol, then this method should be updated - accordingly to return such relevent attributes. - - Defining more than _hashable_content is necessary if __eq__ has - been defined by a class. See note about this in Basic.__eq__.""" - return self._args - - @property -
    [docs] def assumptions0(self): - """ - Return object `type` assumptions. - - For example: - - Symbol('x', real=True) - Symbol('x', integer=True) - - are different objects. In other words, besides Python type (Symbol in - this case), the initial assumptions are also forming their typeinfo. - - Examples - ======== - - >>> from sympy import Symbol - >>> from sympy.abc import x - >>> x.assumptions0 - {'commutative': True} - >>> x = Symbol("x", positive=True) - >>> x.assumptions0 - {'commutative': True, 'complex': True, 'hermitian': True, - 'imaginary': False, 'negative': False, 'nonnegative': True, - 'nonpositive': False, 'nonzero': True, 'positive': True, 'real': True, - 'zero': False} - - """ - return {} -
    -
    [docs] def compare(self, other): - """ - Return -1, 0, 1 if the object is smaller, equal, or greater than other. - - Not in the mathematical sense. If the object is of a different type - from the "other" then their classes are ordered according to - the sorted_classes list. - - Examples - ======== - - >>> from sympy.abc import x, y - >>> x.compare(y) - -1 - >>> x.compare(x) - 0 - >>> y.compare(x) - 1 - - """ - # all redefinitions of __cmp__ method should start with the - # following three lines: - if self is other: - return 0 - c = cmp(self.__class__, other.__class__) - if c: - return c - # - st = self._hashable_content() - ot = other._hashable_content() - c = cmp(len(st), len(ot)) - if c: - return c - for l, r in zip(st, ot): - if isinstance(l, Basic): - c = l.compare(r) - elif isinstance(l, frozenset): - c = 0 - else: - c = cmp(l, r) - if c: - return c - return 0 -
    - @staticmethod - def _compare_pretty(a, b): - from sympy.series.order import Order - if isinstance(a, Order) and not isinstance(b, Order): - return 1 - if not isinstance(a, Order) and isinstance(b, Order): - return -1 - - if a.is_Rational and b.is_Rational: - return cmp(a.p*b.q, b.p*a.q) - else: - from sympy.core.symbol import Wild - p1, p2, p3 = Wild("p1"), Wild("p2"), Wild("p3") - r_a = a.match(p1 * p2**p3) - if r_a and p3 in r_a: - a3 = r_a[p3] - r_b = b.match(p1 * p2**p3) - if r_b and p3 in r_b: - b3 = r_b[p3] - c = Basic.compare(a3, b3) - if c != 0: - return c - - return Basic.compare(a, b) - - @staticmethod - @deprecated(useinstead="default_sort_key", issue=1491, deprecated_since_version="0.7.2") -
    [docs] def compare_pretty(a, b): - """ - Is a > b in the sense of ordering in printing? - - THIS FUNCTION IS DEPRECATED. Use ``default_sort_key`` instead. - - :: - - yes ..... return 1 - no ...... return -1 - equal ... return 0 - - Strategy: - - It uses Basic.compare as a fallback, but improves it in many cases, - like ``x**3``, ``x**4``, ``O(x**3)`` etc. In those simple cases, it just parses the - expression and returns the "sane" ordering such as:: - - 1 < x < x**2 < x**3 < O(x**4) etc. - - Examples - ======== - - >>> from sympy.abc import x - >>> from sympy import Basic, Number - >>> Basic._compare_pretty(x, x**2) - -1 - >>> Basic._compare_pretty(x**2, x**2) - 0 - >>> Basic._compare_pretty(x**3, x**2) - 1 - >>> Basic._compare_pretty(Number(1, 2), Number(1, 3)) - 1 - >>> Basic._compare_pretty(Number(0), Number(-1)) - 1 - - """ - try: - a = _sympify(a) - except SympifyError: - pass - - try: - b = _sympify(b) - except SympifyError: - pass - - # both objects are non-SymPy - if (not isinstance(a, Basic)) and (not isinstance(b, Basic)): - return cmp(a, b) - - if not isinstance(a, Basic): - return -1 # other < sympy - - if not isinstance(b, Basic): - return +1 # sympy > other - - # now both objects are from SymPy, so we can proceed to usual comparison - return cmp(a.sort_key(), b.sort_key()) -
    - @classmethod -
    [docs] def fromiter(cls, args, **assumptions): - """ - Create a new object from an iterable. - - This is a convenience function that allows one to create objects from - any iterable, without having to convert to a list or tuple first. - - Examples - ======== - - >>> from sympy import Tuple - >>> Tuple.fromiter(i for i in range(5)) - (0, 1, 2, 3, 4) - - """ - return cls(*tuple(args), **assumptions) -
    - @classmethod -
    [docs] def class_key(cls): - """Nice order of classes. """ - return 5, 0, cls.__name__ -
    - @cacheit -
    [docs] def sort_key(self, order=None): - """ - Return a sort key. - - Examples - ======== - - >>> from sympy.core import Basic, S, I - >>> from sympy.abc import x - - >>> sorted([S(1)/2, I, -I], key=lambda x: x.sort_key()) - [1/2, -I, I] - - >>> S("[x, 1/x, 1/x**2, x**2, x**(1/2), x**(1/4), x**(3/2)]") - [x, 1/x, x**(-2), x**2, sqrt(x), x**(1/4), x**(3/2)] - >>> sorted(_, key=lambda x: x.sort_key()) - [x**(-2), 1/x, x**(1/4), sqrt(x), x, x**(3/2), x**2] - - """ - - # XXX: remove this when issue #2070 is fixed - def inner_key(arg): - if isinstance(arg, Basic): - return arg.sort_key(order) - else: - return arg - - args = self._sorted_args - args = len(args), tuple([ inner_key(arg) for arg in args ]) - return self.class_key(), args, S.One.sort_key(), S.One -
    - def __eq__(self, other): - """Return a boolean indicating whether a == b on the basis of - their symbolic trees. - - This is the same as a.compare(b) == 0 but faster. - - Notes - ===== - - If a class that overrides __eq__() needs to retain the - implementation of __hash__() from a parent class, the - interpreter must be told this explicitly by setting __hash__ = - <ParentClass>.__hash__. Otherwise the inheritance of __hash__() - will be blocked, just as if __hash__ had been explicitly set to - None. - - References - ========== - - from http://docs.python.org/dev/reference/datamodel.html#object.__hash__ - """ - - if type(self) is not type(other): - # issue 3001 a**1.0 == a like a**2.0 == a**2 - while isinstance(self, C.Pow) and self.exp == 1: - self = self.base - while isinstance(other, C.Pow) and other.exp == 1: - other = other.base - try: - other = _sympify(other) - except SympifyError: - return False # sympy != other - - if type(self) is not type(other): - return False - - return self._hashable_content() == other._hashable_content() - - def __ne__(self, other): - """a != b -> Compare two symbolic trees and see whether they are different - - this is the same as: - - a.compare(b) != 0 - - but faster - """ - - if type(self) is not type(other): - try: - other = _sympify(other) - except SympifyError: - return True # sympy != other - - if type(self) is not type(other): - return True - - return self._hashable_content() != other._hashable_content() - -
    [docs] def dummy_eq(self, other, symbol=None): - """ - Compare two expressions and handle dummy symbols. - - Examples - ======== - - >>> from sympy import Dummy - >>> from sympy.abc import x, y - - >>> u = Dummy('u') - - >>> (u**2 + 1).dummy_eq(x**2 + 1) - True - >>> (u**2 + 1) == (x**2 + 1) - False - - >>> (u**2 + y).dummy_eq(x**2 + y, x) - True - >>> (u**2 + y).dummy_eq(x**2 + y, y) - False - - """ - dummy_symbols = [ s for s in self.free_symbols if s.is_Dummy ] - - if not dummy_symbols: - return self == other - elif len(dummy_symbols) == 1: - dummy = dummy_symbols.pop() - else: - raise ValueError( - "only one dummy symbol allowed on the left-hand side") - - if symbol is None: - symbols = other.free_symbols - - if not symbols: - return self == other - elif len(symbols) == 1: - symbol = symbols.pop() - else: - raise ValueError("specify a symbol in which expressions should be compared") - - tmp = dummy.__class__() - - return self.subs(dummy, tmp) == other.subs(symbol, tmp) - - # Note, we always use the default ordering (lex) in __str__ and __repr__, - # regardless of the global setting. See issue 2388.
    - def __repr__(self): - from sympy.printing import sstr - return sstr(self, order=None) - - def __str__(self): - from sympy.printing import sstr - return sstr(self, order=None) - -
    [docs] def atoms(self, *types): - """Returns the atoms that form the current object. - - By default, only objects that are truly atomic and can't - be divided into smaller pieces are returned: symbols, numbers, - and number symbols like I and pi. It is possible to request - atoms of any type, however, as demonstrated below. - - Examples - ======== - - >>> from sympy import I, pi, sin - >>> from sympy.abc import x, y - >>> (1 + x + 2*sin(y + I*pi)).atoms() - set([1, 2, I, pi, x, y]) - - If one or more types are given, the results will contain only - those types of atoms. - - Examples - ======== - - >>> from sympy import Number, NumberSymbol, Symbol - >>> (1 + x + 2*sin(y + I*pi)).atoms(Symbol) - set([x, y]) - - >>> (1 + x + 2*sin(y + I*pi)).atoms(Number) - set([1, 2]) - - >>> (1 + x + 2*sin(y + I*pi)).atoms(Number, NumberSymbol) - set([1, 2, pi]) - - >>> (1 + x + 2*sin(y + I*pi)).atoms(Number, NumberSymbol, I) - set([1, 2, I, pi]) - - Note that I (imaginary unit) and zoo (complex infinity) are special - types of number symbols and are not part of the NumberSymbol class. - - The type can be given implicitly, too: - - >>> (1 + x + 2*sin(y + I*pi)).atoms(x) # x is a Symbol - set([x, y]) - - Be careful to check your assumptions when using the implicit option - since ``S(1).is_Integer = True`` but ``type(S(1))`` is ``One``, a special type - of sympy atom, while ``type(S(2))`` is type ``Integer`` and will find all - integers in an expression: - - >>> from sympy import S - >>> (1 + x + 2*sin(y + I*pi)).atoms(S(1)) - set([1]) - - >>> (1 + x + 2*sin(y + I*pi)).atoms(S(2)) - set([1, 2]) - - Finally, arguments to atoms() can select more than atomic atoms: any - sympy type (loaded in core/__init__.py) can be listed as an argument - and those types of "atoms" as found in scanning the arguments of the - expression recursively: - - >>> from sympy import Function, Mul - >>> from sympy.core.function import AppliedUndef - >>> f = Function('f') - >>> (1 + f(x) + 2*sin(y + I*pi)).atoms(Function) - set([f(x), sin(y + I*pi)]) - >>> (1 + f(x) + 2*sin(y + I*pi)).atoms(AppliedUndef) - set([f(x)]) - - >>> (1 + x + 2*sin(y + I*pi)).atoms(Mul) - set([I*pi, 2*sin(y + I*pi)]) - - """ - if types: - types = tuple( - [t if isinstance(t, type) else type(t) for t in types]) - else: - types = (Atom,) - result = set() - for expr in preorder_traversal(self): - if isinstance(expr, types): - result.add(expr) - return result -
    - @property -
    [docs] def free_symbols(self): - """Return from the atoms of self those which are free symbols. - - For most expressions, all symbols are free symbols. For some classes - this is not true. e.g. Integrals use Symbols for the dummy variables - which are bound variables, so Integral has a method to return all symbols - except those. Derivative keeps track of symbols with respect to which it - will perform a derivative; those are bound variables, too, so it has - its own symbols method. - - Any other method that uses bound variables should implement a symbols - method.""" - union = set.union - return reduce(union, [arg.free_symbols for arg in self.args], set()) -
    - def is_hypergeometric(self, k): - from sympy.simplify import hypersimp - return hypersimp(self, k) is not None - - @property -
    [docs] def is_number(self): - """Returns ``True`` if 'self' is a number. - - >>> from sympy import log, Integral - >>> from sympy.abc import x, y - - >>> x.is_number - False - >>> (2*x).is_number - False - >>> (2 + log(2)).is_number - True - >>> (2 + Integral(2, x)).is_number - False - >>> (2 + Integral(2, (x, 1, 2))).is_number - True - - """ - # should be overriden by subclasses - return False -
    - @property - def is_comparable(self): - is_real = self.is_real - if is_real is False: - return False - is_number = self.is_number - if is_number is False: - return False - if is_real and is_number: - return True - n, i = [p.evalf(2) for p in self.as_real_imag()] - if not i.is_Number or not n.is_Number: - return False - if i: - # if _prec = 1 we can't decide and if not, - # the answer is False so return False - return False - else: - return n._prec != 1 - - @property -
    [docs] def func(self): - """ - The top-level function in an expression. - - The following should hold for all objects:: - - >> x == x.func(*x.args) - - Examples - ======== - - >>> from sympy.abc import x - >>> a = 2*x - >>> a.func - <class 'sympy.core.mul.Mul'> - >>> a.args - (2, x) - >>> a.func(*a.args) - 2*x - >>> a == a.func(*a.args) - True - - """ - return self.__class__ -
    - @property -
    [docs] def args(self): - """Returns a tuple of arguments of 'self'. - - Examples - ======== - - >>> from sympy import symbols, cot - >>> from sympy.abc import x, y - - >>> cot(x).args - (x,) - - >>> cot(x).args[0] - x - - >>> (x*y).args - (x, y) - - >>> (x*y).args[1] - y - - Notes - ===== - - Never use self._args, always use self.args. - Only use _args in __new__ when creating a new function. - Don't override .args() from Basic (so that it's easy to - change the interface in the future if needed). - """ - return self._args -
    - @property - def _sorted_args(self): - """ - The same as ``args``. Derived classes which don't fix an - order on their arguments should override this method to - produce the sorted representation. - """ - return self.args - -
    [docs] def iter_basic_args(self): - """ - Iterates arguments of ``self``. - - Examples - ======== - - >>> from sympy.abc import x - >>> a = 2*x - >>> a.iter_basic_args() - <...iterator object at 0x...> - >>> list(a.iter_basic_args()) - [2, x] - - """ - return iter(self.args) -
    -
    [docs] def as_poly(self, *gens, **args): - """Converts ``self`` to a polynomial or returns ``None``. - - >>> from sympy import Poly, sin - >>> from sympy.abc import x, y - - >>> print((x**2 + x*y).as_poly()) - Poly(x**2 + x*y, x, y, domain='ZZ') - - >>> print((x**2 + x*y).as_poly(x, y)) - Poly(x**2 + x*y, x, y, domain='ZZ') - - >>> print((x**2 + sin(y)).as_poly(x, y)) - None - - """ - from sympy.polys import Poly, PolynomialError - - try: - poly = Poly(self, *gens, **args) - - if not poly.is_Poly: - return None - else: - return poly - except PolynomialError: - return None -
    -
    [docs] def as_content_primitive(self, radical=False): - """A stub to allow Basic args (like Tuple) to be skipped when computing - the content and primitive components of an expression. - - See docstring of Expr.as_content_primitive - """ - return S.One, self -
    -
    [docs] def subs(self, *args, **kwargs): - """ - Substitutes old for new in an expression after sympifying args. - - `args` is either: - - two arguments, e.g. foo.subs(old, new) - - one iterable argument, e.g. foo.subs(iterable). The iterable may be - o an iterable container with (old, new) pairs. In this case the - replacements are processed in the order given with successive - patterns possibly affecting replacements already made. - o a dict or set whose key/value items correspond to old/new pairs. - In this case the old/new pairs will be sorted by op count and in - case of a tie, by number of args and the default_sort_key. The - resulting sorted list is then processed as an iterable container - (see previous). - - If the keyword ``simultaneous`` is True, the subexpressions will not be - evaluated until all the substitutions have been made. - - Examples - ======== - - >>> from sympy import pi, exp - >>> from sympy.abc import x, y - >>> (1 + x*y).subs(x, pi) - pi*y + 1 - >>> (1 + x*y).subs({x:pi, y:2}) - 1 + 2*pi - >>> (1 + x*y).subs([(x, pi), (y, 2)]) - 1 + 2*pi - >>> reps = [(y, x**2), (x, 2)] - >>> (x + y).subs(reps) - 6 - >>> (x + y).subs(reversed(reps)) - x**2 + 2 - - >>> (x**2 + x**4).subs(x**2, y) - y**2 + y - - To replace only the x**2 but not the x**4, use xreplace: - - >>> (x**2 + x**4).xreplace({x**2: y}) - x**4 + y - - To delay evaluation until all substitutions have been made, - set the keyword ``simultaneous`` to True: - - >>> (x/y).subs([(x, 0), (y, 0)]) - 0 - >>> (x/y).subs([(x, 0), (y, 0)], simultaneous=True) - nan - - This has the added feature of not allowing subsequent substitutions - to affect those already made: - - >>> ((x + y)/y).subs({x + y: y, y: x + y}) - 1 - >>> ((x + y)/y).subs({x + y: y, y: x + y}, simultaneous=True) - y/(x + y) - - In order to obtain a canonical result, unordered iterables are - sorted by count_op length, number of arguments and by the - default_sort_key to break any ties. All other iterables are left - unsorted. - - >>> from sympy import sqrt, sin, cos, exp - >>> from sympy.abc import a, b, c, d, e - - >>> A = (sqrt(sin(2*x)), a) - >>> B = (sin(2*x), b) - >>> C = (cos(2*x), c) - >>> D = (x, d) - >>> E = (exp(x), e) - - >>> expr = sqrt(sin(2*x))*sin(exp(x)*x)*cos(2*x) + sin(2*x) - - >>> expr.subs(dict([A,B,C,D,E])) - a*c*sin(d*e) + b - - See Also - ======== - replace: replacement capable of doing wildcard-like matching, - parsing of match, and conditional replacements - xreplace: exact node replacement in expr tree; also capable of - using matching rules - - """ - from sympy.core.containers import Dict - from sympy.utilities import default_sort_key - - unordered = False - if len(args) == 1: - sequence = args[0] - if isinstance(sequence, set): - unordered = True - elif isinstance(sequence, (Dict, dict)): - unordered = True - sequence = list(sequence.items()) - elif not iterable(sequence): - from sympy.utilities.misc import filldedent - raise ValueError(filldedent(""" - When a single argument is passed to subs - it should be a dictionary of old: new pairs or an iterable - of (old, new) tuples.""")) - elif len(args) == 2: - sequence = [args] - else: - raise ValueError("subs accepts either 1 or 2 arguments") - - sequence = list(sequence) - for i in range(len(sequence)): - o, n = sequence[i] - so, sn = sympify(o), sympify(n) - if not isinstance(so, Basic): - if type(o) is str: - so = C.Symbol(o) - sequence[i] = (so, sn) - if _aresame(so, sn): - sequence[i] = None - continue - sequence = [_f for _f in sequence if _f] - - if unordered: - sequence = dict(sequence) - if not all(k.is_Atom for k in sequence): - d = {} - for o, n in sequence.items(): - try: - ops = o.count_ops(), len(o.args) - except TypeError: - ops = (0, 0) - d.setdefault(ops, []).append((o, n)) - newseq = [] - for k in sorted(list(d.keys()), reverse=True): - newseq.extend( - sorted([v[0] for v in d[k]], key=default_sort_key)) - sequence = [(k, sequence[k]) for k in newseq] - del newseq, d - else: - sequence = sorted([(k, v) for (k, v) in sequence.items()], - key=default_sort_key) - - if kwargs.pop('simultaneous', False): # XXX should this be the default for dict subs? - reps = {} - rv = self - for old, new in sequence: - d = C.Dummy() - rv = rv._subs(old, d) - reps[d] = new - if not isinstance(rv, Basic): - break - return rv.xreplace(reps) - else: - rv = self - for old, new in sequence: - rv = rv._subs(old, new) - if not isinstance(rv, Basic): - break - return rv -
    - @cacheit - def _subs(self, old, new, **hints): - """Substitutes an expression old -> new. - - If self is not equal to old then _eval_subs is called. - If _eval_subs doesn't want to make any special replacement - then a None is received which indicates that the fallback - should be applied wherein a search for replacements is made - amongst the arguments of self. - - >>> from sympy import Basic, Add, Mul - >>> from sympy.abc import x, y, z - - Examples - ======== - - Add's _eval_subs knows how to target x + y in the following - so it makes the change: - - >>> (x + y + z).subs(x + y, 1) - z + 1 - - Add's _eval_subs doesn't need to know how to find x + y in - the following: - - >>> Add._eval_subs(z*(x + y) + 3, x + y, 1) is None - True - - The returned None will cause the fallback routine to traverse the args and - pass the z*(x + y) arg to Mul where the change will take place and the - substitution will succeed: - - >>> (z*(x + y) + 3).subs(x + y, 1) - z + 3 - - ** Developers Notes ** - - An _eval_subs routine for a class should be written if: - - 1) any arguments are not instances of Basic (e.g. bool, tuple); - - 2) some arguments should not be targeted (as in integration - variables); - - 3) if there is something other than a literal replacement - that should be attempted (as in Piecewise where the condition - may be updated without doing a replacement). - - If it is overridden, here are some special cases that might arise: - - 1) If it turns out that no special change was made and all - the original sub-arguments should be checked for - replacements then None should be returned. - - 2) If it is necessary to do substitutions on a portion of - the expression then _subs should be called. _subs will - handle the case of any sub-expression being equal to old - (which usually would not be the case) while its fallback - will handle the recursion into the sub-arguments. For - example, after Add's _eval_subs removes some matching terms - it must process the remaining terms so it calls _subs - on each of the un-matched terms and then adds them - onto the terms previously obtained. - - 3) If the initial expression should remain unchanged then - the original expression should be returned. (Whenever an - expression is returned, modified or not, no further - substitution of old -> new is attempted.) Sum's _eval_subs - routine uses this strategy when a substitution is attempted - on any of its summation variables. - """ - - def fallback(self, old, new): - """ - Try to replace old with new in any of self's arguments. - """ - hit = False - args = list(self.args) - for i, arg in enumerate(args): - if not hasattr(arg, '_eval_subs'): - continue - arg = arg._subs(old, new, **hints) - if arg is not args[i]: - hit = True - args[i] = arg - if hit: - return self.func(*args) - return self - - if _aresame(self, old): - return new - - rv = self._eval_subs(old, new) - if rv is None: - rv = fallback(self, old, new) - return rv - - def _eval_subs(self, old, new): - """Override this stub if you want to do anything more than - attempt a replacement of old with new in the arguments of self. - - See also: _subs - """ - return None - -
    [docs] def xreplace(self, rule): - """ - Replace occurrences of objects within the expression. - - Parameters - ========== - rule : dict-like - Expresses a replacement rule - - Returns - ======= - xreplace : the result of the replacement - - Examples - ======== - >>> from sympy import symbols, pi, exp - >>> x, y, z = symbols('x y z') - >>> (1 + x*y).xreplace({x: pi}) - pi*y + 1 - >>> (1 + x*y).xreplace({x:pi, y:2}) - 1 + 2*pi - - Replacements occur only if an entire node in the expression tree is - matched: - - >>> (x*y + z).xreplace({x*y: pi}) - z + pi - >>> (x*y*z).xreplace({x*y: pi}) - x*y*z - >>> (2*x).xreplace({2*x: y, x: z}) - y - >>> (2*2*x).xreplace({2*x: y, x: z}) - 4*z - >>> (x + y + 2).xreplace({x + y: 2}) - x + y + 2 - >>> (x + 2 + exp(x + 2)).xreplace({x + 2: y}) - x + exp(y) + 2 - - xreplace doesn't differentiate between free and bound symbols. In the - following, subs(x, y) would not change x since it is a bound symbol, - but xreplace does: - - >>> from sympy import Integral - >>> Integral(x, (x, 1, 2*x)).xreplace({x: y}) - Integral(y, (y, 1, 2*y)) - - Trying to replace x with an expression raises an error: - - >>> Integral(x, (x, 1, 2*x)).xreplace({x: 2*y}) #doctest: +SKIP - ValueError: Invalid limits given: ((2*y, 1, 4*y),) - - See Also - ======== - replace: replacement capable of doing wildcard-like matching, - parsing of match, and conditional replacements - subs: substitution of subexpressions as defined by the objects - themselves. - - """ - if self in rule: - return rule[self] - elif rule: - args = tuple([arg.xreplace(rule) for arg in self.args]) - if not _aresame(args, self.args): - return self.func(*args) - return self -
    - @deprecated(useinstead="has", issue=2389, deprecated_since_version="0.7.2") - def __contains__(self, obj): - if self == obj: - return True - for arg in self.args: - try: - if obj in arg: - return True - except TypeError: - if obj == arg: - return True - return False - - @cacheit -
    [docs] def has(self, *patterns): - """ - Test whether any subexpression matches any of the patterns. - - Examples - ======== - - >>> from sympy import sin, S - >>> from sympy.abc import x, y, z - >>> (x**2 + sin(x*y)).has(z) - False - >>> (x**2 + sin(x*y)).has(x, y, z) - True - >>> x.has(x) - True - - Note that ``expr.has(*patterns)`` is exactly equivalent to - ``any(expr.has(p) for p in patterns)``. In particular, ``False`` is - returned when the list of patterns is empty. - - >>> x.has() - False - - """ - return any(self._has(pattern) for pattern in patterns) -
    - def _has(self, pattern): - """Helper for .has()""" - from sympy.core.function import UndefinedFunction, Function - if isinstance(pattern, UndefinedFunction): - return any(f.func == pattern or f == pattern - for f in self.atoms(Function, UndefinedFunction)) - - pattern = sympify(pattern) - if isinstance(pattern, BasicType): - return any(isinstance(arg, pattern) - for arg in preorder_traversal(self)) - - try: - match = pattern._has_matcher() - return any(match(arg) for arg in preorder_traversal(self)) - except AttributeError: - return any(arg == pattern for arg in preorder_traversal(self)) - - def _has_matcher(self): - """Helper for .has()""" - return self.__eq__ - -
    [docs] def replace(self, query, value, map=False): - """ - Replace matching subexpressions of ``self`` with ``value``. - - If ``map = True`` then also return the mapping {old: new} where ``old`` - was a sub-expression found with query and ``new`` is the replacement - value for it. - - Traverses an expression tree and performs replacement of matching - subexpressions from the bottom to the top of the tree. The list of - possible combinations of queries and replacement values is listed - below: - - Examples - ======== - - Initial setup - - >>> from sympy import log, sin, cos, tan, Wild - >>> from sympy.abc import x, y - >>> f = log(sin(x)) + tan(sin(x**2)) - - 1.1. type -> type - obj.replace(sin, tan) - - >>> f.replace(sin, cos) - log(cos(x)) + tan(cos(x**2)) - >>> sin(x).replace(sin, cos, map=True) - (cos(x), {sin(x): cos(x)}) - - 1.2. type -> func - obj.replace(sin, lambda arg: ...) - - >>> f.replace(sin, lambda arg: sin(2*arg)) - log(sin(2*x)) + tan(sin(2*x**2)) - - 2.1. expr -> expr - obj.replace(sin(a), tan(a)) - - >>> a = Wild('a') - >>> f.replace(sin(a), tan(a)) - log(tan(x)) + tan(tan(x**2)) - - 2.2. expr -> func - obj.replace(sin(a), lambda a: ...) - - >>> f.replace(sin(a), cos(a)) - log(cos(x)) + tan(cos(x**2)) - >>> f.replace(sin(a), lambda a: sin(2*a)) - log(sin(2*x)) + tan(sin(2*x**2)) - - 3.1. func -> func - obj.replace(lambda expr: ..., lambda expr: ...) - - >>> g = 2*sin(x**3) - >>> g.replace(lambda expr: expr.is_Number, lambda expr: expr**2) - 4*sin(x**9) - - See Also - ======== - subs: substitution of subexpressions as defined by the objects - themselves. - xreplace: exact node replacement in expr tree; also capable of - using matching rules - - """ - if isinstance(query, type): - _query = lambda expr: isinstance(expr, query) - - if isinstance(value, type): - _value = lambda expr, result: value(*expr.args) - elif isinstance(value, collections.Callable): - _value = lambda expr, result: value(*expr.args) - else: - raise TypeError( - "given a type, replace() expects another " - "type or a callable") - elif isinstance(query, Basic): - _query = lambda expr: expr.match(query) - - if isinstance(value, Basic): - _value = lambda expr, result: value.subs(result) - elif isinstance(value, collections.Callable): - _value = lambda expr, result: value(**dict([ ( - str(key)[:-1], val) for key, val in result.items() ])) - else: - raise TypeError( - "given an expression, replace() expects " - "another expression or a callable") - elif isinstance(query, collections.Callable): - _query = query - - if isinstance(value, collections.Callable): - _value = lambda expr, result: value(expr) - else: - raise TypeError( - "given a callable, replace() expects " - "another callable") - else: - raise TypeError( - "first argument to replace() must be a " - "type, an expression or a callable") - - mapping = {} - - def rec_replace(expr): - args, construct = [], False - - for arg in expr.args: - result = rec_replace(arg) - - if result is not None: - construct = True - else: - result = arg - - args.append(result) - - if construct: - return expr.__class__(*args) - else: - result = _query(expr) - - if result: - value = _value(expr, result) - - if map: - mapping[expr] = value - - return value - else: - return None - - result = rec_replace(self) - - if result is None: - result = self - - if not map: - return result - else: - return result, mapping -
    -
    [docs] def find(self, query, group=False): - """Find all subexpressions matching a query. """ - query = _make_find_query(query) - results = list(filter(query, preorder_traversal(self))) - - if not group: - return set(results) - else: - groups = {} - - for result in results: - if result in groups: - groups[result] += 1 - else: - groups[result] = 1 - - return groups -
    -
    [docs] def count(self, query): - """Count the number of matching subexpressions. """ - query = _make_find_query(query) - return sum(bool(query(sub)) for sub in preorder_traversal(self)) -
    -
    [docs] def matches(self, expr, repl_dict={}, old=False): - """ - Helper method for match() that looks for a match between wild symbols - in self and expressions in expr. - - Examples - ======== - - >>> from sympy import symbols, Wild, Integer, Basic - >>> a, b, c = symbols('a b c') - >>> x = Wild('x') - >>> Basic(a + x, x).matches(Basic(a + b, c)) is None - True - >>> Basic(a + x, x).matches(Basic(a + b + c, b + c)) - {x_: b + c} - """ - expr = sympify(expr) - if not isinstance(expr, self.__class__): - return None - - if self == expr: - return repl_dict - - if len(self.args) != len(expr.args): - return None - - d = repl_dict.copy() - for arg, other_arg in zip(self.args, expr.args): - if arg == other_arg: - continue - d = arg.xreplace(d).matches(other_arg, d, old=old) - if d is None: - return None - return d -
    -
    [docs] def match(self, pattern, old=False): - """ - Pattern matching. - - Wild symbols match all. - - Return ``None`` when expression (self) does not match - with pattern. Otherwise return a dictionary such that:: - - pattern.xreplace(self.match(pattern)) == self - - Examples - ======== - - >>> from sympy import symbols, Wild - >>> from sympy.abc import x, y - >>> p = Wild("p") - >>> q = Wild("q") - >>> r = Wild("r") - >>> e = (x+y)**(x+y) - >>> e.match(p**p) - {p_: x + y} - >>> e.match(p**q) - {p_: x + y, q_: x + y} - >>> e = (2*x)**2 - >>> e.match(p*q**r) - {p_: 4, q_: x, r_: 2} - >>> (p*q**r).xreplace(e.match(p*q**r)) - 4*x**2 - - The ``old`` flag will give the old-style pattern matching where - expressions and patterns are essentially solved to give the - match. Both of the following give None unless ``old=True``: - - >>> (x - 2).match(p - x, old=True) - {p_: 2*x - 2} - >>> (2/x).match(p*x, old=True) - {p_: 2/x**2} - - """ - from sympy import signsimp, count_ops - pattern = sympify(pattern) - s = signsimp(self) - p = signsimp(pattern) - # if we still have the same relationship between the types of - # input, then use the sign simplified forms - if (pattern.func == self.func) and (s.func == p.func): - rv = p.matches(s, old=old) - else: - rv = pattern.matches(self, old=old) - return rv -
    -
    [docs] def count_ops(self, visual=None): - """wrapper for count_ops that returns the operation count.""" - from sympy import count_ops - return count_ops(self, visual) -
    -
    [docs] def doit(self, **hints): - """Evaluate objects that are not evaluated by default like limits, - integrals, sums and products. All objects of this kind will be - evaluated recursively, unless some species were excluded via 'hints' - or unless the 'deep' hint was set to 'False'. - - >>> from sympy import Integral - >>> from sympy.abc import x, y - - >>> 2*Integral(x, x) - 2*Integral(x, x) - - >>> (2*Integral(x, x)).doit() - x**2 - - >>> (2*Integral(x, x)).doit(deep = False) - 2*Integral(x, x) - - """ - if hints.get('deep', True): - terms = [ term.doit(**hints) for term in self.args ] - return self.func(*terms) - else: - return self -
    - def _eval_rewrite(self, pattern, rule, **hints): - if self.is_Atom: - return self - sargs = self.args - terms = [ t._eval_rewrite(pattern, rule, **hints) for t in sargs ] - return self.func(*terms) - -
    [docs] def rewrite(self, *args, **hints): - """ Rewrite functions in terms of other functions. - - Rewrites expression containing applications of functions - of one kind in terms of functions of different kind. For - example you can rewrite trigonometric functions as complex - exponentials or combinatorial functions as gamma function. - - As a pattern this function accepts a list of functions to - to rewrite (instances of DefinedFunction class). As rule - you can use string or a destination function instance (in - this case rewrite() will use the str() function). - - There is also possibility to pass hints on how to rewrite - the given expressions. For now there is only one such hint - defined called 'deep'. When 'deep' is set to False it will - forbid functions to rewrite their contents. - - >>> from sympy import sin, exp, I - >>> from sympy.abc import x, y - - Unspecified pattern: - >>> sin(x).rewrite(exp) - -I*(exp(I*x) - exp(-I*x))/2 - - Pattern as a single function: - >>> sin(x).rewrite(sin, exp) - -I*(exp(I*x) - exp(-I*x))/2 - - Pattern as a list of functions: - >>> sin(x).rewrite([sin, ], exp) - -I*(exp(I*x) - exp(-I*x))/2 - - """ - if self.is_Atom or not args: - return self - else: - pattern = args[:-1] - if isinstance(args[-1], str): - rule = '_eval_rewrite_as_' + args[-1] - else: - rule = '_eval_rewrite_as_' + args[-1].__name__ - - if not pattern: - return self._eval_rewrite(None, rule, **hints) - else: - if iterable(pattern[0]): - pattern = pattern[0] - - pattern = [ p.__class__ for p in pattern if self.has(p) ] - - if pattern: - return self._eval_rewrite(tuple(pattern), rule, **hints) - else: - return self - -
    -
    [docs]class Atom(Basic): - """ - A parent class for atomic things. An atom is an expression with no subexpressions. - - Examples - ======== - - Symbol, Number, Rational, Integer, ... - But not: Add, Mul, Pow, ... - """ - - is_Atom = True - - __slots__ = [] - - def matches(self, expr, repl_dict={}, old=False): - if self == expr: - return repl_dict - - def xreplace(self, rule): - return rule.get(self, self) - - def doit(self, **hints): - return self - - @classmethod - def class_key(cls): - return 2, 0, cls.__name__ - - @cacheit - def sort_key(self, order=None): - from sympy.core import S - return self.class_key(), (1, (str(self),)), S.One.sort_key(), S.One - - def _eval_simplify(self, ratio, measure): - return self - - @property - def _sorted_args(self): - # this is here as a safeguard against accidentally using _sorted_args - # on Atoms -- they cannot be rebuilt as atom.func(*atom._sorted_args) - # since there are no args. So the calling routine should be checking - # to see that this property is not called for Atoms. - raise AttributeError('Atoms have no args. It might be necessary' - ' to make a check for Atoms in the calling code.') - -
    -def _aresame(a, b): - """Return True if a and b are structurally the same, else False. - - Examples - ======== - - To SymPy, 2.0 == 2: - - >>> from sympy import S, Symbol, cos, sin - >>> 2.0 == S(2) - True - - Since a simple 'same or not' result is sometimes useful, this routine was - written to provide that query: - - >>> from sympy.core.basic import _aresame - >>> _aresame(S(2.0), S(2)) - False - - """ - - - for i, j in zip(preorder_traversal(a), preorder_traversal(b)): - if i != j or type(i) != type(j): - return False - else: - return True - - -def _atomic(e): - """Return atom-like quantities as far as substitution is - concerned: Derivatives, Functions and Symbols. Don't - return any 'atoms' that are inside such quantities unless - they also appear outside, too. - - Examples - ======== - >>> from sympy import Derivative, Function, cos - >>> from sympy.abc import x, y - >>> from sympy.core.basic import _atomic - >>> f = Function('f') - >>> _atomic(x + y) - set([x, y]) - >>> _atomic(x + f(y)) - set([x, f(y)]) - >>> _atomic(Derivative(f(x), x) + cos(x) + y) - set([y, cos(x), Derivative(f(x), x)]) - - """ - from sympy import Derivative, Function, Symbol - pot = preorder_traversal(e) - seen = set() - try: - free = e.free_symbols - except AttributeError: - return set([e]) - atoms = set() - for p in pot: - if p in seen: - pot.skip() - continue - seen.add(p) - if isinstance(p, Symbol) and p in free: - atoms.add(p) - elif isinstance(p, (Derivative, Function)): - pot.skip() - atoms.add(p) - return atoms - - -class preorder_traversal(object): - """ - Do a pre-order traversal of a tree. - - This iterator recursively yields nodes that it has visited in a pre-order - fashion. That is, it yields the current node then descends through the - tree breadth-first to yield all of a node's children's pre-order - traversal. - - - For an expression, the order of the traversal depends on the order of - .args, which in many cases can be arbitrary. - - Parameters - ========== - node : sympy expression - The expression to traverse. - keys : (default None) sort key(s) - The key(s) used to sort args of Basic objects. When None, args of Basic - objects are processed in arbitrary order. If key is defined, it will - be passed along to ordered() as the only key(s) to use to sort the - arguments; if ``key`` is simply True then the default keys of ordered - will be used. - - Yields - ====== - subtree : sympy expression - All of the subtrees in the tree. - - Examples - ======== - - >>> from sympy import symbols - >>> from sympy import symbols, default_sort_key - >>> from sympy.core.basic import preorder_traversal - >>> x, y, z = symbols('x y z') - - The nodes are returned in the order that they are encountered unless key - is given; simply passing key=True will guarantee that the traversal is - unique. - - >>> list(preorder_traversal((x + y)*z, keys=None)) # doctest: +SKIP - [z*(x + y), z, x + y, y, x] - >>> list(preorder_traversal((x + y)*z, keys=True)) - [z*(x + y), z, x + y, x, y] - - """ - def __init__(self, node, keys=None): - self._skip_flag = False - self._pt = self._preorder_traversal(node, keys) - - def _preorder_traversal(self, node, keys): - yield node - if self._skip_flag: - self._skip_flag = False - return - if isinstance(node, Basic): - args = node.args - if keys: - if keys != True: - args = ordered(args, keys, default=False) - else: - args = ordered(args) - for arg in args: - for subtree in self._preorder_traversal(arg, keys): - yield subtree - elif iterable(node): - for item in node: - for subtree in self._preorder_traversal(item, keys): - yield subtree - - def skip(self): - """ - Skip yielding current node's (last yielded node's) subtrees. - - Examples - -------- - >>> from sympy.core import symbols - >>> from sympy.core.basic import preorder_traversal - >>> x, y, z = symbols('x y z') - >>> pt = preorder_traversal((x+y*z)*z) - >>> for i in pt: - ... print(i) - ... if i == x+y*z: - ... pt.skip() - z*(x + y*z) - z - x + y*z - """ - self._skip_flag = True - - def __next__(self): - return next(self._pt) - - def __iter__(self): - return self - - -def _make_find_query(query): - """Convert the argument of Basic.find() into a callable""" - try: - query = sympify(query) - except SympifyError: - pass - if isinstance(query, type): - return lambda expr: isinstance(expr, query) - elif isinstance(query, Basic): - return lambda expr: expr.match(query) is not None - return query -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/core/cache.html b/dev-py3k/_modules/sympy/core/cache.html deleted file mode 100644 index 6b599f23cf1..00000000000 --- a/dev-py3k/_modules/sympy/core/cache.html +++ /dev/null @@ -1,257 +0,0 @@ - - - - - - - - - - sympy.core.cache — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.core.cache

    -""" Caching facility for SymPy """
    -
    -# TODO: refactor CACHE & friends into class?
    -
    -# global cache registry:
    -CACHE = []  # [] of
    -            #    (item, {} or tuple of {})
    -
    -from sympy.core.decorators import wraps
    -
    -
    -def print_cache():
    -    """print cache content"""
    -
    -    for item, cache in CACHE:
    -        item = str(item)
    -        head = '='*len(item)
    -
    -        print(head)
    -        print(item)
    -        print(head)
    -
    -        if not isinstance(cache, tuple):
    -            cache = (cache,)
    -            shown = False
    -        else:
    -            shown = True
    -
    -        for i, kv in enumerate(cache):
    -            if shown:
    -                print('\n*** %i ***\n' % i)
    -
    -            for k, v in kv.items():
    -                print('  %s :\t%s' % (k, v))
    -
    -
    -def clear_cache():
    -    """clear cache content"""
    -    for item, cache in CACHE:
    -        if not isinstance(cache, tuple):
    -            cache = (cache,)
    -
    -        for kv in cache:
    -            kv.clear()
    -
    -########################################
    -
    -
    -def __cacheit_nocache(func):
    -    return func
    -
    -
    -def __cacheit(func):
    -    """caching decorator.
    -
    -       important: the result of cached function must be *immutable*
    -
    -
    -       Examples
    -       ========
    -
    -       >>> from sympy.core.cache import cacheit
    -       >>> @cacheit
    -       ... def f(a,b):
    -       ...    return a+b
    -
    -       >>> @cacheit
    -       ... def f(a,b):
    -       ...    return [a,b] # <-- WRONG, returns mutable object
    -
    -       to force cacheit to check returned results mutability and consistency,
    -       set environment variable SYMPY_USE_CACHE to 'debug'
    -    """
    -
    -    func._cache_it_cache = func_cache_it_cache = {}
    -    CACHE.append((func, func_cache_it_cache))
    -
    -    @wraps(func)
    -    def wrapper(*args, **kw_args):
    -        """
    -        Assemble the args and kw_args to compute the hash.
    -        """
    -        k = [(x, type(x)) for x in args]
    -        if kw_args:
    -            keys = sorted(kw_args)
    -            k.extend([(x, kw_args[x], type(kw_args[x])) for x in keys])
    -        k = tuple(k)
    -        try:
    -            return func_cache_it_cache[k]
    -        except KeyError:
    -            pass
    -        func_cache_it_cache[k] = r = func(*args, **kw_args)
    -        return r
    -    return wrapper
    -
    -
    -def __cacheit_debug(func):
    -    """cacheit + code to check cache consistency"""
    -    cfunc = __cacheit(func)
    -
    -    @wraps(func)
    -    def wrapper(*args, **kw_args):
    -        # always call function itself and compare it with cached version
    -        r1 = func(*args, **kw_args)
    -        r2 = cfunc(*args, **kw_args)
    -
    -        # try to see if the result is immutable
    -        #
    -        # this works because:
    -        #
    -        # hash([1,2,3])         -> raise TypeError
    -        # hash({'a':1, 'b':2})  -> raise TypeError
    -        # hash((1,[2,3]))       -> raise TypeError
    -        #
    -        # hash((1,2,3))         -> just computes the hash
    -        hash(r1), hash(r2)
    -
    -        # also see if returned values are the same
    -        assert r1 == r2
    -
    -        return r1
    -    return wrapper
    -
    -
    -def _getenv(key, default=None):
    -    from os import getenv
    -    return getenv(key, default)
    -
    -# SYMPY_USE_CACHE=yes/no/debug
    -USE_CACHE = _getenv('SYMPY_USE_CACHE', 'yes').lower()
    -
    -if USE_CACHE == 'no':
    -    cacheit = __cacheit_nocache
    -elif USE_CACHE == 'yes':
    -    cacheit = __cacheit
    -elif USE_CACHE == 'debug':
    -    cacheit = __cacheit_debug   # a lot slower
    -else:
    -    raise RuntimeError(
    -        'unrecognized value for SYMPY_USE_CACHE: %s' % USE_CACHE)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/core/compatibility.html b/dev-py3k/_modules/sympy/core/compatibility.html deleted file mode 100644 index a0a856f1fe1..00000000000 --- a/dev-py3k/_modules/sympy/core/compatibility.html +++ /dev/null @@ -1,863 +0,0 @@ - - - - - - - - - - sympy.core.compatibility — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.core.compatibility

    -"""
    -Reimplementations of constructs introduced in later versions of Python than
    -we support. Also some functions that are needed SymPy-wide and are located
    -here for easy import.
    -"""
    -
    -from collections import defaultdict
    -
    -# These are in here because telling if something is an iterable just by calling
    -# hasattr(obj, "__iter__") behaves differently in Python 2 and Python 3.  In
    -# particular, hasattr(str, "__iter__") is False in Python 2 and True in Python 3.
    -# I think putting them here also makes it easier to use them in the core.
    -
    -
    -
    [docs]def iterable(i, exclude=(str, dict)): - """ - Return a boolean indicating whether ``i`` is SymPy iterable. - - When SymPy is working with iterables, it is almost always assuming - that the iterable is not a string or a mapping, so those are excluded - by default. If you want a pure Python definition, make exclude=None. To - exclude multiple items, pass them as a tuple. - - See also: is_sequence - - Examples - ======== - - >>> from sympy.utilities.iterables import iterable - >>> from sympy import Tuple - >>> things = [[1], (1,), set([1]), Tuple(1), (j for j in [1, 2]), {1:2}, '1', 1] - >>> for i in things: - ... print(iterable(i), type(i)) - True <... 'list'> - True <... 'tuple'> - True <... 'set'> - True <class 'sympy.core.containers.Tuple'> - True <... 'generator'> - False <... 'dict'> - False <... 'str'> - False <... 'int'> - - >>> iterable({}, exclude=None) - True - >>> iterable({}, exclude=str) - True - >>> iterable("no", exclude=str) - False - - """ - try: - iter(i) - except TypeError: - return False - if exclude: - return not isinstance(i, exclude) - return True - -
    -
    [docs]def is_sequence(i, include=None): - """ - Return a boolean indicating whether ``i`` is a sequence in the SymPy - sense. If anything that fails the test below should be included as - being a sequence for your application, set 'include' to that object's - type; multiple types should be passed as a tuple of types. - - Note: although generators can generate a sequence, they often need special - handling to make sure their elements are captured before the generator is - exhausted, so these are not included by default in the definition of a - sequence. - - See also: iterable - - Examples - ======== - - >>> from sympy.utilities.iterables import is_sequence - >>> from types import GeneratorType - >>> is_sequence([]) - True - >>> is_sequence(set()) - False - >>> is_sequence('abc') - False - >>> is_sequence('abc', include=str) - True - >>> generator = (c for c in 'abc') - >>> is_sequence(generator) - False - >>> is_sequence(generator, include=(str, GeneratorType)) - True - - """ - return (hasattr(i, '__getitem__') and - iterable(i) or - bool(include) and - isinstance(i, include)) -
    -""" -Wrapping some imports in try/except statements to allow the same code to -be used in Python 3+ as well. -""" - -try: - callable = callable -except NameError: - import collections - - def callable(obj): - return isinstance(obj, collections.Callable) - -try: - from functools import reduce -except ImportError: - reduce = reduce - - -def cmp_to_key(mycmp): - """ - Convert a cmp= function into a key= function - - This code is included in Python 2.7 and 3.2 in functools. - """ - class K(object): - def __init__(self, obj, *args): - self.obj = obj - - def __lt__(self, other): - return mycmp(self.obj, other.obj) < 0 - - def __gt__(self, other): - return mycmp(self.obj, other.obj) > 0 - - def __eq__(self, other): - return mycmp(self.obj, other.obj) == 0 - - def __le__(self, other): - return mycmp(self.obj, other.obj) <= 0 - - def __ge__(self, other): - return mycmp(self.obj, other.obj) >= 0 - - def __ne__(self, other): - return mycmp(self.obj, other.obj) != 0 - return K - -try: - import builtins - cmp = builtins.cmp -except AttributeError: - def cmp(a, b): - return (a > b) - (a < b) - -try: - from itertools import product -except ImportError: # Python 2.5 - def product(*args, **kwargs): - """ - Cartesian product of input iterables. - - Equivalent to nested for-loops in a generator expression. For example, - cartes(A, B) returns the same as ((x,y) for x in A for y in B). - - The nested loops cycle like an odometer with the rightmost element - advancing on every iteration. This pattern creates a lexicographic - ordering so that if the input's iterables are sorted, the product - tuples are emitted in sorted order. - - To compute the product of an iterable with itself, specify the number - of repetitions with the optional repeat keyword argument. For example, - product(A, repeat=4) means the same as product(A, A, A, A). - - Examples - ======== - - >>> from sympy.utilities.iterables import cartes - >>> [''.join(p) for p in list(cartes('ABC', 'xy'))] - ['Ax', 'Ay', 'Bx', 'By', 'Cx', 'Cy'] - >>> list(cartes(list(range(2)), repeat=2)) - [(0, 0), (0, 1), (1, 0), (1, 1)] - - See Also - ======== - variations - """ - pools = list(map(tuple, args)) * kwargs.get('repeat', 1) - result = [[]] - for pool in pools: - result = [x + [y] for x in result for y in pool] - for prod in result: - yield tuple(prod) - -try: - from itertools import permutations -except ImportError: # Python 2.5 - def permutations(iterable, r=None): - """ - Return successive r length permutations of elements in the iterable. - - If r is not specified or is None, then r defaults to the length of - the iterable and all possible full-length permutations are generated. - - Permutations are emitted in lexicographic sort order. So, if the input - iterable is sorted, the permutation tuples will be produced in sorted - order. - - Elements are treated as unique based on their position, not on their - value. So if the input elements are unique, there will be no repeat - values in each permutation. - - Examples; - >>> from sympy.core.compatibility import permutations - >>> [''.join(p) for p in list(permutations('ABC', 2))] - ['AB', 'AC', 'BA', 'BC', 'CA', 'CB'] - >>> list(permutations(list(range(3)))) - [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)] - """ - - pool = tuple(iterable) - n = len(pool) - r = n if r is None else r - if r > n: - return - indices = list(range(n)) - cycles = list(range(n, n - r, -1)) - yield tuple(pool[i] for i in indices[:r]) - while n: - for i in reversed(list(range(r))): - cycles[i] -= 1 - if cycles[i] == 0: - indices[i:] = indices[i + 1:] + indices[i:i + 1] - cycles[i] = n - i - else: - j = cycles[i] - indices[i], indices[-j] = indices[-j], indices[i] - yield tuple(pool[i] for i in indices[:r]) - break - else: - return - -try: - from itertools import combinations, combinations_with_replacement -except ImportError: # < python 2.6 - def combinations(iterable, r): - """ - Return r length subsequences of elements from the input iterable. - - Combinations are emitted in lexicographic sort order. So, if the - input iterable is sorted, the combination tuples will be produced - in sorted order. - - Elements are treated as unique based on their position, not on their - value. So if the input elements are unique, there will be no repeat - values in each combination. - - See also: combinations_with_replacement - - Examples - ======== - - >>> from sympy.core.compatibility import combinations - >>> list(combinations('ABC', 2)) - [('A', 'B'), ('A', 'C'), ('B', 'C')] - >>> list(combinations(list(range(4)), 3)) - [(0, 1, 2), (0, 1, 3), (0, 2, 3), (1, 2, 3)] - """ - pool = tuple(iterable) - n = len(pool) - if r > n: - return - indices = list(range(r)) - yield tuple(pool[i] for i in indices) - while True: - for i in reversed(list(range(r))): - if indices[i] != i + n - r: - break - else: - return - indices[i] += 1 - for j in range(i + 1, r): - indices[j] = indices[j - 1] + 1 - yield tuple(pool[i] for i in indices) - - def combinations_with_replacement(iterable, r): - """Return r length subsequences of elements from the input iterable - allowing individual elements to be repeated more than once. - - Combinations are emitted in lexicographic sort order. So, if the - input iterable is sorted, the combination tuples will be produced - in sorted order. - - Elements are treated as unique based on their position, not on their - value. So if the input elements are unique, the generated combinations - will also be unique. - - See also: combinations - - Examples - ======== - - >>> from sympy.core.compatibility import combinations_with_replacement - >>> list(combinations_with_replacement('AB', 2)) - [('A', 'A'), ('A', 'B'), ('B', 'B')] - """ - pool = tuple(iterable) - n = len(pool) - if not n and r: - return - indices = [0] * r - yield tuple(pool[i] for i in indices) - while True: - for i in reversed(list(range(r))): - if indices[i] != n - 1: - break - else: - return - indices[i:] = [indices[i] + 1] * (r - i) - yield tuple(pool[i] for i in indices) - - -
    [docs]def set_intersection(*sets): - """Return the intersection of all the given sets. - - As of Python 2.6 you can write ``set.intersection(*sets)``. - - Examples - ======== - - >>> from sympy.core.compatibility import set_intersection - >>> set_intersection(set([1, 2]), set([2, 3])) - set([2]) - >>> set_intersection() - set() - """ - if not sets: - return set() - rv = sets[0] - for s in sets: - rv &= s - return rv - -
    -
    [docs]def set_union(*sets): - """Return the union of all the given sets. - - As of Python 2.6 you can write ``set.union(*sets)``. - - >>> from sympy.core.compatibility import set_union - >>> set_union(set([1, 2]), set([2, 3])) - set([1, 2, 3]) - >>> set_union() - set() - """ - rv = set() - for s in sets: - rv |= s - return rv -
    -try: - bin = bin -except NameError: # Python 2.5 - def bin(x): - """ - bin(number) -> string - - Stringifies an int or long in base 2. - """ - if x < 0: - return '-' + bin(-x) - out = [] - if x == 0: - out.append('0') - while x > 0: - out.append('01'[x & 1]) - x >>= 1 - pass - return '0b' + ''.join(reversed(out)) - -try: - next = next -except NameError: # Python 2.5 - def next(*args): - """ - next(iterator[, default]) - - Return the next item from the iterator. If default is given and the - iterator is exhausted, it is returned instead of raising StopIteration. - """ - if len(args) == 1: - return args[0].__next__() - elif len(args) == 2: - try: - return args[0].__next__() - except StopIteration: - return args[1] - else: - raise TypeError('Expected 1 or 2 arguments, got %s' % len(args)) - -try: - from builtins import bin -except ImportError: # Python 2.5 - _hexDict = { - '0': '0000', '1': '0001', '2': '0010', '3': '0011', '4': '0100', '5': '0101', - '6': '0110', '7': '0111', '8': '1000', '9': '1001', 'a': '1010', 'b': '1011', - 'c': '1100', 'd': '1101', 'e': '1110', 'f': '1111', 'L': ''} - - def bin(n): - """Return the equivalent to Python 2.6's bin function. - - Examples - ======== - - >>> from sympy.core.compatibility import bin - >>> bin(-123) - '-0b1111011' - >>> bin(0) # this is the only time a 0 will be to the right of 'b' - '0b0' - - See Also - ======== - sympy.physics.quantum.shor.arr - - Modified from http://code.activestate.com/recipes/576847/ - """ - # ========================================================= - # create hex of int, remove '0x'. now for each hex char, - # look up binary string, append in list and join at the end. - # ========================================================= - if n < 0: - return '-%s' % bin(-n) - return '0b%s' % (''.join([_hexDict[hstr] for hstr in hex(n)[2:].lower() - ]).lstrip('0') or '0') - - -
    [docs]def as_int(n): - """ - Convert the argument to a builtin integer. - - The return value is guaranteed to be equal to the input. ValueError is - raised if the input has a non-integral value. - - Examples - ======== - - >>> from sympy.core.compatibility import as_int - >>> from sympy import sqrt - >>> 3.0 - 3.0 - >>> as_int(3.0) # convert to int and test for equality - 3 - >>> int(sqrt(10)) - 3 - >>> as_int(sqrt(10)) - Traceback (most recent call last): - ... - ValueError: ... is not an integer - - """ - try: - result = int(n) - if result != n: - raise TypeError - except TypeError: - raise ValueError('%s is not an integer' % n) - return result - -
    -def default_sort_key(item, order=None): - """Return a key that can be used for sorting. - - The key has the structure: - - (class_key, (len(args), args), exponent.sort_key(), coefficient) - - This key is supplied by the sort_key routine of Basic objects when - ``item`` is a Basic object or an object (other than a string) that - sympifies to a Basic object. Otherwise, this function produces the - key. - - The ``order`` argument is passed along to the sort_key routine and is - used to determine how the terms *within* an expression are ordered. - (See examples below) ``order`` options are: 'lex', 'grlex', 'grevlex', - and reversed values of the same (e.g. 'rev-lex'). The default order - value is None (which translates to 'lex'). - - Examples - ======== - - >>> from sympy import Basic, S, I, default_sort_key - >>> from sympy.core.function import UndefinedFunction - >>> from sympy.abc import x - - The following are eqivalent ways of getting the key for an object: - - >>> x.sort_key() == default_sort_key(x) - True - - Here are some examples of the key that is produced: - - >>> default_sort_key(UndefinedFunction('f')) - ((0, 0, 'UndefinedFunction'), (1, ('f',)), ((1, 0, 'Number'), - (0, ()), (), 1), 1) - >>> default_sort_key('1') - ((0, 0, 'str'), (1, ('1',)), ((1, 0, 'Number'), (0, ()), (), 1), 1) - >>> default_sort_key(S.One) - ((1, 0, 'Number'), (0, ()), (), 1) - >>> default_sort_key(2) - ((1, 0, 'Number'), (0, ()), (), 2) - - - While sort_key is a method only defined for SymPy objects, - default_sort_key will accept anything as an argument so it is - more robust as a sorting key. For the following, using key= - lambda i: i.sort_key() would fail because 2 doesn't have a sort_key - method; that's why default_sort_key is used. Note, that it also - handles sympification of non-string items likes ints: - - >>> a = [2, I, -I] - >>> sorted(a, key=default_sort_key) - [2, -I, I] - - The returned key can be used anywhere that a key can be specified for - a function, e.g. sort, min, max, etc...: - - >>> a.sort(key=default_sort_key); a[0] - 2 - >>> min(a, key=default_sort_key) - 2 - - Note - ---- - - The key returned is useful for getting items into a canonical order - that will be the same across platforms. It is not directly useful for - sorting lists of expressions: - - >>> a, b = x, 1/x - - Since ``a`` has only 1 term, its value of sort_key is unaffected by - ``order``: - - >>> a.sort_key() == a.sort_key('rev-lex') - True - - If ``a`` and ``b`` are combined then the key will differ because there - are terms that can be ordered: - - >>> eq = a + b - >>> eq.sort_key() == eq.sort_key('rev-lex') - False - >>> eq.as_ordered_terms() - [x, 1/x] - >>> eq.as_ordered_terms('rev-lex') - [1/x, x] - - But since the keys for each of these terms are independent of ``order``'s - value, they don't sort differently when they appear separately in a list: - - >>> sorted(eq.args, key=default_sort_key) - [1/x, x] - >>> sorted(eq.args, key=lambda i: default_sort_key(i, order='rev-lex')) - [1/x, x] - - The order of terms obtained when using these keys is the order that would - be obtained if those terms were *factors* in a product. - - See Also - ======== - - sympy.core.expr.as_ordered_factors, sympy.core.expr.as_ordered_terms - - """ - - from sympy.core import S, Basic - from sympy.core.sympify import sympify, SympifyError - from sympy.core.compatibility import iterable - - if isinstance(item, Basic): - return item.sort_key(order=order) - - if iterable(item, exclude=str): - if isinstance(item, dict): - args = list(item.items()) - unordered = True - elif isinstance(item, set): - args = item - unordered = True - else: - # e.g. tuple, list - args = list(item) - unordered = False - - args = [default_sort_key(arg, order=order) for arg in args] - - if unordered: - # e.g. dict, set - args = sorted(args) - - cls_index, args = 10, (len(args), tuple(args)) - else: - if not isinstance(item, str): - try: - item = sympify(item) - except SympifyError: - # e.g. lambda x: x - pass - else: - if isinstance(item, Basic): - # e.g int -> Integer - return default_sort_key(item) - # e.g. UndefinedFunction - - # e.g. str - cls_index, args = 0, (1, (str(item),)) - - return (cls_index, 0, item.__class__.__name__ - ), args, S.One.sort_key(), S.One - - -def _nodes(e): - """ - A helper for ordered() which returns the node count of ``e`` which - for Basic object is the number of Basic nodes in the expression tree - but for other object is 1 (unless the object is an iterable or dict - for which the sum of nodes is returned). - """ - from .basic import Basic - - if isinstance(e, Basic): - return e.count(Basic) - elif iterable(e): - return 1 + sum(_nodes(ei) for ei in e) - elif isinstance(e, dict): - return 1 + sum(_nodes(k) + _nodes(v) for k, v in e.items()) - else: - return 1 - - -def ordered(seq, keys=None, default=True, warn=False): - """Return an iterator of the seq where keys are used to break ties. - Two default keys will be applied after and provided unless ``default`` - is False. The two keys are _nodes and default_sort_key which will - place smaller expressions before larger ones (in terms of Basic nodes) - and where there are ties, they will be broken by the default_sort_key. - - If ``warn`` is True then an error will be raised if there were no - keys remaining to break ties. This can be used if it was expected that - there should be no ties. - - Examples - ======== - - >>> from sympy.utilities.iterables import ordered, default_sort_key - >>> from sympy import count_ops - >>> from sympy.abc import x, y - - The count_ops is not sufficient to break ties in this list and the first - two items appear in their original order (i.e. the sorting is stable): - - >>> list(ordered([y + 2, x + 2, x**2 + y + 3], - ... count_ops, default=False, warn=False)) - ... - [y + 2, x + 2, x**2 + y + 3] - - The default_sort_key allows the tie to be broken: - - >>> list(ordered([y + 2, x + 2, x**2 + y + 3])) - ... - [x + 2, y + 2, x**2 + y + 3] - - Here, sequences are sorted by length, then sum: - - >>> seq, keys = [[[1, 2, 1], [0, 3, 1], [1, 1, 3], [2], [1]], [ - ... lambda x: len(x), - ... lambda x: sum(x)]] - ... - >>> list(ordered(seq, keys, default=False, warn=False)) - [[1], [2], [1, 2, 1], [0, 3, 1], [1, 1, 3]] - - If ``warn`` is True, an error will be raised if there were not - enough keys to break ties: - - >>> list(ordered(seq, keys, default=False, warn=True)) - Traceback (most recent call last): - ... - ValueError: not enough keys to break ties - - - Notes - ===== - - The decorated sort is one of the fastest ways to sort a sequence for - which special item comparison is desired: the sequence is decorated, - sorted on the basis of the decoration (e.g. making all letters lower - case) and then undecorated. If one wants to break ties for items that - have the same decorated value, a second key can be used. But if the - second key is expensive to compute then it is inefficient to decorate - all items with both keys: only those items having identical first key - values need to be decorated. This function applies keys successively - only when needed to break ties. By yielding an iterator, use of the - tie-breaker is delayed as long as possible. - - This function is best used in cases when use of the first key is - expected to be a good hashing function; if there are no unique hashes - from application of a key then that key should not have been used. The - exception, however, is that even if there are many collisions, if the - first group is small and one does not need to process all items in the - list then time will not be wasted sorting what one was not interested - in. For example, if one were looking for the minimum in a list and - there were several criteria used to define the sort order, then this - function would be good at returning that quickly if the first group - of candidates is small relative to the number of items being processed. - - """ - d = defaultdict(list) - if keys: - if not isinstance(keys, (list, tuple)): - keys = [keys] - keys = list(keys) - - f = keys.pop(0) - for a in seq: - d[f(a)].append(a) - else: - if not default: - raise ValueError('if default=False then keys must be provided') - d[None].extend(seq) - - for k in sorted(d.keys()): - if len(d[k]) > 1: - if keys: - d[k] = ordered(d[k], keys, default, warn) - elif default: - d[k] = ordered(d[k], (_nodes, default_sort_key,), - default=False, warn=warn) - elif warn: - raise ValueError('not enough keys to break ties') - for v in d[k]: - yield v - d.pop(k) - -try: - next = next -except NameError: - def next(x): - return x.__next__() -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/core/containers.html b/dev-py3k/_modules/sympy/core/containers.html deleted file mode 100644 index 76dd502a342..00000000000 --- a/dev-py3k/_modules/sympy/core/containers.html +++ /dev/null @@ -1,332 +0,0 @@ - - - - - - - - - - sympy.core.containers — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.core.containers

    -"""Module for SymPy containers
    -
    -    (SymPy objects that store other SymPy objects)
    -
    -    The containers implemented in this module are subclassed to Basic.
    -    They are supposed to work seamlessly within the SymPy framework.
    -"""
    -
    -from sympy.core.basic import Basic
    -from sympy.core.sympify import sympify, converter
    -from sympy.utilities.iterables import iterable
    -
    -
    -
    [docs]class Tuple(Basic): - """ - Wrapper around the builtin tuple object - - The Tuple is a subclass of Basic, so that it works well in the - SymPy framework. The wrapped tuple is available as self.args, but - you can also access elements or slices with [:] syntax. - - >>> from sympy import symbols - >>> from sympy.core.containers import Tuple - >>> a, b, c, d = symbols('a b c d') - >>> Tuple(a, b, c)[1:] - (b, c) - >>> Tuple(a, b, c).subs(a, d) - (d, b, c) - - """ - - def __new__(cls, *args, **assumptions): - args = [ sympify(arg) for arg in args ] - obj = Basic.__new__(cls, *args, **assumptions) - return obj - - def __getitem__(self, i): - if isinstance(i, slice): - indices = i.indices(len(self)) - return Tuple(*[self.args[i] for i in range(*indices)]) - return self.args[i] - - def __len__(self): - return len(self.args) - - def __contains__(self, item): - return item in self.args - - def __iter__(self): - return iter(self.args) - - def __add__(self, other): - if isinstance(other, Tuple): - return Tuple(*(self.args + other.args)) - elif isinstance(other, tuple): - return Tuple(*(self.args + other)) - else: - return NotImplemented - - def __radd__(self, other): - if isinstance(other, Tuple): - return Tuple(*(other.args + self.args)) - elif isinstance(other, tuple): - return Tuple(*(other + self.args)) - else: - return NotImplemented - - def __eq__(self, other): - if isinstance(other, Basic): - return super(Tuple, self).__eq__(other) - return self.args == other - - def __ne__(self, other): - if isinstance(other, Basic): - return super(Tuple, self).__ne__(other) - return self.args != other - - def __hash__(self): - return hash(self.args) - - def _to_mpmath(self, prec): - return tuple([a._to_mpmath(prec) for a in self.args]) - - def __lt__(self, other): - return self.args < other.args - - def __le__(self, other): - return self.args <= other.args -
    -converter[tuple] = lambda tup: Tuple(*tup) - - -def tuple_wrapper(method): - """ - Decorator that converts any tuple in the function arguments into a Tuple. - - The motivation for this is to provide simple user interfaces. The user can - call a function with regular tuples in the argument, and the wrapper will - convert them to Tuples before handing them to the function. - - >>> from sympy.core.containers import tuple_wrapper, Tuple - >>> def f(*args): - ... return args - >>> g = tuple_wrapper(f) - - The decorated function g sees only the Tuple argument: - - >>> g(0, (1, 2), 3) - (0, (1, 2), 3) - - """ - def wrap_tuples(*args, **kw_args): - newargs = [] - for arg in args: - if type(arg) is tuple: - newargs.append(Tuple(*arg)) - else: - newargs.append(arg) - return method(*newargs, **kw_args) - return wrap_tuples - - -
    [docs]class Dict(Basic): - """ - Wrapper around the builtin dict object - - The Dict is a subclass of Basic, so that it works well in the - SymPy framework. Because it is immutable, it may be included - in sets, but its values must all be given at instantiation and - cannot be changed afterwards. Otherwise it behaves identically - to the Python dict. - - >>> from sympy import S - >>> from sympy.core.containers import Dict - - >>> D = Dict({1: 'one', 2: 'two'}) - >>> for key in D: - ... if key == 1: - ... print(key, D[key]) - 1 one - - The args are sympified so the 1 and 2 are Integers and the values - are Symbols. Queries automatically sympify args so the following work: - - >>> 1 in D - True - >>> D.has('one') # searches keys and values - True - >>> 'one' in D # not in the keys - False - >>> D[1] - one - - """ - - def __new__(cls, *args): - if len(args) == 1 and ((args[0].__class__ is dict) or - (args[0].__class__ is Dict)): - items = [Tuple(k, v) for k, v in list(args[0].items())] - elif iterable(args) and all(len(arg) == 2 for arg in args): - items = [Tuple(k, v) for k, v in args] - else: - raise TypeError('Pass Dict args as Dict((k1, v1), ...) or Dict({k1: v1, ...})') - elements = frozenset(items) - obj = Basic.__new__(cls, elements) - obj.elements = elements - obj._dict = dict(items) # In case Tuple decides it wants to sympify - return obj - - def __getitem__(self, key): - """x.__getitem__(y) <==> x[y]""" - return self._dict[sympify(key)] - - def __setitem__(self, key, value): - raise NotImplementedError("SymPy Dicts are Immutable") - - @property - def args(self): - return tuple(self.elements) - -
    [docs] def items(self): - '''D.items() -> list of D's (key, value) pairs, as 2-tuples''' - return list(self._dict.items()) -
    -
    [docs] def keys(self): - '''D.keys() -> list of D's keys''' - return list(self._dict.keys()) -
    -
    [docs] def values(self): - '''D.values() -> list of D's values''' - return list(self._dict.values()) -
    - def __iter__(self): - '''x.__iter__() <==> iter(x)''' - return iter(self._dict) - - def __len__(self): - '''x.__len__() <==> len(x)''' - return self._dict.__len__() - -
    [docs] def get(self, key, default=None): - '''D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.''' - return self._dict.get(sympify(key), default) -
    - def __contains__(self, key): - '''D.__contains__(k) -> True if D has a key k, else False''' - return sympify(key) in self._dict - - def __lt__(self, other): - return self.args < other.args - - @property - def _sorted_args(self): - from sympy.utilities import default_sort_key - return sorted(self.args, key=default_sort_key)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/core/evalf.html b/dev-py3k/_modules/sympy/core/evalf.html deleted file mode 100644 index 49088a8c960..00000000000 --- a/dev-py3k/_modules/sympy/core/evalf.html +++ /dev/null @@ -1,1456 +0,0 @@ - - - - - - - - - - sympy.core.evalf — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.core.evalf

    -"""
    -Adaptive numerical evaluation of SymPy expressions, using mpmath
    -for mathematical functions.
    -"""
    -import sympy.mpmath.libmp as libmp
    -from sympy.mpmath import make_mpc, make_mpf, mp, mpc, mpf, nsum, quadts, quadosc
    -from sympy.mpmath import inf as mpmath_inf
    -from sympy.mpmath.libmp import (bitcount, from_int, from_man_exp,
    -        from_rational, fhalf, fnan, fnone, fone, fzero, mpf_abs, mpf_add,
    -        mpf_atan, mpf_atan2, mpf_cmp, mpf_cos, mpf_e, mpf_exp, mpf_log, mpf_lt,
    -        mpf_mul, mpf_neg, mpf_pi, mpf_pow, mpf_pow_int, mpf_shift, mpf_sin,
    -        mpf_sqrt, normalize, round_nearest, to_int, to_str)
    -from sympy.mpmath.libmp.backend import MPZ
    -from sympy.mpmath.libmp.libmpc import _infs_nan
    -from sympy.mpmath.libmp.libmpf import dps_to_prec, prec_to_dps
    -
    -from sympy.mpmath.libmp.gammazeta import mpf_bernoulli
    -
    -import math
    -
    -from .sympify import sympify
    -from .core import C
    -from .singleton import S
    -from .containers import Tuple
    -
    -LG10 = math.log(10, 2)
    -rnd = round_nearest
    -
    -# Used in a few places as placeholder values to denote exponents and
    -# precision levels, e.g. of exact numbers. Must be careful to avoid
    -# passing these to mpmath functions or returning them in final results.
    -INF = float(mpmath_inf)
    -MINUS_INF = float(-mpmath_inf)
    -
    -# ~= 100 digits. Real men set this to INF.
    -DEFAULT_MAXPREC = 333
    -
    -
    -
    [docs]class PrecisionExhausted(ArithmeticError): - pass - -#----------------------------------------------------------------------------# -# # -# Helper functions for arithmetic and complex parts # -# # -#----------------------------------------------------------------------------# -
    -""" -An mpf value tuple is a tuple of integers (sign, man, exp, bc) -representing a floating-point number: [1, -1][sign]*man*2**exp where -sign is 0 or 1 and bc should correspond to the number of bits used to -represent the mantissa (man) in binary notation, e.g. - ->>> from sympy.core.evalf import bitcount ->>> sign, man, exp, bc = 0, 5, 1, 3 ->>> n = [1, -1][sign]*man*2**exp ->>> n, bitcount(man) -(10, 3) - -A temporary result is a tuple (re, im, re_acc, im_acc) where -re and im are nonzero mpf value tuples representing approximate -numbers, or None to denote exact zeros. - -re_acc, im_acc are integers denoting log2(e) where e is the estimated -relative accuracy of the respective complex part, but may be anything -if the corresponding complex part is None. - -""" - - -def fastlog(x): - """Fast approximation of log2(x) for an mpf value tuple x. - - Notes: Calculated as exponent + width of mantissa. This is an - approximation for two reasons: 1) it gives the ceil(log2(abs(x))) - value and 2) it is too high by 1 in the case that x is an exact - power of 2. Although this is easy to remedy by testing to see if - the odd mpf mantissa is 1 (indicating that one was dealing with - an exact power of 2) that would decrease the speed and is not - necessary as this is only being used as an approximation for the - number of bits in x. The correct return value could be written as - "x[2] + (x[3] if x[1] != 1 else 0)". - Since mpf tuples always have an odd mantissa, no check is done - to see if the mantissa is a multiple of 2 (in which case the - result would be too large by 1). - - Examples - ======== - - >>> from sympy import log - >>> from sympy.core.evalf import fastlog, bitcount - >>> s, m, e = 0, 5, 1 - >>> bc = bitcount(m) - >>> n = [1, -1][s]*m*2**e - >>> n, (log(n)/log(2)).evalf(2), fastlog((s, m, e, bc)) - (10, 3.3, 4) - """ - - if not x or x == fzero: - return MINUS_INF - return x[2] + x[3] - - -def pure_complex(v): - """Return a and b if v matches a + I*b where b is not zero and - a and b are Numbers, else None. - - >>> from sympy.core.evalf import pure_complex - >>> from sympy import Tuple, I - >>> a, b = Tuple(2, 3) - >>> pure_complex(a) - >>> pure_complex(a + b*I) - (2, 3) - >>> pure_complex(I) - (0, 1) - """ - h, t = v.as_coeff_Add() - c, i = t.as_coeff_Mul() - if i is S.ImaginaryUnit: - return h, c - - -def scaled_zero(mag, sign=1): - """Return an mpf representing a power of two with magnitude ``mag`` - and -1 for precision. Or, if ``mag`` is a scaled_zero tuple, then just - remove the sign from within the list that it was initially wrapped - in. - - Examples - ======== - - >>> from sympy.core.evalf import scaled_zero - >>> from sympy import Float - >>> z, p = scaled_zero(100) - >>> z, p - (([0], 1, 100, 1), -1) - >>> ok = scaled_zero(z) - >>> ok - (0, 1, 100, 1) - >>> Float(ok) - 1.26765060022823e+30 - >>> Float(ok, p) - 0.e+30 - >>> ok, p = scaled_zero(100, -1) - >>> Float(scaled_zero(ok), p) - -0.e+30 - """ - if type(mag) is tuple and len(mag) == 4 and iszero(mag, scaled=True): - return (mag[0][0],) + mag[1:] - elif type(mag) is int: - if sign not in [-1, 1]: - raise ValueError('sign must be +/-1') - rv, p = mpf_shift(fone, mag), -1 - s = 0 if sign == 1 else 1 - rv = ([s],) + rv[1:] - return rv, p - else: - raise ValueError('scaled zero expects int or scaled_zero tuple.') - - -def iszero(mpf, scaled=False): - if not scaled: - return not mpf or not mpf[1] and not mpf[-1] - return mpf and type(mpf[0]) is list and mpf[1] == mpf[-1] == 1 - - -def complex_accuracy(result): - """ - Returns relative accuracy of a complex number with given accuracies - for the real and imaginary parts. The relative accuracy is defined - in the complex norm sense as ||z|+|error|| / |z| where error - is equal to (real absolute error) + (imag absolute error)*i. - - The full expression for the (logarithmic) error can be approximated - easily by using the max norm to approximate the complex norm. - - In the worst case (re and im equal), this is wrong by a factor - sqrt(2), or by log2(sqrt(2)) = 0.5 bit. - """ - re, im, re_acc, im_acc = result - if not im: - if not re: - return INF - return re_acc - if not re: - return im_acc - re_size = fastlog(re) - im_size = fastlog(im) - absolute_error = max(re_size - re_acc, im_size - im_acc) - relative_error = absolute_error - max(re_size, im_size) - return -relative_error - - -def get_abs(expr, prec, options): - re, im, re_acc, im_acc = evalf(expr, prec + 2, options) - if not re: - re, re_acc, im, im_acc = im, im_acc, re, re_acc - if im: - return libmp.mpc_abs((re, im), prec), None, re_acc, None - elif re: - return mpf_abs(re), None, re_acc, None - else: - return None, None, None, None - - -def get_complex_part(expr, no, prec, options): - """no = 0 for real part, no = 1 for imaginary part""" - workprec = prec - i = 0 - while 1: - res = evalf(expr, workprec, options) - value, accuracy = res[no::2] - # XXX is the last one correct? Consider re((1+I)**2).n() - if (not value) or accuracy >= prec or -value[2] > prec: - return value, None, accuracy, None - workprec += max(30, 2**i) - i += 1 - - -def evalf_abs(expr, prec, options): - return get_abs(expr.args[0], prec, options) - - -def evalf_re(expr, prec, options): - return get_complex_part(expr.args[0], 0, prec, options) - - -def evalf_im(expr, prec, options): - return get_complex_part(expr.args[0], 1, prec, options) - - -def finalize_complex(re, im, prec): - if re == fzero and im == fzero: - raise ValueError("got complex zero with unknown accuracy") - elif re == fzero: - return None, im, None, prec - elif im == fzero: - return re, None, prec, None - - size_re = fastlog(re) - size_im = fastlog(im) - if size_re > size_im: - re_acc = prec - im_acc = prec + min(-(size_re - size_im), 0) - else: - im_acc = prec - re_acc = prec + min(-(size_im - size_re), 0) - return re, im, re_acc, im_acc - - -def chop_parts(value, prec): - """ - Chop off tiny real or complex parts. - """ - re, im, re_acc, im_acc = value - # Method 1: chop based on absolute value - if re and re not in _infs_nan and (fastlog(re) < -prec + 4): - re, re_acc = None, None - if im and im not in _infs_nan and (fastlog(im) < -prec + 4): - im, im_acc = None, None - # Method 2: chop if inaccurate and relatively small - if re and im: - delta = fastlog(re) - fastlog(im) - if re_acc < 2 and (delta - re_acc <= -prec + 4): - re, re_acc = None, None - if im_acc < 2 and (delta - im_acc >= prec - 4): - im, im_acc = None, None - return re, im, re_acc, im_acc - - -def check_target(expr, result, prec): - a = complex_accuracy(result) - if a < prec: - raise PrecisionExhausted("Failed to distinguish the expression: \n\n%s\n\n" - "from zero. Try simplifying the input, using chop=True, or providing " - "a higher maxn for evalf" % (expr)) - - -def get_integer_part(expr, no, options, return_ints=False): - """ - With no = 1, computes ceiling(expr) - With no = -1, computes floor(expr) - - Note: this function either gives the exact result or signals failure. - """ - - # The expression is likely less than 2^30 or so - assumed_size = 30 - ire, iim, ire_acc, iim_acc = evalf(expr, assumed_size, options) - - # We now know the size, so we can calculate how much extra precision - # (if any) is needed to get within the nearest integer - if ire and iim: - gap = max(fastlog(ire) - ire_acc, fastlog(iim) - iim_acc) - elif ire: - gap = fastlog(ire) - ire_acc - elif iim: - gap = fastlog(iim) - iim_acc - else: - # ... or maybe the expression was exactly zero - return None, None, None, None - - margin = 10 - - if gap >= -margin: - ire, iim, ire_acc, iim_acc = \ - evalf(expr, margin + assumed_size + gap, options) - - # We can now easily find the nearest integer, but to find floor/ceil, we - # must also calculate whether the difference to the nearest integer is - # positive or negative (which may fail if very close). - def calc_part(expr, nexpr): - nint = int(to_int(nexpr, rnd)) - expr = C.Add(expr, -nint, evaluate=False) - x, _, x_acc, _ = evalf(expr, 10, options) - try: - check_target(expr, (x, None, x_acc, None), 3) - except PrecisionExhausted: - if not expr.equals(0): - raise PrecisionExhausted - x = fzero - nint += int(no*(mpf_cmp(x or fzero, fzero) == no)) - nint = from_int(nint) - return nint, fastlog(nint) + 10 - - re, im, re_acc, im_acc = None, None, None, None - - if ire: - re, re_acc = calc_part(C.re(expr, evaluate=False), ire) - if iim: - im, im_acc = calc_part(C.im(expr, evaluate=False), iim) - - if return_ints: - return int(to_int(re or fzero)), int(to_int(im or fzero)) - return re, im, re_acc, im_acc - - -def evalf_ceiling(expr, prec, options): - return get_integer_part(expr.args[0], 1, options) - - -def evalf_floor(expr, prec, options): - return get_integer_part(expr.args[0], -1, options) - -#----------------------------------------------------------------------------# -# # -# Arithmetic operations # -# # -#----------------------------------------------------------------------------# - - -def add_terms(terms, prec, target_prec): - """ - Helper for evalf_add. Adds a list of (mpfval, accuracy) terms. - - Returns - ------- - - - None, None if there are no non-zero terms; - - terms[0] if there is only 1 term; - - scaled_zero if the sum of the terms produces a zero by cancellation - e.g. mpfs representing 1 and -1 would produce a scaled zero which need - special handling since they are not actually zero and they are purposely - malformed to ensure that they can't be used in anything but accuracy - calculations; - - a tuple that is scaled to target_prec that corresponds to the - sum of the terms. - - The returned mpf tuple will be normalized to target_prec; the input - prec is used to define the working precision. - - XXX explain why this is needed and why one can't just loop using mpf_add - """ - terms = [t for t in terms if not iszero(t)] - if not terms: - return None, None - elif len(terms) == 1: - return terms[0] - working_prec = 2*prec - sum_man, sum_exp, absolute_error = 0, 0, MINUS_INF - for x, accuracy in terms: - sign, man, exp, bc = x - if sign: - man = -man - absolute_error = max(absolute_error, bc + exp - accuracy) - delta = exp - sum_exp - if exp >= sum_exp: - # x much larger than existing sum? - # first: quick test - if ((delta > working_prec) and - ((not sum_man) or - delta - bitcount(abs(sum_man)) > working_prec)): - sum_man = man - sum_exp = exp - else: - sum_man += (man << delta) - else: - delta = -delta - # x much smaller than existing sum? - if delta - bc > working_prec: - if not sum_man: - sum_man, sum_exp = man, exp - else: - sum_man = (sum_man << delta) + man - sum_exp = exp - if not sum_man: - return scaled_zero(absolute_error) - if sum_man < 0: - sum_sign = 1 - sum_man = -sum_man - else: - sum_sign = 0 - sum_bc = bitcount(sum_man) - sum_accuracy = sum_exp + sum_bc - absolute_error - r = normalize(sum_sign, sum_man, sum_exp, sum_bc, target_prec, - rnd), sum_accuracy - #print "returning", to_str(r[0],50), r[1] - return r - - -def evalf_add(v, prec, options): - res = pure_complex(v) - if res: - h, c = res - re, _, re_acc, _ = evalf(h, prec, options) - im, _, im_acc, _ = evalf(c, prec, options) - return re, im, re_acc, im_acc - - oldmaxprec = options.get('maxprec', DEFAULT_MAXPREC) - - i = 0 - target_prec = prec - while 1: - options['maxprec'] = min(oldmaxprec, 2*prec) - - terms = [evalf(arg, prec + 10, options) for arg in v.args] - re, re_acc = add_terms( - [a[0::2] for a in terms if a[0]], prec, target_prec) - im, im_acc = add_terms( - [a[1::2] for a in terms if a[1]], prec, target_prec) - acc = complex_accuracy((re, im, re_acc, im_acc)) - if acc >= target_prec: - if options.get('verbose'): - print("ADD: wanted", target_prec, "accurate bits, got", re_acc, im_acc) - break - else: - if (prec - target_prec) > options['maxprec']: - break - - prec = prec + max(10 + 2**i, target_prec - acc) - i += 1 - if options.get('verbose'): - print("ADD: restarting with prec", prec) - - options['maxprec'] = oldmaxprec - if iszero(re, scaled=True): - re = scaled_zero(re) - if iszero(im, scaled=True): - im = scaled_zero(im) - return re, im, re_acc, im_acc - - -def evalf_mul(v, prec, options): - res = pure_complex(v) - if res: - # the only pure complex that is a mul is h*I - _, h = res - im, _, im_acc, _ = evalf(h, prec, options) - return None, im, None, im_acc - args = list(v.args) - - # With guard digits, multiplication in the real case does not destroy - # accuracy. This is also true in the complex case when considering the - # total accuracy; however accuracy for the real or imaginary parts - # separately may be lower. - acc = prec - - # XXX: big overestimate - working_prec = prec + len(args) + 5 - - # Empty product is 1 - start = man, exp, bc = MPZ(1), 0, 1 - - # First, we multiply all pure real or pure imaginary numbers. - # direction tells us that the result should be multiplied by - # I**direction; all other numbers get put into complex_factors - # to be multiplied out after the first phase. - last = len(args) - direction = 0 - args.append(S.One) - complex_factors = [] - for i, arg in enumerate(args): - if i != last and pure_complex(arg): - args[-1] = (args[-1]*arg).expand() - continue - elif i == last and arg is S.One: - continue - re, im, re_acc, im_acc = evalf(arg, working_prec, options) - if re and im: - complex_factors.append((re, im, re_acc, im_acc)) - continue - elif re: - (s, m, e, b), w_acc = re, re_acc - elif im: - (s, m, e, b), w_acc = im, im_acc - direction += 1 - else: - return None, None, None, None - direction += 2*s - man *= m - exp += e - bc += b - if bc > 3*working_prec: - man >>= working_prec - exp += working_prec - acc = min(acc, w_acc) - sign = (direction & 2) >> 1 - if not complex_factors: - v = normalize(sign, man, exp, bitcount(man), prec, rnd) - # multiply by i - if direction & 1: - return None, v, None, acc - else: - return v, None, acc, None - else: - # initialize with the first term - if (man, exp, bc) != start: - # there was a real part; give it an imaginary part - re, im = (sign, man, exp, bitcount(man)), (0, MPZ(0), 0, 0) - i0 = 0 - else: - # there is no real part to start (other than the starting 1) - wre, wim, wre_acc, wim_acc = complex_factors[0] - acc = min(acc, - complex_accuracy((wre, wim, wre_acc, wim_acc))) - re = wre - im = wim - i0 = 1 - - for wre, wim, wre_acc, wim_acc in complex_factors[i0:]: - # acc is the overall accuracy of the product; we aren't - # computing exact accuracies of the product. - acc = min(acc, - complex_accuracy((wre, wim, wre_acc, wim_acc))) - - use_prec = working_prec - A = mpf_mul(re, wre, use_prec) - B = mpf_mul(mpf_neg(im), wim, use_prec) - C = mpf_mul(re, wim, use_prec) - D = mpf_mul(im, wre, use_prec) - re = mpf_add(A, B, use_prec) - im = mpf_add(C, D, use_prec) - if options.get('verbose'): - print("MUL: wanted", prec, "accurate bits, got", acc) - # multiply by I - if direction & 1: - re, im = mpf_neg(im), re - return re, im, acc, acc - - -def evalf_pow(v, prec, options): - - target_prec = prec - base, exp = v.args - - # We handle x**n separately. This has two purposes: 1) it is much - # faster, because we avoid calling evalf on the exponent, and 2) it - # allows better handling of real/imaginary parts that are exactly zero - if exp.is_Integer: - p = exp.p - # Exact - if not p: - return fone, None, prec, None - # Exponentiation by p magnifies relative error by |p|, so the - # base must be evaluated with increased precision if p is large - prec += int(math.log(abs(p), 2)) - re, im, re_acc, im_acc = evalf(base, prec + 5, options) - # Real to integer power - if re and not im: - return mpf_pow_int(re, p, target_prec), None, target_prec, None - # (x*I)**n = I**n * x**n - if im and not re: - z = mpf_pow_int(im, p, target_prec) - case = p % 4 - if case == 0: - return z, None, target_prec, None - if case == 1: - return None, z, None, target_prec - if case == 2: - return mpf_neg(z), None, target_prec, None - if case == 3: - return None, mpf_neg(z), None, target_prec - # Zero raised to an integer power - if not re: - return None, None, None, None - # General complex number to arbitrary integer power - re, im = libmp.mpc_pow_int((re, im), p, prec) - # Assumes full accuracy in input - return finalize_complex(re, im, target_prec) - - # Pure square root - if exp is S.Half: - xre, xim, _, _ = evalf(base, prec + 5, options) - # General complex square root - if xim: - re, im = libmp.mpc_sqrt((xre or fzero, xim), prec) - return finalize_complex(re, im, prec) - if not xre: - return None, None, None, None - # Square root of a negative real number - if mpf_lt(xre, fzero): - return None, mpf_sqrt(mpf_neg(xre), prec), None, prec - # Positive square root - return mpf_sqrt(xre, prec), None, prec, None - - # We first evaluate the exponent to find its magnitude - # This determines the working precision that must be used - prec += 10 - yre, yim, _, _ = evalf(exp, prec, options) - # Special cases: x**0 - if not (yre or yim): - return fone, None, prec, None - - ysize = fastlog(yre) - # Restart if too big - # XXX: prec + ysize might exceed maxprec - if ysize > 5: - prec += ysize - yre, yim, _, _ = evalf(exp, prec, options) - - # Pure exponential function; no need to evalf the base - if base is S.Exp1: - if yim: - re, im = libmp.mpc_exp((yre or fzero, yim), prec) - return finalize_complex(re, im, target_prec) - return mpf_exp(yre, target_prec), None, target_prec, None - - xre, xim, _, _ = evalf(base, prec + 5, options) - # 0**y - if not (xre or xim): - return None, None, None, None - - # (real ** complex) or (complex ** complex) - if yim: - re, im = libmp.mpc_pow( - (xre or fzero, xim or fzero), (yre or fzero, yim), - target_prec) - return finalize_complex(re, im, target_prec) - # complex ** real - if xim: - re, im = libmp.mpc_pow_mpf((xre or fzero, xim), yre, target_prec) - return finalize_complex(re, im, target_prec) - # negative ** real - elif mpf_lt(xre, fzero): - re, im = libmp.mpc_pow_mpf((xre, fzero), yre, target_prec) - return finalize_complex(re, im, target_prec) - # positive ** real - else: - return mpf_pow(xre, yre, target_prec), None, target_prec, None - - -#----------------------------------------------------------------------------# -# # -# Special functions # -# # -#----------------------------------------------------------------------------# -def evalf_trig(v, prec, options): - """ - This function handles sin and cos of real arguments. - - TODO: should also handle tan and complex arguments. - """ - if v.func is C.cos: - func = mpf_cos - elif v.func is C.sin: - func = mpf_sin - else: - raise NotImplementedError - arg = v.args[0] - # 20 extra bits is possibly overkill. It does make the need - # to restart very unlikely - xprec = prec + 20 - re, im, re_acc, im_acc = evalf(arg, xprec, options) - if im: - raise NotImplementedError - if not re: - if v.func is C.cos: - return fone, None, prec, None - elif v.func is C.sin: - return None, None, None, None - else: - raise NotImplementedError - # For trigonometric functions, we are interested in the - # fixed-point (absolute) accuracy of the argument. - xsize = fastlog(re) - # Magnitude <= 1.0. OK to compute directly, because there is no - # danger of hitting the first root of cos (with sin, magnitude - # <= 2.0 would actually be ok) - if xsize < 1: - return func(re, prec, rnd), None, prec, None - # Very large - if xsize >= 10: - xprec = prec + xsize - re, im, re_acc, im_acc = evalf(arg, xprec, options) - # Need to repeat in case the argument is very close to a - # multiple of pi (or pi/2), hitting close to a root - while 1: - y = func(re, prec, rnd) - ysize = fastlog(y) - gap = -ysize - accuracy = (xprec - xsize) - gap - if accuracy < prec: - if options.get('verbose'): - print("SIN/COS", accuracy, "wanted", prec, "gap", gap) - print(to_str(y, 10)) - if xprec > options.get('maxprec', DEFAULT_MAXPREC): - return y, None, accuracy, None - xprec += gap - re, im, re_acc, im_acc = evalf(arg, xprec, options) - continue - else: - return y, None, prec, None - - -def evalf_log(expr, prec, options): - arg = expr.args[0] - workprec = prec + 10 - xre, xim, xacc, _ = evalf(arg, workprec, options) - - if xim: - # XXX: use get_abs etc instead - re = evalf_log( - C.log(C.Abs(arg, evaluate=False), evaluate=False), prec, options) - im = mpf_atan2(xim, xre or fzero, prec) - return re[0], im, re[2], prec - - imaginary_term = (mpf_cmp(xre, fzero) < 0) - - re = mpf_log(mpf_abs(xre), prec, rnd) - size = fastlog(re) - if prec - size > workprec: - # We actually need to compute 1+x accurately, not x - arg = C.Add(S.NegativeOne, arg, evaluate=False) - xre, xim, _, _ = evalf_add(arg, prec, options) - prec2 = workprec - fastlog(xre) - re = mpf_log(mpf_add(xre, fone, prec2), prec, rnd) - - re_acc = prec - - if imaginary_term: - return re, mpf_pi(prec), re_acc, prec - else: - return re, None, re_acc, None - - -def evalf_atan(v, prec, options): - arg = v.args[0] - xre, xim, reacc, imacc = evalf(arg, prec + 5, options) - if xre is xim is None: - return (None,)*4 - if xim: - raise NotImplementedError - return mpf_atan(xre, prec, rnd), None, prec, None - - -def evalf_subs(prec, subs): - """ Change all Float entries in `subs` to have precision prec. """ - newsubs = {} - for a, b in list(subs.items()): - b = S(b) - if b.is_Float: - b = b._eval_evalf(prec) - newsubs[a] = b - return newsubs - - -def evalf_piecewise(expr, prec, options): - if 'subs' in options: - expr = expr.subs(evalf_subs(prec, options['subs'])) - newopts = options.copy() - del newopts['subs'] - if hasattr(expr, 'func'): - return evalf(expr, prec, newopts) - if type(expr) == float: - return evalf(C.Float(expr), prec, newopts) - if type(expr) == int: - return evalf(C.Integer(expr), prec, newopts) - - # We still have undefined symbols - raise NotImplementedError - - -def evalf_bernoulli(expr, prec, options): - arg = expr.args[0] - if not arg.is_Integer: - raise ValueError("Bernoulli number index must be an integer") - n = int(arg) - b = mpf_bernoulli(n, prec, rnd) - if b == fzero: - return None, None, None, None - return b, None, prec, None - -#----------------------------------------------------------------------------# -# # -# High-level operations # -# # -#----------------------------------------------------------------------------# - - -def as_mpmath(x, prec, options): - x = sympify(x) - if isinstance(x, C.Zero): - return mpf(0) - if isinstance(x, C.Infinity): - return mpf('inf') - if isinstance(x, C.NegativeInfinity): - return mpf('-inf') - # XXX - re, im, _, _ = evalf(x, prec, options) - if im: - return mpc(re or fzero, im) - return mpf(re) - - -def do_integral(expr, prec, options): - func = expr.args[0] - x, xlow, xhigh = expr.args[1] - orig = mp.prec - - oldmaxprec = options.get('maxprec', DEFAULT_MAXPREC) - options['maxprec'] = min(oldmaxprec, 2*prec) - - try: - mp.prec = prec + 5 - xlow = as_mpmath(xlow, prec + 15, options) - xhigh = as_mpmath(xhigh, prec + 15, options) - - # Integration is like summation, and we can phone home from - # the integrand function to update accuracy summation style - # Note that this accuracy is inaccurate, since it fails - # to account for the variable quadrature weights, - # but it is better than nothing - - have_part = [False, False] - max_real_term = [MINUS_INF] - max_imag_term = [MINUS_INF] - - def f(t): - re, im, re_acc, im_acc = evalf(func, mp.prec, {'subs': {x: t}}) - - have_part[0] = re or have_part[0] - have_part[1] = im or have_part[1] - - max_real_term[0] = max(max_real_term[0], fastlog(re)) - max_imag_term[0] = max(max_imag_term[0], fastlog(im)) - - if im: - return mpc(re or fzero, im) - return mpf(re or fzero) - - if options.get('quad') == 'osc': - A = C.Wild('A', exclude=[x]) - B = C.Wild('B', exclude=[x]) - D = C.Wild('D') - m = func.match(C.cos(A*x + B)*D) - if not m: - m = func.match(C.sin(A*x + B)*D) - if not m: - raise ValueError("An integrand of the form sin(A*x+B)*f(x) " - "or cos(A*x+B)*f(x) is required for oscillatory quadrature") - period = as_mpmath(2*S.Pi/m[A], prec + 15, options) - result = quadosc(f, [xlow, xhigh], period=period) - # XXX: quadosc does not do error detection yet - quadrature_error = MINUS_INF - else: - result, quadrature_error = quadts(f, [xlow, xhigh], error=1) - quadrature_error = fastlog(quadrature_error._mpf_) - - finally: - options['maxprec'] = oldmaxprec - mp.prec = orig - - if have_part[0]: - re = result.real._mpf_ - if re == fzero: - re, re_acc = scaled_zero( - min(-prec, -max_real_term[0], -quadrature_error)) - re = scaled_zero(re) # handled ok in evalf_integral - else: - re_acc = -max(max_real_term[0] - fastlog(re) - - prec, quadrature_error) - else: - re, re_acc = None, None - - if have_part[1]: - im = result.imag._mpf_ - if im == fzero: - im, im_acc = scaled_zero( - min(-prec, -max_imag_term[0], -quadrature_error)) - im = scaled_zero(im) # handled ok in evalf_integral - else: - im_acc = -max(max_imag_term[0] - fastlog(im) - - prec, quadrature_error) - else: - im, im_acc = None, None - - result = re, im, re_acc, im_acc - return result - - -def evalf_integral(expr, prec, options): - workprec = prec - i = 0 - maxprec = options.get('maxprec', INF) - while 1: - result = do_integral(expr, workprec, options) - # if a scaled_zero comes back accuracy will compute to -1 - # which will cause workprec to increment by 1 - accuracy = complex_accuracy(result) - if accuracy >= prec or workprec >= maxprec: - return result - workprec += prec - max(-2**i, accuracy) - i += 1 - - -def check_convergence(numer, denom, n): - """ - Returns (h, g, p) where - -- h is: - > 0 for convergence of rate 1/factorial(n)**h - < 0 for divergence of rate factorial(n)**(-h) - = 0 for geometric or polynomial convergence or divergence - - -- abs(g) is: - > 1 for geometric convergence of rate 1/h**n - < 1 for geometric divergence of rate h**n - = 1 for polynomial convergence or divergence - - (g < 0 indicates an alternating series) - - -- p is: - > 1 for polynomial convergence of rate 1/n**h - <= 1 for polynomial divergence of rate n**(-h) - - """ - npol = C.Poly(numer, n) - dpol = C.Poly(denom, n) - p = npol.degree() - q = dpol.degree() - rate = q - p - if rate: - return rate, None, None - constant = dpol.LC() / npol.LC() - if abs(constant) != 1: - return rate, constant, None - if npol.degree() == dpol.degree() == 0: - return rate, constant, 0 - pc = npol.all_coeffs()[1] - qc = dpol.all_coeffs()[1] - return rate, constant, (qc - pc)/dpol.LC() - - -def hypsum(expr, n, start, prec): - """ - Sum a rapidly convergent infinite hypergeometric series with - given general term, e.g. e = hypsum(1/factorial(n), n). The - quotient between successive terms must be a quotient of integer - polynomials. - """ - from sympy import hypersimp, lambdify - - if start: - expr = expr.subs(n, n + start) - hs = hypersimp(expr, n) - if hs is None: - raise NotImplementedError("a hypergeometric series is required") - num, den = hs.as_numer_denom() - - func1 = lambdify(n, num) - func2 = lambdify(n, den) - - h, g, p = check_convergence(num, den, n) - - if h < 0: - raise ValueError("Sum diverges like (n!)^%i" % (-h)) - - # Direct summation if geometric or faster - if h > 0 or (h == 0 and abs(g) > 1): - term = expr.subs(n, 0) - term = (MPZ(term.p) << prec) // term.q - s = term - k = 1 - while abs(term) > 5: - term *= MPZ(func1(k - 1)) - term //= MPZ(func2(k - 1)) - s += term - k += 1 - return from_man_exp(s, -prec) - else: - alt = g < 0 - if abs(g) < 1: - raise ValueError("Sum diverges like (%i)^n" % abs(1/g)) - if p < 1 or (p == 1 and not alt): - raise ValueError("Sum diverges like n^%i" % (-p)) - # We have polynomial convergence: use Richardson extrapolation - # Need to use at least quad precision because a lot of cancellation - # might occur in the extrapolation process - prec2 = 4*prec - term = expr.subs(n, 0) - term = (MPZ(term.p) << prec2) // term.q - - def summand(k, _term=[term]): - if k: - k = int(k) - _term[0] *= MPZ(func1(k - 1)) - _term[0] //= MPZ(func2(k - 1)) - return make_mpf(from_man_exp(_term[0], -prec2)) - - orig = mp.prec - try: - mp.prec = prec - v = nsum(summand, [0, mpmath_inf], method='richardson') - finally: - mp.prec = orig - return v._mpf_ - - -def evalf_sum(expr, prec, options): - func = expr.function - limits = expr.limits - if len(limits) != 1 or not isinstance(limits[0], Tuple) or \ - len(limits[0]) != 3: - raise NotImplementedError - prec2 = prec + 10 - try: - n, a, b = limits[0] - if b != S.Infinity or a != int(a): - raise NotImplementedError - # Use fast hypergeometric summation if possible - v = hypsum(func, n, int(a), prec2) - delta = prec - fastlog(v) - if fastlog(v) < -10: - v = hypsum(func, n, int(a), delta) - return v, None, min(prec, delta), None - except NotImplementedError: - # Euler-Maclaurin summation for general series - eps = C.Float(2.0)**(-prec) - for i in range(1, 5): - m = n = 2**i * prec - s, err = expr.euler_maclaurin(m=m, n=n, eps=eps, - eval_integral=False) - err = err.evalf() - if err <= eps: - break - err = fastlog(evalf(abs(err), 20, options)[0]) - try: - re, im, re_acc, im_acc = evalf(s, prec2, options) - except TypeError: # issue 3174 - # when should it try subs if they are in options? - raise NotImplementedError - if re_acc is None: - re_acc = -err - if im_acc is None: - im_acc = -err - return re, im, re_acc, im_acc - - -#----------------------------------------------------------------------------# -# # -# Symbolic interface # -# # -#----------------------------------------------------------------------------# - -def evalf_symbol(x, prec, options): - val = options['subs'][x] - if isinstance(val, mpf): - if not val: - return None, None, None, None - return val._mpf_, None, prec, None - else: - if not '_cache' in options: - options['_cache'] = {} - cache = options['_cache'] - cached, cached_prec = cache.get(x.name, (None, MINUS_INF)) - if cached_prec >= prec: - return cached - v = evalf(sympify(val), prec, options) - cache[x.name] = (v, prec) - return v - -evalf_table = None - - -def _create_evalf_table(): - global evalf_table - evalf_table = { - C.Symbol: evalf_symbol, - C.Dummy: evalf_symbol, - C.Float: lambda x, prec, options: (x._mpf_, None, prec, None), - C.Rational: lambda x, prec, options: (from_rational(x.p, x.q, prec), None, prec, None), - C.Integer: lambda x, prec, options: (from_int(x.p, prec), None, prec, None), - C.Zero: lambda x, prec, options: (None, None, prec, None), - C.One: lambda x, prec, options: (fone, None, prec, None), - C.Half: lambda x, prec, options: (fhalf, None, prec, None), - C.Pi: lambda x, prec, options: (mpf_pi(prec), None, prec, None), - C.Exp1: lambda x, prec, options: (mpf_e(prec), None, prec, None), - C.ImaginaryUnit: lambda x, prec, options: (None, fone, None, prec), - C.NegativeOne: lambda x, prec, options: (fnone, None, prec, None), - C.NaN : lambda x, prec, options: (fnan, None, prec, None), - - C.exp: lambda x, prec, options: evalf_pow(C.Pow(S.Exp1, x.args[0], - evaluate=False), prec, options), - - C.cos: evalf_trig, - C.sin: evalf_trig, - - C.Add: evalf_add, - C.Mul: evalf_mul, - C.Pow: evalf_pow, - - C.log: evalf_log, - C.atan: evalf_atan, - C.Abs: evalf_abs, - - C.re: evalf_re, - C.im: evalf_im, - C.floor: evalf_floor, - C.ceiling: evalf_ceiling, - - C.Integral: evalf_integral, - C.Sum: evalf_sum, - C.Piecewise: evalf_piecewise, - - C.bernoulli: evalf_bernoulli, - } - - -def evalf(x, prec, options): - from sympy import re as re_, im as im_ - try: - rf = evalf_table[x.func] - r = rf(x, prec, options) - except KeyError: - try: - # Fall back to ordinary evalf if possible - if 'subs' in options: - x = x.subs(evalf_subs(prec, options['subs'])) - re, im = x._eval_evalf(prec).as_real_imag() - if re.has(re_) or im.has(im_): - raise NotImplementedError - if re == 0: - re = None - reprec = None - else: - re = re._to_mpmath(prec, allow_ints=False)._mpf_ - reprec = prec - if im == 0: - im = None - imprec = None - else: - im = im._to_mpmath(prec, allow_ints=False)._mpf_ - imprec = prec - r = re, im, reprec, imprec - except AttributeError: - raise NotImplementedError - if options.get("verbose"): - print("### input", x) - print("### output", to_str(r[0] or fzero, 50)) - print("### raw", r) # r[0], r[2] - print() - chop = options.get('chop', False) - if chop: - if chop is True: - chop_prec = prec - else: - # convert (approximately) from given tolerance; - # the formula here will will make 1e-i rounds to 0 for - # i in the range +/-27 while 2e-i will not be chopped - chop_prec = int(round(-3.321*math.log10(chop) + 2.5)) - if chop_prec == 3: - chop_prec -= 1 - r = chop_parts(r, chop_prec) - if options.get("strict"): - check_target(x, r, prec) - return r - - -class EvalfMixin(object): - """Mixin class adding evalf capabililty.""" - - __slots__ = [] - - def evalf(self, n=15, subs=None, maxn=100, chop=False, strict=False, quad=None, verbose=False): - """ - Evaluate the given formula to an accuracy of n digits. - Optional keyword arguments: - - subs=<dict> - Substitute numerical values for symbols, e.g. - subs={x:3, y:1+pi}. - - maxn=<integer> - Allow a maximum temporary working precision of maxn digits - (default=100) - - chop=<bool> - Replace tiny real or imaginary parts in subresults - by exact zeros (default=False) - - strict=<bool> - Raise PrecisionExhausted if any subresult fails to evaluate - to full accuracy, given the available maxprec - (default=False) - - quad=<str> - Choose algorithm for numerical quadrature. By default, - tanh-sinh quadrature is used. For oscillatory - integrals on an infinite interval, try quad='osc'. - - verbose=<bool> - Print debug information (default=False) - - """ - # for sake of sage that doesn't like evalf(1) - if n == 1 and isinstance(self, C.Number): - from sympy.core.expr import _mag - rv = self.evalf(2, subs, maxn, chop, strict, quad, verbose) - m = _mag(rv) - rv = rv.round(1 - m) - return rv - - if not evalf_table: - _create_evalf_table() - prec = dps_to_prec(n) - options = {'maxprec': max(prec, int(maxn*LG10)), 'chop': chop, - 'strict': strict, 'verbose': verbose} - if subs is not None: - options['subs'] = subs - if quad is not None: - options['quad'] = quad - try: - result = evalf(self, prec + 4, options) - except NotImplementedError: - # Fall back to the ordinary evalf - v = self._eval_evalf(prec) - if v is None: - return self - try: - # If the result is numerical, normalize it - result = evalf(v, prec, options) - except NotImplementedError: - # Probably contains symbols or unknown functions - return v - re, im, re_acc, im_acc = result - if re: - p = max(min(prec, re_acc), 1) - #re = mpf_pos(re, p, rnd) - re = C.Float._new(re, p) - else: - re = S.Zero - if im: - p = max(min(prec, im_acc), 1) - #im = mpf_pos(im, p, rnd) - im = C.Float._new(im, p) - return re + im*S.ImaginaryUnit - else: - return re - - n = evalf - - def _evalf(self, prec): - """Helper for evalf. Does the same thing but takes binary precision""" - r = self._eval_evalf(prec) - if r is None: - r = self - return r - - def _eval_evalf(self, prec): - return - - def _to_mpmath(self, prec, allow_ints=True): - # mpmath functions accept ints as input - errmsg = "cannot convert to mpmath number" - if allow_ints and self.is_Integer: - return self.p - if hasattr(self, '_as_mpf_val'): - return make_mpf(self._as_mpf_val(prec)) - try: - re, im, _, _ = evalf(self, prec, {}) - if im: - if not re: - re = fzero - return make_mpc((re, im)) - elif re: - return make_mpf(re) - else: - return make_mpf(fzero) - except NotImplementedError: - v = self._eval_evalf(prec) - if v is None: - raise ValueError(errmsg) - if v.is_Float: - return make_mpf(v._mpf_) - # Number + Number*I is also fine - re, im = v.as_real_imag() - if allow_ints and re.is_Integer: - re = from_int(re.p) - elif re.is_Float: - re = re._mpf_ - else: - raise ValueError(errmsg) - if allow_ints and im.is_Integer: - im = from_int(im.p) - elif im.is_Float: - im = im._mpf_ - else: - raise ValueError(errmsg) - return make_mpc((re, im)) - - -
    [docs]def N(x, n=15, **options): - """ - Calls x.evalf(n, \*\*options). - - Both .n() and N() are equivalent to .evalf(); use the one that you like better. - See also the docstring of .evalf() for information on the options. - - Examples - ======== - - >>> from sympy import Sum, Symbol, oo, N - >>> from sympy.abc import k - >>> Sum(1/k**k, (k, 1, oo)) - Sum(k**(-k), (k, 1, oo)) - >>> N(_, 4) - 1.291 - - """ - return sympify(x).evalf(n, **options)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/core/expr.html b/dev-py3k/_modules/sympy/core/expr.html deleted file mode 100644 index 8c9f2267bfa..00000000000 --- a/dev-py3k/_modules/sympy/core/expr.html +++ /dev/null @@ -1,3079 +0,0 @@ - - - - - - - - - - sympy.core.expr — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.core.expr

    -from .core import C
    -from .sympify import sympify
    -from .basic import Basic, Atom
    -from .singleton import S
    -from .evalf import EvalfMixin, pure_complex
    -from .decorators import _sympifyit, call_highest_priority
    -from .cache import cacheit
    -from .compatibility import reduce, as_int, default_sort_key
    -from sympy.mpmath.libmp import mpf_log, prec_to_dps
    -
    -from collections import defaultdict
    -from inspect import getmro
    -import collections
    -from functools import reduce
    -
    -
    -
    [docs]class Expr(Basic, EvalfMixin): - __slots__ = [] - - @property - def _diff_wrt(self): - """Is it allowed to take derivative wrt to this instance. - - This determines if it is allowed to take derivatives wrt this object. - Subclasses such as Symbol, Function and Derivative should return True - to enable derivatives wrt them. The implementation in Derivative - separates the Symbol and non-Symbol _diff_wrt=True variables and - temporarily converts the non-Symbol vars in Symbols when performing - the differentiation. - - Note, see the docstring of Derivative for how this should work - mathematically. In particular, note that expr.subs(yourclass, Symbol) - should be well-defined on a structural level, or this will lead to - inconsistent results. - - Examples - ======== - - >>> from sympy import Expr - >>> e = Expr() - >>> e._diff_wrt - False - >>> class MyClass(Expr): - ... _diff_wrt = True - ... - >>> (2*MyClass()).diff(MyClass()) - 2 - """ - return False - - @cacheit - def sort_key(self, order=None): - - coeff, expr = self.as_coeff_Mul() - - if expr.is_Pow: - expr, exp = expr.args - else: - expr, exp = expr, S.One - - if expr.is_Atom: - args = (str(expr),) - else: - if expr.is_Add: - args = expr.as_ordered_terms(order=order) - elif expr.is_Mul: - args = expr.as_ordered_factors(order=order) - else: - args = expr.args - - args = tuple( - [ default_sort_key(arg, order=order) for arg in args ]) - - args = (len(args), tuple(args)) - exp = exp.sort_key(order=order) - - return expr.class_key(), args, exp, coeff - - def __call__(self, *args): - # (x+Lambda(y, 2*y))(z) -> x+2*z - return Expr._recursive_call(self, args) - - @staticmethod - def _recursive_call(expr_to_call, on_args): - def the_call_method_is_overridden(expr): - for cls in getmro(type(expr)): - if '__call__' in cls.__dict__: - return cls != Expr - - if isinstance(expr_to_call, collections.Callable) and the_call_method_is_overridden(expr_to_call): - if isinstance(expr_to_call, C.Symbol): # XXX When you call a Symbol it is - return expr_to_call # transformed into an UndefFunction - else: - return expr_to_call(*on_args) - elif expr_to_call.args: - args = [Expr._recursive_call( - sub, on_args) for sub in expr_to_call.args] - return type(expr_to_call)(*args) - else: - return expr_to_call - - # *************** - # * Arithmetics * - # *************** - # Expr and its sublcasses use _op_priority to determine which object - # passed to a binary special method (__mul__, etc.) will handle the - # operation. In general, the 'call_highest_priority' decorator will choose - # the object with the highest _op_priority to handle the call. - # Custom subclasses that want to define their own binary special methods - # should set an _op_priority value that is higher than the default. - # - # **NOTE**: - # This is a temporary fix, and will eventually be replaced with - # something better and more powerful. See issue 2411. - _op_priority = 10.0 - - def __pos__(self): - return self - - def __neg__(self): - return Mul(S.NegativeOne, self) - - def __abs__(self): - return C.Abs(self) - - @_sympifyit('other', NotImplemented) - @call_highest_priority('__radd__') - def __add__(self, other): - return Add(self, other) - - @_sympifyit('other', NotImplemented) - @call_highest_priority('__add__') - def __radd__(self, other): - return Add(other, self) - - @_sympifyit('other', NotImplemented) - @call_highest_priority('__rsub__') - def __sub__(self, other): - return Add(self, -other) - - @_sympifyit('other', NotImplemented) - @call_highest_priority('__sub__') - def __rsub__(self, other): - return Add(other, -self) - - @_sympifyit('other', NotImplemented) - @call_highest_priority('__rmul__') - def __mul__(self, other): - return Mul(self, other) - - @_sympifyit('other', NotImplemented) - @call_highest_priority('__mul__') - def __rmul__(self, other): - return Mul(other, self) - - @_sympifyit('other', NotImplemented) - @call_highest_priority('__rpow__') - def __pow__(self, other): - return Pow(self, other) - - @_sympifyit('other', NotImplemented) - @call_highest_priority('__pow__') - def __rpow__(self, other): - return Pow(other, self) - - @_sympifyit('other', NotImplemented) - @call_highest_priority('__rdiv__') - def __div__(self, other): - return Mul(self, Pow(other, S.NegativeOne)) - - @_sympifyit('other', NotImplemented) - @call_highest_priority('__div__') - def __rdiv__(self, other): - return Mul(other, Pow(self, S.NegativeOne)) - - __truediv__ = __div__ - __rtruediv__ = __rdiv__ - - @_sympifyit('other', NotImplemented) - @call_highest_priority('__rmod__') - def __mod__(self, other): - return Mod(self, other) - - @_sympifyit('other', NotImplemented) - @call_highest_priority('__mod__') - def __rmod__(self, other): - return Mod(other, self) - - def __int__(self): - # Although we only need to round to the units position, we'll - # get one more digit so the extra testing below can be avoided - # unless the rounded value rounded to an integer, e.g. if an - # expression were equal to 1.9 and we rounded to the unit position - # we would get a 2 and would not know if this rounded up or not - # without doing a test (as done below). But if we keep an extra - # digit we know that 1.9 is not the same as 1 and there is no - # need for further testing: our int value is correct. If the value - # were 1.99, however, this would round to 2.0 and our int value is - # off by one. So...if our round value is the same as the int value - # (regardless of how much extra work we do to calculate extra decimal - # places) we need to test whether we are off by one. - r = self.round(2) - if not r.is_Number: - raise TypeError("can't convert complex to int") - i = int(r) - if not i: - return 0 - # off-by-one check - if i == r and not (self - i).equals(0): - isign = 1 if i > 0 else -1 - x = C.Dummy() - # in the following (self - i).evalf(2) will not always work while - # (self - r).evalf(2) and the use of subs does; if the test that - # was added when this comment was added passes, it might be safe - # to simply use sign to compute this rather than doing this by hand: - diff_sign = 1 if (self - x).evalf(2, subs={x: i}) > 0 else -1 - if diff_sign != isign: - i -= isign - return i - - def __float__(self): - # Don't bother testing if it's a number; if it's not this is going - # to fail, and if it is we still need to check that it evalf'ed to - # a number. - result = self.evalf() - if result.is_Number: - return float(result) - if result.is_number and result.as_real_imag()[1]: - raise TypeError("can't convert complex to float") - raise TypeError("can't convert expression to float") - - def __complex__(self): - result = self.evalf() - re, im = result.as_real_imag() - return complex(float(re), float(im)) - - @_sympifyit('other', False) # sympy > other - def __ge__(self, other): - dif = self - other - if dif.is_nonnegative != dif.is_negative: - return dif.is_nonnegative - return C.GreaterThan(self, other) - - @_sympifyit('other', False) # sympy > other - def __le__(self, other): - dif = self - other - if dif.is_nonpositive != dif.is_positive: - return dif.is_nonpositive - return C.LessThan(self, other) - - @_sympifyit('other', False) # sympy > other - def __gt__(self, other): - dif = self - other - if dif.is_positive != dif.is_nonpositive: - return dif.is_positive - return C.StrictGreaterThan(self, other) - - @_sympifyit('other', False) # sympy > other - def __lt__(self, other): - dif = self - other - if dif.is_negative != dif.is_nonnegative: - return dif.is_negative - return C.StrictLessThan(self, other) - - @staticmethod - def _from_mpmath(x, prec): - if hasattr(x, "_mpf_"): - return C.Float._new(x._mpf_, prec) - elif hasattr(x, "_mpc_"): - re, im = x._mpc_ - re = C.Float._new(re, prec) - im = C.Float._new(im, prec)*S.ImaginaryUnit - return re + im - else: - raise TypeError("expected mpmath number (mpf or mpc)") - - @property -
    [docs] def is_number(self): - """Returns True if 'self' is a number. - - >>> from sympy import log, Integral - >>> from sympy.abc import x, y - - >>> x.is_number - False - >>> (2*x).is_number - False - >>> (2 + log(2)).is_number - True - >>> (2 + Integral(2, x)).is_number - False - >>> (2 + Integral(2, (x, 1, 2))).is_number - True - - """ - if not self.args: - return False - return all(obj.is_number for obj in self.iter_basic_args()) -
    - def _random(self, n=None, re_min=-1, im_min=-1, re_max=1, im_max=1): - """Return self evaluated, if possible, replacing free symbols with - random complex values, if necessary. - - The random complex value for each free symbol is generated - by the random_complex_number routine giving real and imaginary - parts in the range given by the re_min, re_max, im_min, and im_max - values. The returned value is evaluated to a precision of n - (if given) else the maximum of 15 and the precision needed - to get more than 1 digit of precision. If the expression - could not be evaluated to a number, or could not be evaluated - to more than 1 digit of precision, then None is returned. - - Examples - ======== - - >>> from sympy import sqrt - >>> from sympy.abc import x, y - >>> x._random() # doctest: +SKIP - 0.0392918155679172 + 0.916050214307199*I - >>> x._random(2) # doctest: +SKIP - -0.77 - 0.87*I - >>> (x + y/2)._random(2) # doctest: +SKIP - -0.57 + 0.16*I - >>> sqrt(2)._random(2) - 1.4 - - See Also - ======== - - sympy.utilities.randtest.random_complex_number - """ - - free = self.free_symbols - prec = 1 - if free: - from sympy.utilities.randtest import random_complex_number - a, c, b, d = re_min, re_max, im_min, im_max - reps = dict(list(zip(free, [random_complex_number(a, b, c, d, rational=True) - for zi in free]))) - try: - nmag = abs(self.evalf(2, subs=reps)) - except TypeError: - # if an out of range value resulted in evalf problems - # then return None -- XXX is there a way to know how to - # select a good random number for a given expression? - # e.g. when calculating n! negative values for n should not - # be used - return None - else: - reps = {} - nmag = abs(self.evalf(2)) - - if not hasattr(nmag, '_prec'): - # e.g. exp_polar(2*I*pi) doesn't evaluate but is_number is True - return None - - if nmag._prec == 1: - # increase the precision up to the default maximum - # precision to see if we can get any significance - - # get the prec steps (patterned after giant_steps in - # libintmath) which approximately doubles the prec - # each step - from sympy.core.evalf import DEFAULT_MAXPREC as target - L = [target] - start = 2 - while 1: - Li = L[-1]//2 + 2 - if Li >= L[-1] or Li < start: - if L[-1] != start: - L.append(start) - break - L.append(Li) - L = L[::-1] - - # evaluate - for prec in L: - nmag = abs(self.evalf(prec, subs=reps)) - if nmag._prec != 1: - break - - if nmag._prec != 1: - if n is None: - n = max(prec, 15) - return self.evalf(n, subs=reps) - - # never got any significance - return None - -
    [docs] def is_constant(self, *wrt, **flags): - """Return True if self is constant, False if not, or None if - the constancy could not be determined conclusively. - - If an expression has no free symbols then it is a constant. If - there are free symbols it is possible that the expression is a - constant, perhaps (but not necessarily) zero. To test such - expressions, two strategies are tried: - - 1) numerical evaluation at two random points. If two such evaluations - give two different values and the values have a precision greater than - 1 then self is not constant. If the evaluations agree or could not be - obtained with any precision, no decision is made. The numerical testing - is done only if ``wrt`` is different than the free symbols. - - 2) differentiation with respect to variables in 'wrt' (or all free - symbols if omitted) to see if the expression is constant or not. This - will not always lead to an expression that is zero even though an - expression is constant (see added test in test_expr.py). If - all derivatives are zero then self is constant with respect to the - given symbols. - - If neither evaluation nor differentiation can prove the expression is - constant, None is returned unless two numerical values happened to be - the same and the flag ``failing_number`` is True -- in that case the - numerical value will be returned. - - If flag simplify=False is passed, self will not be simplified; - the default is True since self should be simplified before testing. - - Examples - ======== - - >>> from sympy import cos, sin, Sum, S, pi - >>> from sympy.abc import a, n, x, y - >>> x.is_constant() - False - >>> S(2).is_constant() - True - >>> Sum(x, (x, 1, 10)).is_constant() - True - >>> Sum(x, (x, 1, n)).is_constant() # doctest: +SKIP - False - >>> Sum(x, (x, 1, n)).is_constant(y) - True - >>> Sum(x, (x, 1, n)).is_constant(n) # doctest: +SKIP - False - >>> Sum(x, (x, 1, n)).is_constant(x) - True - >>> eq = a*cos(x)**2 + a*sin(x)**2 - a - >>> eq.is_constant() - True - >>> eq.subs({x:pi, a:2}) == eq.subs({x:pi, a:3}) == 0 - True - - >>> (0**x).is_constant() - False - >>> x.is_constant() - False - >>> (x**x).is_constant() - False - >>> one = cos(x)**2 + sin(x)**2 - >>> one.is_constant() - True - >>> ((one - 1)**(x + 1)).is_constant() # could be 0 or 1 - False - """ - - simplify = flags.get('simplify', True) - - # Except for expressions that contain units, only one of these should - # be necessary since if something is - # known to be a number it should also know that there are no - # free symbols. But is_number quits as soon as it hits a non-number - # whereas free_symbols goes until all free symbols have been collected, - # thus is_number should be faster. But a double check on free symbols - # is made just in case there is a discrepancy between the two. - free = self.free_symbols - if self.is_number or not free: - # if the following assertion fails then that object's free_symbols - # method needs attention: if an expression is a number it cannot - # have free symbols - assert not free - return True - - # if we are only interested in some symbols and they are not in the - # free symbols then this expression is constant wrt those symbols - wrt = set(wrt) - if wrt and not wrt & free: - return True - wrt = wrt or free - - # simplify unless this has already been done - if simplify: - self = self.simplify() - - # is_zero should be a quick assumptions check; it can be wrong for - # numbers (see test_is_not_constant test), giving False when it - # shouldn't, but hopefully it will never give True unless it is sure. - if self.is_zero: - return True - - # try numerical evaluation to see if we get two different values - failing_number = None - if wrt == free: - # try 0 and 1 - a = self.subs(list(zip(free, [0]*len(free)))) - if a is S.NaN: - a = self._random(None, 0, 0, 0, 0) - if a is not None and a is not S.NaN: - b = self.subs(list(zip(free, [1]*len(free)))) - if b is S.NaN: - b = self._random(None, 1, 0, 1, 0) - if b is not None and b is not S.NaN: - if b.equals(a) is False: - return False - # try random real - b = self._random(None, -1, 0, 1, 0) - if b is not None and b is not S.NaN and b.equals(a) is False: - return False - # try random complex - b = self._random() - if b is not None and b is not S.NaN: - if a != b: - return False - failing_number = a if a.is_number else b - - # now we will test each wrt symbol (or all free symbols) to see if the - # expression depends on them or not using differentiation. This is - # not sufficient for all expressions, however, so we don't return - # False if we get a derivative other than 0 with free symbols. - for w in wrt: - deriv = self.diff(w).simplify() - if deriv != 0: - if not (deriv.is_Number or pure_complex(deriv)): - if flags.get('failing_number', False): - return failing_number - elif deriv.free_symbols: - # dead line provided _random returns None in such cases - return None - return False - return True -
    -
    [docs] def equals(self, other, failing_expression=False): - """Return True if self == other, False if it doesn't, or None. If - failing_expression is True then the expression which did not simplify - to a 0 will be returned instead of None. - - If ``self`` is a Number (or complex number) that is not zero, then - the result is False. - - If ``self`` is a number and has not evaluated to zero, evalf will be - used to test whether the expression evaluates to zero. If it does so - and the result has significance (i.e. the precision is either -1, for - a Rational result, or is greater than 1) then the evalf value will be - used to return True or False. - - """ - - other = sympify(other) - if self == other: - return True - - # they aren't the same so see if we can make the difference 0; - # don't worry about doing simplification steps one at a time - # because if the expression ever goes to 0 then the subsequent - # simplification steps that are done will be very fast. - diff = (self - other).as_content_primitive()[1] - diff = factor_terms(diff.simplify(), radical=True) - - if not diff: - return True - - if all(f.is_Atom for m in Add.make_args(diff) - for f in Mul.make_args(m)): - # if there is no expanding to be done after simplifying - # then this can't be a zero - return False - - constant = diff.is_constant(simplify=False, failing_number=True) - if constant is False or \ - not diff.free_symbols and not diff.is_number: - return False - elif constant is True: - ndiff = diff._random() - if ndiff: - return False - - # diff has not simplified to zero; constant is either None, True - # or the number with significance (prec != 1) that was randomly - # calculated twice as the same value. - if constant not in (True, None) and constant != 0: - return False - - if failing_expression: - return diff - return None -
    - def _eval_is_positive(self): - if self.is_number: - if self.is_real is False: - return False - try: - # check to see that we can get a value - n2 = self._eval_evalf(2) - except AttributeError: - n2 = None - if n2 is None: - return None - n, i = self.evalf(2).as_real_imag() - if not i.is_Number or not n.is_Number: - return False - if i: - if i._prec != 1: - return False - elif n._prec != 1: - if n > 0: - return True - return False - - def _eval_is_negative(self): - if self.is_number: - if self.is_real is False: - return False - try: - # check to see that we can get a value - n2 = self._eval_evalf(2) - except AttributeError: - n2 = None - if n2 is None: - return None - n, i = self.evalf(2).as_real_imag() - if not i.is_Number or not n.is_Number: - return False - if i: - if i._prec != 1: - return False - elif n._prec != 1: - if n < 0: - return True - return False - - def _eval_interval(self, x, a, b): - """ - Returns evaluation over an interval. For most functions this is: - - self.subs(x, b) - self.subs(x, a), - - possibly using limit() if NaN is returned from subs. - - If b or a is None, it only evaluates -self.subs(x, a) or self.subs(b, x), - respectively. - - """ - from sympy.series import limit - if (a is None and b is None): - raise ValueError('Both interval ends cannot be None.') - - if a is None: - A = 0 - else: - A = self.subs(x, a) - if A.has(S.NaN): - A = limit(self, x, a) - if A is S.NaN: - return A - - if b is None: - B = 0 - else: - B = self.subs(x, b) - if B.has(S.NaN): - B = limit(self, x, b) - - return B - A - - def _eval_power(self, other): - # subclass to compute self**other for cases when - # other is not NaN, 0, or 1 - return None - - def _eval_conjugate(self): - if self.is_real: - return self - elif self.is_imaginary: - return -self - - def conjugate(self): - from sympy.functions.elementary.complexes import conjugate as c - return c(self) - - def _eval_transpose(self): - from sympy.functions.elementary.complexes import conjugate - if self.is_complex: - return self - elif self.is_hermitian: - return conjugate(self) - elif self.is_antihermitian: - return -conjugate(self) - - def transpose(self): - from sympy.functions.elementary.complexes import transpose - return transpose(self) - - def _eval_adjoint(self): - from sympy.functions.elementary.complexes import conjugate, transpose - if self.is_hermitian: - return self - elif self.is_antihermitian: - return -self - obj = self._eval_conjugate() - if obj is not None: - return transpose(obj) - obj = self._eval_transpose() - if obj is not None: - return conjugate(obj) - - def adjoint(self): - from sympy.functions.elementary.complexes import adjoint - return adjoint(self) - - @classmethod - def _parse_order(cls, order): - """Parse and configure the ordering of terms. """ - from sympy.polys.monomialtools import monomial_key - - try: - reverse = order.startswith('rev-') - except AttributeError: - reverse = False - else: - if reverse: - order = order[4:] - - monom_key = monomial_key(order) - - def neg(monom): - result = [] - - for m in monom: - if isinstance(m, tuple): - result.append(neg(m)) - else: - result.append(-m) - - return tuple(result) - - def key(term): - _, ((re, im), monom, ncpart) = term - - monom = neg(monom_key(monom)) - ncpart = tuple([ e.sort_key(order=order) for e in ncpart ]) - coeff = ((bool(im), im), (re, im)) - - return monom, ncpart, coeff - - return key, reverse - -
    [docs] def as_ordered_factors(self, order=None): - """Return list of ordered factors (if Mul) else [self].""" - return [self] -
    -
    [docs] def as_ordered_terms(self, order=None, data=False): - """ - Transform an expression to an ordered list of terms. - - Examples - ======== - - >>> from sympy import sin, cos - >>> from sympy.abc import x, y - - >>> (sin(x)**2*cos(x) + sin(x)**2 + 1).as_ordered_terms() - [sin(x)**2*cos(x), sin(x)**2, 1] - - """ - key, reverse = self._parse_order(order) - terms, gens = self.as_terms() - - if not any(term.is_Order for term, _ in terms): - ordered = sorted(terms, key=key, reverse=reverse) - else: - _terms, _order = [], [] - - for term, repr in terms: - if not term.is_Order: - _terms.append((term, repr)) - else: - _order.append((term, repr)) - - ordered = sorted(_terms, key=key, reverse=True) \ - + sorted(_order, key=key, reverse=True) - - if data: - return ordered, gens - else: - return [ term for term, _ in ordered ] -
    -
    [docs] def as_terms(self): - """Transform an expression to a list of terms. """ - from sympy.core import Add, Mul, S - from sympy.core.exprtools import decompose_power - - gens, terms = set([]), [] - - for term in Add.make_args(self): - coeff, _term = term.as_coeff_Mul() - - coeff = complex(coeff) - cpart, ncpart = {}, [] - - if _term is not S.One: - for factor in Mul.make_args(_term): - if factor.is_number: - try: - coeff *= complex(factor) - except TypeError: - pass - else: - continue - - if factor.is_commutative: - base, exp = decompose_power(factor) - - cpart[base] = exp - gens.add(base) - else: - ncpart.append(factor) - - coeff = coeff.real, coeff.imag - ncpart = tuple(ncpart) - - terms.append((term, (coeff, cpart, ncpart))) - - gens = sorted(gens, key=default_sort_key) - - k, indices = len(gens), {} - - for i, g in enumerate(gens): - indices[g] = i - - result = [] - - for term, (coeff, cpart, ncpart) in terms: - monom = [0]*k - - for base, exp in cpart.items(): - monom[indices[base]] = exp - - result.append((term, (coeff, tuple(monom), ncpart))) - - return result, gens -
    -
    [docs] def removeO(self): - """Removes the additive O(..) symbol if there is one""" - return self -
    -
    [docs] def getO(self): - """Returns the additive O(..) symbol if there is one, else None.""" - return None -
    -
    [docs] def getn(self): - """ - Returns the order of the expression. - - The order is determined either from the O(...) term. If there - is no O(...) term, it returns None. - - Examples - ======== - - >>> from sympy import O - >>> from sympy.abc import x - >>> (1 + x + O(x**2)).getn() - 2 - >>> (1 + x).getn() - - """ - o = self.getO() - if o is None: - return None - elif o.is_Order: - o = o.expr - if o is S.One: - return S.Zero - if o.is_Symbol: - return S.One - if o.is_Pow: - return o.args[1] - if o.is_Mul: # x**n*log(x)**n or x**n/log(x)**n - for oi in o.args: - if oi.is_Symbol: - return S.One - if oi.is_Pow: - syms = oi.atoms(C.Symbol) - if len(syms) == 1: - x = syms.pop() - oi = oi.subs(x, C.Dummy('x', positive=True)) - if oi.base.is_Symbol and oi.exp.is_Rational: - return abs(oi.exp) - - raise NotImplementedError('not sure of order of %s' % o) -
    -
    [docs] def count_ops(self, visual=None): - """wrapper for count_ops that returns the operation count.""" - from .function import count_ops - return count_ops(self, visual) -
    -
    [docs] def args_cnc(self, cset=False, warn=True, split_1=True): - """Return [commutative factors, non-commutative factors] of self. - - self is treated as a Mul and the ordering of the factors is maintained. - If ``cset`` is True the commutative factors will be returned in a set. - If there were repeated factors (as may happen with an unevaluated Mul) - then an error will be raised unless it is explicitly supressed by - setting ``warn`` to False. - - Note: -1 is always separated from a Number unless split_1 is False. - - >>> from sympy import symbols, oo - >>> A, B = symbols('A B', commutative=0) - >>> x, y = symbols('x y') - >>> (-2*x*y).args_cnc() - [[-1, 2, x, y], []] - >>> (-2.5*x).args_cnc() - [[-1, 2.5, x], []] - >>> (-2*x*A*B*y).args_cnc() - [[-1, 2, x, y], [A, B]] - >>> (-2*x*A*B*y).args_cnc(split_1=False) - [[-2, x, y], [A, B]] - >>> (-2*x*y).args_cnc(cset=True) - [set([-1, 2, x, y]), []] - - The arg is always treated as a Mul: - - >>> (-2 + x + A).args_cnc() - [[], [x - 2 + A]] - >>> (-oo).args_cnc() # -oo is a singleton - [[-1, oo], []] - """ - - if self.is_Mul: - args = list(self.args) - else: - args = [self] - for i, mi in enumerate(args): - if not mi.is_commutative: - c = args[:i] - nc = args[i:] - break - else: - c = args - nc = [] - - if c and split_1 and ( - c[0].is_Number and - c[0].is_negative and - c[0] != S.NegativeOne): - c[:1] = [S.NegativeOne, -c[0]] - - if cset: - clen = len(c) - c = set(c) - if clen and warn and len(c) != clen: - raise ValueError('repeated commutative arguments: %s' % - [ci for ci in c if list(self.args).count(ci) > 1]) - return [c, nc] -
    -
    [docs] def coeff(self, x, n=1, right=False): - """ - Returns the coefficient from the term containing "x**n" or None if - there is no such term. If ``n`` is zero then all terms independent of - x will be returned. - - When x is noncommutative, the coeff to the left (default) or right of x - can be returned. The keyword 'right' is ignored when x is commutative. - - See Also - ======== - - as_coefficient - - Examples - ======== - - >>> from sympy import symbols - >>> from sympy.abc import x, y, z - - You can select terms that have an explicit negative in front of them: - - >>> (-x + 2*y).coeff(-1) - x - >>> (x - 2*y).coeff(-1) - 2*y - - You can select terms with no Rational coefficient: - - >>> (x + 2*y).coeff(1) - x - >>> (3 + 2*x + 4*x**2).coeff(1) - 0 - - You can select terms independent of x by making n=0; in this case - expr.as_independent(x)[0] is returned (and 0 will be returned instead - of None): - - >>> (3 + 2*x + 4*x**2).coeff(x, 0) - 3 - >>> eq = ((x + 1)**3).expand() + 1 - >>> eq - x**3 + 3*x**2 + 3*x + 2 - >>> [eq.coeff(x, i) for i in reversed(list(range(4)))] - [1, 3, 3, 2] - >>> eq -= 2 - >>> [eq.coeff(x, i) for i in reversed(list(range(4)))] - [1, 3, 3, 0] - - You can select terms that have a numerical term in front of them: - - >>> (-x - 2*y).coeff(2) - -y - >>> from sympy import sqrt - >>> (x + sqrt(2)*x).coeff(sqrt(2)) - x - - The matching is exact: - - >>> (3 + 2*x + 4*x**2).coeff(x) - 2 - >>> (3 + 2*x + 4*x**2).coeff(x**2) - 4 - >>> (3 + 2*x + 4*x**2).coeff(x**3) - 0 - >>> (z*(x + y)**2).coeff((x + y)**2) - z - >>> (z*(x + y)**2).coeff(x + y) - 0 - - In addition, no factoring is done, so 1 + z*(1 + y) is not obtained - from the following: - - >>> (x + z*(x + x*y)).coeff(x) - 1 - - If such factoring is desired, factor_terms can be used first: - - >>> from sympy import factor_terms - >>> factor_terms(x + z*(x + x*y)).coeff(x) - z*(y + 1) + 1 - - >>> n, m, o = symbols('n m o', commutative=False) - >>> n.coeff(n) - 1 - >>> (3*n).coeff(n) - 3 - >>> (n*m + m*n*m).coeff(n) # = (1 + m)*n*m - 1 + m - >>> (n*m + m*n*m).coeff(n, right=True) # = (1 + m)*n*m - m - - If there is more than one possible coefficient 0 is returned: - - >>> (n*m + m*n).coeff(n) - 0 - - If there is only one possible coefficient, it is returned: - - >>> (n*m + x*m*n).coeff(m*n) - x - >>> (n*m + x*m*n).coeff(m*n, right=1) - 1 - - See Also - ======== - - as_coeff_Add: a method to separate the additive constant from an expression - as_coeff_Mul: a method to separate the multiplicative constant from an expression - as_independent: a method to separate x dependent terms/factors from others - - """ - x = sympify(x) - if not isinstance(x, Basic): - return S.Zero - - n = as_int(n) - - if not x: - return S.Zero - - if x == self: - if n == 1: - return S.One - return S.Zero - - if x is S.One: - co = [a for a in Add.make_args(self) - if a.as_coeff_Mul()[0] is S.One] - if not co: - return S.Zero - return Add(*co) - - if n == 0: - if x.is_Add and self.is_Add: - c = self.coeff(x, right=right) - if not c: - return S.Zero - if not right: - return self - Add(*[a*x for a in Add.make_args(c)]) - return self - Add(*[x*a for a in Add.make_args(c)]) - return self.as_independent(x, as_Add=not self.is_Mul)[0] - - # continue with the full method, looking for this power of x: - x = x**n - - def incommon(l1, l2): - if not l1 or not l2: - return [] - n = min(len(l1), len(l2)) - for i in range(n): - if l1[i] != l2[i]: - return l1[:i] - return l1[:] - - def find(l, sub, first=True): - """ Find where list sub appears in list l. When ``first`` is True - the first occurance from the left is returned, else the last - occurance is returned. Return None if sub is not in l. - - >> l = range(5)*2 - >> find(l, [2, 3]) - 2 - >> find(l, [2, 3], first=0) - 7 - >> find(l, [2, 4]) - None - - """ - if not sub or not l or len(sub) > len(l): - return None - n = len(sub) - if not first: - l.reverse() - sub.reverse() - for i in range(0, len(l) - n + 1): - if all(l[i + j] == sub[j] for j in range(n)): - break - else: - i = None - if not first: - l.reverse() - sub.reverse() - if i is not None and not first: - i = len(l) - (i + n) - return i - - co = [] - args = Add.make_args(self) - self_c = self.is_commutative - x_c = x.is_commutative - if self_c and not x_c: - return S.Zero - - if self_c: - xargs = x.args_cnc(cset=True, warn=False)[0] - for a in args: - margs = a.args_cnc(cset=True, warn=False)[0] - if len(xargs) > len(margs): - continue - resid = margs.difference(xargs) - if len(resid) + len(xargs) == len(margs): - co.append(Mul(*resid)) - if co == []: - return S.Zero - elif co: - return Add(*co) - elif x_c: - xargs = x.args_cnc(cset=True, warn=False)[0] - for a in args: - margs, nc = a.args_cnc(cset=True) - if len(xargs) > len(margs): - continue - resid = margs.difference(xargs) - if len(resid) + len(xargs) == len(margs): - co.append(Mul(*(list(resid) + nc))) - if co == []: - return S.Zero - elif co: - return Add(*co) - else: # both nc - xargs, nx = x.args_cnc(cset=True) - # find the parts that pass the commutative terms - for a in args: - margs, nc = a.args_cnc(cset=True) - if len(xargs) > len(margs): - continue - resid = margs.difference(xargs) - if len(resid) + len(xargs) == len(margs): - co.append((resid, nc)) - # now check the non-comm parts - if not co: - return S.Zero - if all(n == co[0][1] for r, n in co): - ii = find(co[0][1], nx, right) - if ii is not None: - if not right: - return Mul(Add(*[Mul(*r) for r, c in co]), Mul(*co[0][1][:ii])) - else: - return Mul(*co[0][1][ii + len(nx):]) - beg = reduce(incommon, (n[1] for n in co)) - if beg: - ii = find(beg, nx, right) - if ii is not None: - if not right: - gcdc = co[0][0] - for i in range(1, len(co)): - gcdc = gcdc.intersection(co[i][0]) - if not gcdc: - break - return Mul(*(list(gcdc) + beg[:ii])) - else: - m = ii + len(nx) - return Add(*[Mul(*(list(r) + n[m:])) for r, n in co]) - end = list(reversed( - reduce(incommon, (list(reversed(n[1])) for n in co)))) - if end: - ii = find(end, nx, right) - if ii is not None: - if not right: - return Add(*[Mul(*(list(r) + n[:-len(end) + ii])) for r, n in co]) - else: - return Mul(*end[ii + len(nx):]) - # look for single match - hit = None - for i, (r, n) in enumerate(co): - ii = find(n, nx, right) - if ii is not None: - if not hit: - hit = ii, r, n - else: - break - else: - if hit: - ii, r, n = hit - if not right: - return Mul(*(list(r) + n[:ii])) - else: - return Mul(*n[ii + len(nx):]) - - return S.Zero -
    -
    [docs] def as_expr(self, *gens): - """ - Convert a polynomial to a SymPy expression. - - Examples - ======== - - >>> from sympy import sin - >>> from sympy.abc import x, y - - >>> f = (x**2 + x*y).as_poly(x, y) - >>> f.as_expr() - x**2 + x*y - - >>> sin(x).as_expr() - sin(x) - - """ - return self -
    -
    [docs] def as_coefficient(self, expr): - """ - Extracts symbolic coefficient at the given expression. In - other words, this functions separates 'self' into the product - of 'expr' and 'expr'-free coefficient. If such separation - is not possible it will return None. - - See Also - ======== - - coeff - - Examples - ======== - - >>> from sympy import E, pi, sin, I, symbols - >>> from sympy.abc import x, y - - >>> E.as_coefficient(E) - 1 - >>> (2*E).as_coefficient(E) - 2 - >>> (2*sin(E)*E).as_coefficient(E) - - >>> (2*E + x*E).as_coefficient(E) - x + 2 - >>> (2*E*x + x).as_coefficient(E) - - >>> (E*(x + 1) + x).as_coefficient(E) - - >>> (2*pi*I).as_coefficient(pi*I) - 2 - >>> (2*I).as_coefficient(pi*I) - - """ - - r = self.extract_multiplicatively(expr) - if r and not r.has(expr): - return r -
    -
    [docs] def as_independent(self, *deps, **hint): - """ - A mostly naive separation of a Mul or Add into arguments that are not - are dependent on deps. To obtain as complete a separation of variables - as possible, use a separation method first, e.g.: - - * separatevars() to change Mul, Add and Pow (including exp) into Mul - * .expand(mul=True) to change Add or Mul into Add - * .expand(log=True) to change log expr into an Add - - The only non-naive thing that is done here is to respect noncommutative - ordering of variables. - - The returned tuple (i, d) has the following interpretation: - - * i will has no variable that appears in deps - * d will be 1 or else have terms that contain variables that are in deps - * if self is an Add then self = i + d - * if self is a Mul then self = i*d - * if self is anything else, either tuple (self, S.One) or (S.One, self) - is returned. - - To force the expression to be treated as an Add, use the hint as_Add=True - - Examples - ======== - - -- self is an Add - - >>> from sympy import sin, cos, exp - >>> from sympy.abc import x, y, z - - >>> (x + x*y).as_independent(x) - (0, x*y + x) - >>> (x + x*y).as_independent(y) - (x, x*y) - >>> (2*x*sin(x) + y + x + z).as_independent(x) - (y + z, 2*x*sin(x) + x) - >>> (2*x*sin(x) + y + x + z).as_independent(x, y) - (z, 2*x*sin(x) + x + y) - - -- self is a Mul - - >>> (x*sin(x)*cos(y)).as_independent(x) - (cos(y), x*sin(x)) - - non-commutative terms cannot always be separated out when self is a Mul - - >>> from sympy import symbols - >>> n1, n2, n3 = symbols('n1 n2 n3', commutative=False) - >>> (n1 + n1*n2).as_independent(n2) - (n1, n1*n2) - >>> (n2*n1 + n1*n2).as_independent(n2) - (0, n1*n2 + n2*n1) - >>> (n1*n2*n3).as_independent(n1) - (1, n1*n2*n3) - >>> (n1*n2*n3).as_independent(n2) - (n1, n2*n3) - >>> ((x-n1)*(x-y)).as_independent(x) - (1, (x - y)*(x - n1)) - - -- self is anything else: - - >>> (sin(x)).as_independent(x) - (1, sin(x)) - >>> (sin(x)).as_independent(y) - (sin(x), 1) - >>> exp(x+y).as_independent(x) - (1, exp(x + y)) - - -- force self to be treated as an Add: - - >>> (3*x).as_independent(x, as_Add=True) - (0, 3*x) - - -- force self to be treated as a Mul: - - >>> (3+x).as_independent(x, as_Add=False) - (1, x + 3) - >>> (-3+x).as_independent(x, as_Add=False) - (1, x - 3) - - Note how the below differs from the above in making the - constant on the dep term positive. - - >>> (y*(-3+x)).as_independent(x) - (y, x - 3) - - -- use .as_independent() for true independence testing instead - of .has(). The former considers only symbols in the free - symbols while the latter considers all symbols - - >>> from sympy import Integral - >>> I = Integral(x, (x, 1, 2)) - >>> I.has(x) - True - >>> x in I.free_symbols - False - >>> I.as_independent(x) == (I, 1) - True - >>> (I + x).as_independent(x) == (I, x) - True - - Note: when trying to get independent terms, a separation method - might need to be used first. In this case, it is important to keep - track of what you send to this routine so you know how to interpret - the returned values - - >>> from sympy import separatevars, log - >>> separatevars(exp(x+y)).as_independent(x) - (exp(y), exp(x)) - >>> (x + x*y).as_independent(y) - (x, x*y) - >>> separatevars(x + x*y).as_independent(y) - (x, y + 1) - >>> (x*(1 + y)).as_independent(y) - (x, y + 1) - >>> (x*(1 + y)).expand(mul=True).as_independent(y) - (x, x*y) - >>> a, b=symbols('a b',positive=True) - >>> (log(a*b).expand(log=True)).as_independent(b) - (log(a), log(b)) - - See also: .separatevars(), .expand(log=True), - .as_two_terms(), .as_coeff_add(), .as_coeff_mul() - """ - from sympy.utilities.iterables import sift - - func = self.func - # sift out deps into symbolic and other and ignore - # all symbols but those that are in the free symbols - sym = set() - other = [] - for d in deps: - if isinstance(d, C.Symbol): # Symbol.is_Symbol is True - sym.add(d) - else: - other.append(d) - - def has(e): - """return the standard has() if there are no literal symbols, else - check to see that symbol-deps are in the free symbols.""" - has_other = e.has(*other) - if not sym: - return has_other - return has_other or e.has(*(e.free_symbols & sym)) - - if hint.get('as_Add', func is Add): - want = Add - else: - want = Mul - if (want is not func or - func is not Add and func is not Mul): - if has(self): - return (want.identity, self) - else: - return (self, want.identity) - else: - if func is Add: - args = list(self.args) - else: - args, nc = self.args_cnc() - - d = sift(args, lambda x: has(x)) - depend = d[True] - indep = d[False] - if func is Add: # all terms were treated as commutative - return (Add(*indep), - Add(*depend)) - else: # handle noncommutative by stopping at first dependent term - for i, n in enumerate(nc): - if has(n): - depend.extend(nc[i:]) - break - indep.append(n) - return Mul(*indep), Mul(*depend) -
    -
    [docs] def as_real_imag(self, deep=True, **hints): - """Performs complex expansion on 'self' and returns a tuple - containing collected both real and imaginary parts. This - method can't be confused with re() and im() functions, - which does not perform complex expansion at evaluation. - - However it is possible to expand both re() and im() - functions and get exactly the same results as with - a single call to this function. - - >>> from sympy import symbols, I - - >>> x, y = symbols('x,y', real=True) - - >>> (x + y*I).as_real_imag() - (x, y) - - >>> from sympy.abc import z, w - - >>> (z + w*I).as_real_imag() - (re(z) - im(w), re(w) + im(z)) - - """ - if hints.get('ignore') == self: - return None - else: - return (C.re(self), C.im(self)) -
    -
    [docs] def as_powers_dict(self): - """Return self as a dictionary of factors with each factor being - treated as a power. The keys are the bases of the factors and the - values, the corresponding exponents. The resulting dictionary should - be used with caution if the expression is a Mul and contains non- - commutative factors since the order that they appeared will be lost in - the dictionary.""" - d = defaultdict(int) - d.update(dict([self.as_base_exp()])) - return d -
    -
    [docs] def as_coefficients_dict(self): - """Return a dictionary mapping terms to their Rational coefficient. - Since the dictionary is a defaultdict, inquiries about terms which - were not present will return a coefficient of 0. If an expression is - not an Add it is considered to have a single term. - - Examples - ======== - - >>> from sympy.abc import a, x - >>> (3*x + a*x + 4).as_coefficients_dict() - {1: 4, x: 3, a*x: 1} - >>> _[a] - 0 - >>> (3*a*x).as_coefficients_dict() - {a*x: 3} - - """ - c, m = self.as_coeff_Mul() - if not c.is_Rational: - c = S.One - m = self - d = defaultdict(int) - d.update({m: c}) - return d -
    - def as_base_exp(self): - # a -> b ** e - return self, S.One - -
    [docs] def as_coeff_terms(self, *deps): - """ - This method is deprecated. Use .as_coeff_mul() instead. - """ - from sympy.utilities.exceptions import SymPyDeprecationWarning - SymPyDeprecationWarning(feature="as_coeff_terms()", - useinstead="as_coeff_mul()", issue=3377, - deprecated_since_version="0.7.0").warn() - return self.as_coeff_mul(*deps) -
    -
    [docs] def as_coeff_factors(self, *deps): - """ - This method is deprecated. Use .as_coeff_add() instead. - """ - from sympy.utilities.exceptions import SymPyDeprecationWarning - SymPyDeprecationWarning(feature="as_coeff_factors()", - useinstead="as_coeff_add()", issue=3377, - deprecated_since_version="0.7.0").warn() - return self.as_coeff_add(*deps) -
    -
    [docs] def as_coeff_mul(self, *deps): - """Return the tuple (c, args) where self is written as a Mul, ``m``. - - c should be a Rational multiplied by any terms of the Mul that are - independent of deps. - - args should be a tuple of all other terms of m; args is empty - if self is a Number or if self is independent of deps (when given). - - This should be used when you don't know if self is a Mul or not but - you want to treat self as a Mul or if you want to process the - individual arguments of the tail of self as a Mul. - - - if you know self is a Mul and want only the head, use self.args[0]; - - if you don't want to process the arguments of the tail but need the - tail then use self.as_two_terms() which gives the head and tail; - - if you want to split self into an independent and dependent parts - use ``self.as_independent(*deps)`` - - >>> from sympy import S - >>> from sympy.abc import x, y - >>> (S(3)).as_coeff_mul() - (3, ()) - >>> (3*x*y).as_coeff_mul() - (3, (x, y)) - >>> (3*x*y).as_coeff_mul(x) - (3*y, (x,)) - >>> (3*y).as_coeff_mul(x) - (3*y, ()) - """ - if deps: - if not self.has(*deps): - return self, tuple() - return S.One, (self,) -
    -
    [docs] def as_coeff_add(self, *deps): - """Return the tuple (c, args) where self is written as an Add, ``a``. - - c should be a Rational added to any terms of the Add that are - independent of deps. - - args should be a tuple of all other terms of ``a``; args is empty - if self is a Number or if self is independent of deps (when given). - - This should be used when you don't know if self is an Add or not but - you want to treat self as an Add or if you want to process the - individual arguments of the tail of self as an Add. - - - if you know self is an Add and want only the head, use self.args[0]; - - if you don't want to process the arguments of the tail but need the - tail then use self.as_two_terms() which gives the head and tail. - - if you want to split self into an independent and dependent parts - use ``self.as_independent(*deps)`` - - >>> from sympy import S - >>> from sympy.abc import x, y - >>> (S(3)).as_coeff_add() - (3, ()) - >>> (3 + x).as_coeff_add() - (3, (x,)) - >>> (3 + x + y).as_coeff_add(x) - (y + 3, (x,)) - >>> (3 + y).as_coeff_add(x) - (y + 3, ()) - - """ - if deps: - if not self.has(*deps): - return self, tuple() - return S.Zero, (self,) -
    -
    [docs] def primitive(self): - """Return the positive Rational that can be extracted non-recursively - from every term of self (i.e., self is treated like an Add). This is - like the as_coeff_Mul() method but primitive always extracts a positive - Rational (never a negative or a Float). - - Examples - ======== - - >>> from sympy.abc import x - >>> (3*(x + 1)**2).primitive() - (3, (x + 1)**2) - >>> a = (6*x + 2); a.primitive() - (2, 3*x + 1) - >>> b = (x/2 + 3); b.primitive() - (1/2, x + 6) - >>> (a*b).primitive() == (1, a*b) - True - """ - if not self: - return S.One, S.Zero - c, r = self.as_coeff_Mul(rational=True) - if c.is_negative: - c, r = -c, -r - return c, r -
    -
    [docs] def as_content_primitive(self, radical=False): - """This method should recursively remove a Rational from all arguments - and return that (content) and the new self (primitive). The content - should always be positive and ``Mul(*foo.as_content_primitive()) == foo``. - The primitive need no be in canonical form and should try to preserve - the underlying structure if possible (i.e. expand_mul should not be - applied to self). - - Examples - ======== - - >>> from sympy import sqrt - >>> from sympy.abc import x, y, z - - >>> eq = 2 + 2*x + 2*y*(3 + 3*y) - - The as_content_primitive function is recursive and retains structure: - - >>> eq.as_content_primitive() - (2, x + 3*y*(y + 1) + 1) - - Integer powers will have Rationals extracted from the base: - - >>> ((2 + 6*x)**2).as_content_primitive() - (4, (3*x + 1)**2) - >>> ((2 + 6*x)**(2*y)).as_content_primitive() - (1, (2*(3*x + 1))**(2*y)) - - Terms may end up joining once their as_content_primitives are added: - - >>> ((5*(x*(1 + y)) + 2*x*(3 + 3*y))).as_content_primitive() - (11, x*(y + 1)) - >>> ((3*(x*(1 + y)) + 2*x*(3 + 3*y))).as_content_primitive() - (9, x*(y + 1)) - >>> ((3*(z*(1 + y)) + 2.0*x*(3 + 3*y))).as_content_primitive() - (1, 6.0*x*(y + 1) + 3*z*(y + 1)) - >>> ((5*(x*(1 + y)) + 2*x*(3 + 3*y))**2).as_content_primitive() - (121, x**2*(y + 1)**2) - >>> ((5*(x*(1 + y)) + 2.0*x*(3 + 3*y))**2).as_content_primitive() - (1, 121.0*x**2*(y + 1)**2) - - Radical content can also be factored out of the primitive: - - >>> (2*sqrt(2) + 4*sqrt(10)).as_content_primitive(radical=True) - (2, sqrt(2)*(1 + 2*sqrt(5))) - - """ - return S.One, self -
    -
    [docs] def as_numer_denom(self): - """ expression -> a/b -> a, b - - This is just a stub that should be defined by - an object's class methods to get anything else. - - See Also - ======== - normal: return a/b instead of a, b - """ - - return self, S.One -
    - def normal(self): - n, d = self.as_numer_denom() - if d is S.One: - return n - return n/d - -
    [docs] def extract_multiplicatively(self, c): - """Return None if it's not possible to make self in the form - c * something in a nice way, i.e. preserving the properties - of arguments of self. - - >>> from sympy import symbols, Rational - - >>> x, y = symbols('x,y', real=True) - - >>> ((x*y)**3).extract_multiplicatively(x**2 * y) - x*y**2 - - >>> ((x*y)**3).extract_multiplicatively(x**4 * y) - - >>> (2*x).extract_multiplicatively(2) - x - - >>> (2*x).extract_multiplicatively(3) - - >>> (Rational(1,2)*x).extract_multiplicatively(3) - x/6 - - """ - c = sympify(c) - if c is S.One: - return self - elif c == self: - return S.One - if c.is_Add: - cc, pc = c.primitive() - if cc is not S.One: - c = Mul(cc, pc, evaluate=False) - if c.is_Mul: - a, b = c.as_two_terms() - x = self.extract_multiplicatively(a) - if x is not None: - return x.extract_multiplicatively(b) - quotient = self / c - if self.is_Number: - if self is S.Infinity: - if c.is_positive: - return S.Infinity - elif self is S.NegativeInfinity: - if c.is_negative: - return S.Infinity - elif c.is_positive: - return S.NegativeInfinity - elif self is S.ComplexInfinity: - if not c.is_zero: - return S.ComplexInfinity - elif self is S.NaN: - return S.NaN - elif self.is_Integer: - if not quotient.is_Integer: - return None - elif self.is_positive and quotient.is_negative: - return None - else: - return quotient - elif self.is_Rational: - if not quotient.is_Rational: - return None - elif self.is_positive and quotient.is_negative: - return None - else: - return quotient - elif self.is_Float: - if not quotient.is_Float: - return None - elif self.is_positive and quotient.is_negative: - return None - else: - return quotient - elif self.is_NumberSymbol or self.is_Symbol or self is S.ImaginaryUnit: - if quotient.is_Mul and len(quotient.args) == 2: - if quotient.args[0].is_Integer and quotient.args[0].is_positive and quotient.args[1] == self: - return quotient - elif quotient.is_Integer: - return quotient - elif self.is_Add: - cs, ps = self.primitive() - if cs is not S.One: - return Mul(cs, ps, evaluate=False).extract_multiplicatively(c) - newargs = [] - for arg in self.args: - newarg = arg.extract_multiplicatively(c) - if newarg is not None: - newargs.append(newarg) - else: - return None - return Add(*newargs) - elif self.is_Mul: - args = list(self.args) - for i, arg in enumerate(args): - newarg = arg.extract_multiplicatively(c) - if newarg is not None: - args[i] = newarg - return Mul(*args) - elif self.is_Pow: - if c.is_Pow and c.base == self.base: - new_exp = self.exp.extract_additively(c.exp) - if new_exp is not None: - return self.base ** (new_exp) - elif c == self.base: - new_exp = self.exp.extract_additively(1) - if new_exp is not None: - return self.base ** (new_exp) -
    -
    [docs] def extract_additively(self, c): - """Return self - c if it's possible to subtract c from self and - make all matching coefficients move towards zero, else return None. - - Examples - ======== - >>> from sympy import S - >>> from sympy.abc import x, y - >>> e = 2*x + 3 - >>> e.extract_additively(x + 1) - x + 2 - >>> e.extract_additively(3*x) - >>> e.extract_additively(4) - >>> (y*(x + 1)).extract_additively(x + 1) - >>> ((x + 1)*(x + 2*y + 1) + 3).extract_additively(x + 1) - (x + 1)*(x + 2*y) + 3 - - Sometimes auto-expansion will return a less simplified result - than desired; gcd_terms might be used in such cases: - - >>> from sympy import gcd_terms - >>> (4*x*(y + 1) + y).extract_additively(x) - 4*x*(y + 1) + x*(4*y + 3) - x*(4*y + 4) + y - >>> gcd_terms(_) - x*(4*y + 3) + y - - See Also - ======== - extract_multiplicatively - coeff - as_coefficient - - """ - - c = sympify(c) - if c is S.Zero: - return self - elif c == self: - return S.Zero - elif self is S.Zero: - return None - - if self.is_Number: - if not c.is_Number: - return None - co = self - diff = co - c - # XXX should we match types? i.e should 3 - .1 succeed? - if (co > 0 and diff > 0 and diff < co or - co < 0 and diff < 0 and diff > co): - return diff - return None - - if c.is_Number: - co, t = self.as_coeff_Add() - xa = co.extract_additively(c) - if xa is None: - return None - return xa + t - - # handle the args[0].is_Number case separately - # since we will have trouble looking for the coeff of - # a number. - if c.is_Add and c.args[0].is_Number: - # whole term as a term factor - co = self.coeff(c) - xa0 = (co.extract_additively(1) or 0)*c - if xa0: - diff = self - co*c - return (xa0 + (diff.extract_additively(c) or diff)) or None - # term-wise - h, t = c.as_coeff_Add() - sh, st = self.as_coeff_Add() - xa = sh.extract_additively(h) - if xa is None: - return None - xa2 = st.extract_additively(t) - if xa2 is None: - return None - return xa + xa2 - - # whole term as a term factor - co = self.coeff(c) - xa0 = (co.extract_additively(1) or 0)*c - if xa0: - diff = self - co*c - return (xa0 + (diff.extract_additively(c) or diff)) or None - # term-wise - coeffs = [] - for a in Add.make_args(c): - ac, at = a.as_coeff_Mul() - co = self.coeff(at) - if not co: - return None - coc, cot = co.as_coeff_Add() - xa = coc.extract_additively(ac) - if xa is None: - return None - self -= co*at - coeffs.append((cot + xa)*at) - coeffs.append(self) - return Add(*coeffs) -
    -
    [docs] def could_extract_minus_sign(self): - """Canonical way to choose an element in the set {e, -e} where - e is any expression. If the canonical element is e, we have - e.could_extract_minus_sign() == True, else - e.could_extract_minus_sign() == False. - - For any expression, the set ``{e.could_extract_minus_sign(), - (-e).could_extract_minus_sign()}`` must be ``{True, False}``. - - >>> from sympy.abc import x, y - >>> (x-y).could_extract_minus_sign() != (y-x).could_extract_minus_sign() - True - - """ - negative_self = -self - self_has_minus = (self.extract_multiplicatively(-1) is not None) - negative_self_has_minus = ( - (negative_self).extract_multiplicatively(-1) is not None) - if self_has_minus != negative_self_has_minus: - return self_has_minus - else: - if self.is_Add: - # We choose the one with less arguments with minus signs - all_args = len(self.args) - negative_args = len([False for arg in self.args if arg.could_extract_minus_sign()]) - positive_args = all_args - negative_args - if positive_args > negative_args: - return False - elif positive_args < negative_args: - return True - elif self.is_Mul: - # We choose the one with an odd number of minus signs - num, den = self.as_numer_denom() - args = Mul.make_args(num) + Mul.make_args(den) - arg_signs = [arg.could_extract_minus_sign() for arg in args] - negative_args = [_f for _f in arg_signs if _f] - return len(negative_args) % 2 == 1 - - # As a last resort, we choose the one with greater value of .sort_key() - return self.sort_key() < negative_self.sort_key() -
    -
    [docs] def extract_branch_factor(self, allow_half=False): - """ - Try to write self as ``exp_polar(2*pi*I*n)*z`` in a nice way. - Return (z, n). - - >>> from sympy import exp_polar, I, pi - >>> from sympy.abc import x, y - >>> exp_polar(I*pi).extract_branch_factor() - (exp_polar(I*pi), 0) - >>> exp_polar(2*I*pi).extract_branch_factor() - (1, 1) - >>> exp_polar(-pi*I).extract_branch_factor() - (exp_polar(I*pi), -1) - >>> exp_polar(3*pi*I + x).extract_branch_factor() - (exp_polar(x + I*pi), 1) - >>> (y*exp_polar(-5*pi*I)*exp_polar(3*pi*I + 2*pi*x)).extract_branch_factor() - (y*exp_polar(2*pi*x), -1) - >>> exp_polar(-I*pi/2).extract_branch_factor() - (exp_polar(-I*pi/2), 0) - - If allow_half is True, also extract exp_polar(I*pi): - - >>> exp_polar(I*pi).extract_branch_factor(allow_half=True) - (1, 1/2) - >>> exp_polar(2*I*pi).extract_branch_factor(allow_half=True) - (1, 1) - >>> exp_polar(3*I*pi).extract_branch_factor(allow_half=True) - (1, 3/2) - >>> exp_polar(-I*pi).extract_branch_factor(allow_half=True) - (1, -1/2) - """ - from sympy import exp_polar, pi, I, ceiling, Add - n = S(0) - res = S(1) - args = Mul.make_args(self) - exps = [] - for arg in args: - if arg.func is exp_polar: - exps += [arg.exp] - else: - res *= arg - piimult = S(0) - extras = [] - while exps: - exp = exps.pop() - if exp.is_Add: - exps += exp.args - continue - if exp.is_Mul: - coeff = exp.as_coefficient(pi*I) - if coeff is not None: - piimult += coeff - continue - extras += [exp] - if not piimult.free_symbols: - coeff = piimult - tail = () - else: - coeff, tail = piimult.as_coeff_add(*piimult.free_symbols) - # round down to nearest multiple of 2 - branchfact = ceiling(coeff/2 - S(1)/2)*2 - n += branchfact/2 - c = coeff - branchfact - if allow_half: - nc = c.extract_additively(1) - if nc is not None: - n += S(1)/2 - c = nc - newexp = pi*I*Add(*((c, ) + tail)) + Add(*extras) - if newexp != 0: - res *= exp_polar(newexp) - return res, n -
    - def _eval_is_polynomial(self, syms): - if self.free_symbols.intersection(syms) == set([]): - return True - return False - -
    [docs] def is_polynomial(self, *syms): - """ - Return True if self is a polynomial in syms and False otherwise. - - This checks if self is an exact polynomial in syms. This function - returns False for expressions that are "polynomials" with symbolic - exponents. Thus, you should be able to apply polynomial algorithms to - expressions for which this returns True, and Poly(expr, \*syms) should - work only if and only if expr.is_polynomial(\*syms) returns True. The - polynomial does not have to be in expanded form. If no symbols are - given, all free symbols in the expression will be used. - - This is not part of the assumptions system. You cannot do - Symbol('z', polynomial=True). - - Examples - ======== - - >>> from sympy import Symbol - >>> x = Symbol('x') - >>> ((x**2 + 1)**4).is_polynomial(x) - True - >>> ((x**2 + 1)**4).is_polynomial() - True - >>> (2**x + 1).is_polynomial(x) - False - - - >>> n = Symbol('n', nonnegative=True, integer=True) - >>> (x**n + 1).is_polynomial(x) - False - - This function does not attempt any nontrivial simplifications that may - result in an expression that does not appear to be a polynomial to - become one. - - >>> from sympy import sqrt, factor, cancel - >>> y = Symbol('y', positive=True) - >>> a = sqrt(y**2 + 2*y + 1) - >>> a.is_polynomial(y) - False - >>> factor(a) - y + 1 - >>> factor(a).is_polynomial(y) - True - - >>> b = (y**2 + 2*y + 1)/(y + 1) - >>> b.is_polynomial(y) - False - >>> cancel(b) - y + 1 - >>> cancel(b).is_polynomial(y) - True - - See also .is_rational_function() - - """ - if syms: - syms = set(map(sympify, syms)) - else: - syms = self.free_symbols - - if syms.intersection(self.free_symbols) == set([]): - # constant polynomial - return True - else: - return self._eval_is_polynomial(syms) -
    - def _eval_is_rational_function(self, syms): - if self.free_symbols.intersection(syms) == set([]): - return True - return False - -
    [docs] def is_rational_function(self, *syms): - """ - Test whether function is a ratio of two polynomials in the given - symbols, syms. When syms is not given, all free symbols will be used. - The rational function does not have to be in expanded or in any kind of - canonical form. - - This function returns False for expressions that are "rational - functions" with symbolic exponents. Thus, you should be able to call - .as_numer_denom() and apply polynomial algorithms to the result for - expressions for which this returns True. - - This is not part of the assumptions system. You cannot do - Symbol('z', rational_function=True). - - Examples - ======== - - >>> from sympy import Symbol, sin - >>> from sympy.abc import x, y - - >>> (x/y).is_rational_function() - True - - >>> (x**2).is_rational_function() - True - - >>> (x/sin(y)).is_rational_function(y) - False - - >>> n = Symbol('n', integer=True) - >>> (x**n + 1).is_rational_function(x) - False - - This function does not attempt any nontrivial simplifications that may - result in an expression that does not appear to be a rational function - to become one. - - >>> from sympy import sqrt, factor, cancel - >>> y = Symbol('y', positive=True) - >>> a = sqrt(y**2 + 2*y + 1)/y - >>> a.is_rational_function(y) - False - >>> factor(a) - (y + 1)/y - >>> factor(a).is_rational_function(y) - True - - See also is_rational_function(). - - """ - if syms: - syms = set(map(sympify, syms)) - else: - syms = self.free_symbols - - if syms.intersection(self.free_symbols) == set([]): - # constant rational function - return True - else: - return self._eval_is_rational_function(syms) - - ################################################################################### - ##################### SERIES, LEADING TERM, LIMIT, ORDER METHODS ################## - ################################################################################### -
    -
    [docs] def series(self, x=None, x0=0, n=6, dir="+"): - """ - Series expansion of "self" around ``x = x0`` yielding either terms of - the series one by one (the lazy series given when n=None), else - all the terms at once when n != None. - - Note: when n != None, if an O() term is returned then the x in the - in it and the entire expression represents x - x0, the displacement - from x0. (If there is no O() term then the series was exact and x has - it's normal meaning.) This is currently necessary since sympy's O() - can only represent terms at x0=0. So instead of:: - - cos(x).series(x0=1, n=2) --> (1 - x)*sin(1) + cos(1) + O((x - 1)**2) - - which graphically looks like this:: - - | - .|. . . - . | \ . . - ---+---------------------- - | . . . . - | \ - x=0 - - the following is returned instead:: - - -x*sin(1) + cos(1) + O(x**2) - - whose graph is this:: - - \ | - . .| . . - . \ . . - -----+\------------------. - | . . . . - | \ - x=0 - - which is identical to ``cos(x + 1).series(n=2)``. - - Usage: - Returns the series expansion of "self" around the point ``x = x0`` - with respect to ``x`` up to O(x**n) (default n is 6). - - If ``x=None`` and ``self`` is univariate, the univariate symbol will - be supplied, otherwise an error will be raised. - - >>> from sympy import cos, exp - >>> from sympy.abc import x, y - >>> cos(x).series() - 1 - x**2/2 + x**4/24 + O(x**6) - >>> cos(x).series(n=4) - 1 - x**2/2 + O(x**4) - >>> e = cos(x + exp(y)) - >>> e.series(y, n=2) - cos(x + 1) - y*sin(x + 1) + O(y**2) - >>> e.series(x, n=2) - cos(exp(y)) - x*sin(exp(y)) + O(x**2) - - If ``n=None`` then an iterator of the series terms will be returned. - - >>> term=cos(x).series(n=None) - >>> [next(term) for i in range(2)] - [1, -x**2/2] - - For ``dir=+`` (default) the series is calculated from the right and - for ``dir=-`` the series from the left. For smooth functions this - flag will not alter the results. - - >>> abs(x).series(dir="+") - x - >>> abs(x).series(dir="-") - -x - - """ - from sympy import collect - if x is None: - syms = self.atoms(C.Symbol) - if len(syms) > 1: - raise ValueError('x must be given for multivariate functions.') - x = syms.pop() - - if not self.has(x): - if n is None: - return (s for s in [self]) - else: - return self - - ## it seems like the following should be doable, but several failures - ## then occur. Is this related to issue 1747 et al See also XPOS below. - #if x.is_positive is x.is_negative is None: - # # replace x with an x that has a positive assumption - # xpos = C.Dummy('x', positive=True) - # rv = self.subs(x, xpos).series(xpos, x0, n, dir) - # if n is None: - # return (s.subs(xpos, x) for s in rv) - # else: - # return rv.subs(xpos, x) - - if len(dir) != 1 or dir not in '+-': - raise ValueError("Dir must be '+' or '-'") - - if x0 in [S.Infinity, S.NegativeInfinity]: - dir = {S.Infinity: '+', S.NegativeInfinity: '-'}[x0] - s = self.subs(x, 1/x).series(x, n=n, dir=dir) - if n is None: - return (si.subs(x, 1/x) for si in s) - # don't include the order term since it will eat the larger terms - return s.removeO().subs(x, 1/x) - - # use rep to shift origin to x0 and change sign (if dir is negative) - # and undo the process with rep2 - if x0 or dir == '-': - if dir == '-': - rep = -x + x0 - rep2 = -x - rep2b = x0 - else: - rep = x + x0 - rep2 = x - rep2b = -x0 - s = self.subs(x, rep).series(x, x0=0, n=n, dir='+') - if n is None: # lseries... - return (si.subs(x, rep2 + rep2b) for si in s) - # nseries... - o = s.getO() or S.Zero - s = s.removeO() - if o and x0: - rep2b = 0 # when O() can handle x0 != 0 this can be removed - return s.subs(x, rep2 + rep2b) + o - - # from here on it's x0=0 and dir='+' handling - - if n is not None: # nseries handling - s1 = self._eval_nseries(x, n=n, logx=None) - o = s1.getO() or S.Zero - if o: - # make sure the requested order is returned - ngot = o.getn() - if ngot > n: - # leave o in its current form (e.g. with x*log(x)) so - # it eats terms properly, then replace it below - s1 += o.subs(x, x**C.Rational(n, ngot)) - elif ngot < n: - # increase the requested number of terms to get the desired - # number keep increasing (up to 9) until the received order - # is different than the original order and then predict how - # many additional terms are needed - for more in range(1, 9): - s1 = self._eval_nseries(x, n=n + more, logx=None) - newn = s1.getn() - if newn != ngot: - ndo = n + (n - ngot)*more/(newn - ngot) - s1 = self._eval_nseries(x, n=ndo, logx=None) - # if this assertion fails then our ndo calculation - # needs modification - assert s1.getn() == n - break - else: - raise ValueError('Could not calculate %s terms for %s' - % (str(n), self)) - o = s1.getO() - s1 = s1.removeO() - else: - o = C.Order(x**n) - if (s1 + o).removeO() == s1: - o = S.Zero - - try: - return collect(s1, x) + o - except NotImplementedError: - return s1 + o - - else: # lseries handling - def yield_lseries(s): - """Return terms of lseries one at a time.""" - for si in s: - if not si.is_Add: - yield si - continue - # yield terms 1 at a time if possible - # by increasing order until all the - # terms have been returned - yielded = 0 - o = C.Order(si)*x - ndid = 0 - ndo = len(si.args) - while 1: - do = (si - yielded + o).removeO() - o *= x - if not do or do.is_Order: - continue - if do.is_Add: - ndid += len(do.args) - else: - ndid += 1 - yield do - if ndid == ndo: - raise StopIteration - yielded += do - - return yield_lseries(self.removeO()._eval_lseries(x)) -
    -
    [docs] def lseries(self, x=None, x0=0, dir='+'): - """ - Wrapper for series yielding an iterator of the terms of the series. - - Note: an infinite series will yield an infinite iterator. The following, - for exaxmple, will never terminate. It will just keep printing terms - of the sin(x) series:: - - for term in sin(x).lseries(x): - print term - - The advantage of lseries() over nseries() is that many times you are - just interested in the next term in the series (i.e. the first term for - example), but you don't know how many you should ask for in nseries() - using the "n" parameter. - - See also nseries(). - """ - return self.series(x, x0, n=None, dir=dir) -
    - def _eval_lseries(self, x): - # default implementation of lseries is using nseries(), and adaptively - # increasing the "n". As you can see, it is not very efficient, because - # we are calculating the series over and over again. Subclasses should - # override this method and implement much more efficient yielding of - # terms. - n = 0 - series = self._eval_nseries(x, n=n, logx=None) - if not series.is_Order: - if series.is_Add: - yield series.removeO() - else: - yield series - raise StopIteration - - while series.is_Order: - n += 1 - series = self._eval_nseries(x, n=n, logx=None) - e = series.removeO() - yield e - while 1: - while 1: - n += 1 - series = self._eval_nseries(x, n=n, logx=None).removeO() - if e != series: - break - yield series - e - e = series - -
    [docs] def nseries(self, x=None, x0=0, n=6, dir='+', logx=None): - """ - Wrapper to _eval_nseries if assumptions allow, else to series. - - If x is given, x0 is 0, dir='+', and self has x, then _eval_nseries is - called. This calculates "n" terms in the innermost expressions and - then builds up the final series just by "cross-multiplying" everything - out. - - Advantage -- it's fast, because we don't have to determine how many - terms we need to calculate in advance. - - Disadvantage -- you may end up with less terms than you may have - expected, but the O(x**n) term appended will always be correct and - so the result, though perhaps shorter, will also be correct. - - If any of those assumptions is not met, this is treated like a - wrapper to series which will try harder to return the correct - number of terms. - - See also lseries(). - """ - if x and not self.has(x): - return self - if x is None or x0 or dir != '+': # {see XPOS above} or (x.is_positive == x.is_negative == None): - assert logx is None - return self.series(x, x0, n, dir) - else: - return self._eval_nseries(x, n=n, logx=logx) -
    - def _eval_nseries(self, x, n, logx): - """ - Return terms of series for self up to O(x**n) at x=0 - from the positive direction. - - This is a method that should be overridden in subclasses. Users should - never call this method directly (use .nseries() instead), so you don't - have to write docstrings for _eval_nseries(). - """ - from sympy.utilities.misc import filldedent - raise NotImplementedError(filldedent(""" - The _eval_nseries method should be added to - %s to give terms up to O(x**n) at x=0 - from the positive direction so it is available when - nseries calls it.""" % self.func) - ) - -
    [docs] def limit(self, x, xlim, dir='+'): - """ Compute limit x->xlim. - """ - from sympy.series.limits import limit - return limit(self, x, xlim, dir) -
    -
    [docs] def compute_leading_term(self, x, skip_abs=False, logx=None): - """ - as_leading_term is only allowed for results of .series() - This is a wrapper to compute a series first. - If skip_abs is true, the absolute term is assumed to be zero. - (This is necessary because sometimes it cannot be simplified - to zero without a lot of work, but is still known to be zero. - See log._eval_nseries for an example.) - If skip_log is true, log(x) is treated as an independent symbol. - (This is needed for the gruntz algorithm.) - """ - from sympy.series.gruntz import calculate_series - from sympy import cancel - if self.removeO() == 0: - return self - if logx is None: - d = C.Dummy('logx') - s = calculate_series(self, x, skip_abs, d).subs(d, C.log(x)) - else: - s = calculate_series(self, x, skip_abs, logx) - s = cancel(s) - if skip_abs: - s = expand_mul(s).as_independent(x)[1] - return s.as_leading_term(x) -
    - @cacheit -
    [docs] def as_leading_term(self, *symbols): - """ - Returns the leading (nonzero) term of the series expansion of self. - - The _eval_as_leading_term routines are used to do this, and they must - always return a non-zero value. - - Examples - ======== - - >>> from sympy.abc import x - >>> (1 + x + x**2).as_leading_term(x) - 1 - >>> (1/x**2 + x + x**2).as_leading_term(x) - x**(-2) - - """ - from sympy import powsimp - if len(symbols) > 1: - c = self - for x in symbols: - c = c.as_leading_term(x) - return c - elif not symbols: - return self - x = sympify(symbols[0]) - if not x.is_Symbol: - raise ValueError('expecting a Symbol but got %s' % x) - if x not in self.free_symbols: - return self - obj = self._eval_as_leading_term(x) - if obj is not None: - return powsimp(obj, deep=True, combine='exp') - raise NotImplementedError('as_leading_term(%s, %s)' % (self, x)) -
    - def _eval_as_leading_term(self, x): - return self - -
    [docs] def as_coeff_exponent(self, x): - """ ``c*x**e -> c,e`` where x can be any symbolic expression. - """ - from sympy import collect - s = collect(self, x) - c, p = s.as_coeff_mul(x) - if len(p) == 1: - b, e = p[0].as_base_exp() - if b == x: - return c, e - return s, S.Zero -
    -
    [docs] def leadterm(self, x): - """ - Returns the leading term a*x**b as a tuple (a, b). - - Examples - ======== - - >>> from sympy.abc import x - >>> (1+x+x**2).leadterm(x) - (1, 0) - >>> (1/x**2+x+x**2).leadterm(x) - (1, -2) - - """ - c, e = self.as_leading_term(x).as_coeff_exponent(x) - if x in c.free_symbols: - from sympy.utilities.misc import filldedent - raise ValueError(filldedent(""" - cannot compute leadterm(%s, %s). The coefficient - should have been free of x but got %s""" % (self, x, c))) - return c, e -
    -
    [docs] def as_coeff_Mul(self, rational=False): - """Efficiently extract the coefficient of a product. """ - return S.One, self -
    -
    [docs] def as_coeff_Add(self): - """Efficiently extract the coefficient of a summation. """ - return S.Zero, self - - ################################################################################### - ##################### DERIVATIVE, INTEGRAL, FUNCTIONAL METHODS #################### - ################################################################################### -
    - def diff(self, *symbols, **assumptions): - new_symbols = list(map(sympify, symbols)) # e.g. x, 2, y, z - assumptions.setdefault("evaluate", True) - return Derivative(self, *new_symbols, **assumptions) - - ########################################################################### - ###################### EXPRESSION EXPANSION METHODS ####################### - ########################################################################### - - # Relevant subclasses should override _eval_expand_hint() methods. See - # the docstring of expand() for more info. - - def _eval_expand_complex(self, **hints): - real, imag = self.as_real_imag(**hints) - return real + S.ImaginaryUnit*imag - - @staticmethod - def _expand_hint(expr, hint, deep=True, **hints): - """ - Helper for ``expand()``. Recursively calls ``expr._eval_expand_hint()``. - - Returns ``(expr, hit)``, where expr is the (possibly) expanded - ``expr`` and ``hit`` is ``True`` if ``expr`` was truly expanded and - ``False`` otherwise. - """ - hit = False - # XXX: Hack to support non-Basic args - # | - # V - if deep and getattr(expr, 'args', ()) and not expr.is_Atom: - sargs = [] - for arg in expr.args: - arg, arghit = Expr._expand_hint(arg, hint, **hints) - hit |= arghit - sargs.append(arg) - - if hit: - expr = expr.func(*sargs) - - if hasattr(expr, '_eval_expand_' + hint): - newexpr = getattr(expr, '_eval_expand_' + hint)(**hints) - if newexpr != expr: - return (newexpr, True) - return (expr, hit) - - @cacheit -
    [docs] def expand(self, deep=True, modulus=None, power_base=True, power_exp=True, - mul=True, log=True, multinomial=True, basic=True, **hints): - """ - Expand an expression using hints. - - See the docstring of the expand() function in sympy.core.function for - more information. - - """ - from sympy.simplify.simplify import fraction - - hints.update(power_base=power_base, power_exp=power_exp, mul=mul, - log=log, multinomial=multinomial, basic=basic) - - expr = self - if hints.pop('frac', False): - n, d = [a.expand(deep=deep, modulus=modulus, **hints) - for a in fraction(self)] - return n/d - elif hints.pop('denom', False): - n, d = fraction(self) - return n/d.expand(deep=deep, modulus=modulus, **hints) - elif hints.pop('numer', False): - n, d = fraction(self) - return n.expand(deep=deep, modulus=modulus, **hints)/d - # Although the hints are sorted here, an earlier hint may get applied - # at a given node in the expression tree before another because of how - # the hints are applied. e.g. expand(log(x*(y + z))) -> log(x*y + - # x*z) because while applying log at the top level, log and mul are - # applied at the deeper level in the tree so that when the log at the - # upper level gets applied, the mul has already been applied at the - # lower level. - - # Additionally, because hints are only applied once, the expression - # may not be expanded all the way. For example, if mul is applied - # before multinomial, x*(x + 1)**2 won't be expanded all the way. For - # now, we just use a special case to make multinomial run before mul, - # so that at least polynomials will be expanded all the way. In the - # future, smarter heuristics should be applied. - # TODO: Smarter heuristics - - def _expand_hint_key(hint): - """Make multinomial come before mul""" - if hint == 'mul': - return 'mulz' - return hint - - for hint in sorted(list(hints.keys()), key=_expand_hint_key): - use_hint = hints[hint] - if use_hint: - expr, hit = Expr._expand_hint(expr, hint, deep=deep, **hints) - - if modulus is not None: - modulus = sympify(modulus) - - if not modulus.is_Integer or modulus <= 0: - raise ValueError( - "modulus must be a positive integer, got %s" % modulus) - - terms = [] - - for term in Add.make_args(expr): - coeff, tail = term.as_coeff_Mul(rational=True) - - coeff %= modulus - - if coeff: - terms.append(coeff*tail) - - expr = Add(*terms) - - return expr - - ########################################################################### - ################### GLOBAL ACTION VERB WRAPPER METHODS #################### - ########################################################################### -
    -
    [docs] def integrate(self, *args, **kwargs): - """See the integrate function in sympy.integrals""" - from sympy.integrals import integrate - return integrate(self, *args, **kwargs) -
    -
    [docs] def simplify(self, ratio=1.7, measure=None): - """See the simplify function in sympy.simplify""" - from sympy.simplify import simplify - from sympy.core.function import count_ops - measure = measure or count_ops - return simplify(self, ratio, measure) -
    -
    [docs] def nsimplify(self, constants=[], tolerance=None, full=False): - """See the nsimplify function in sympy.simplify""" - from sympy.simplify import nsimplify - return nsimplify(self, constants, tolerance, full) -
    -
    [docs] def separate(self, deep=False, force=False): - """See the separate function in sympy.simplify""" - from sympy.simplify import separate - return separate(self, deep) -
    -
    [docs] def collect(self, syms, func=None, evaluate=True, exact=False, distribute_order_term=True): - """See the collect function in sympy.simplify""" - from sympy.simplify import collect - return collect(self, syms, func, evaluate, exact, distribute_order_term) -
    -
    [docs] def together(self, *args, **kwargs): - """See the together function in sympy.polys""" - from sympy.polys import together - return together(self, *args, **kwargs) -
    -
    [docs] def apart(self, x=None, **args): - """See the apart function in sympy.polys""" - from sympy.polys import apart - return apart(self, x, **args) -
    -
    [docs] def ratsimp(self): - """See the ratsimp function in sympy.simplify""" - from sympy.simplify import ratsimp - return ratsimp(self) -
    -
    [docs] def trigsimp(self, deep=False, recursive=False): - """See the trigsimp function in sympy.simplify""" - from sympy.simplify import trigsimp - return trigsimp(self, deep, recursive) -
    -
    [docs] def radsimp(self): - """See the radsimp function in sympy.simplify""" - from sympy.simplify import radsimp - return radsimp(self) -
    -
    [docs] def powsimp(self, deep=False, combine='all'): - """See the powsimp function in sympy.simplify""" - from sympy.simplify import powsimp - return powsimp(self, deep, combine) -
    -
    [docs] def combsimp(self): - """See the combsimp function in sympy.simplify""" - from sympy.simplify import combsimp - return combsimp(self) -
    -
    [docs] def factor(self, *gens, **args): - """See the factor() function in sympy.polys.polytools""" - from sympy.polys import factor - return factor(self, *gens, **args) -
    -
    [docs] def refine(self, assumption=True): - """See the refine function in sympy.assumptions""" - from sympy.assumptions import refine - return refine(self, assumption) -
    -
    [docs] def cancel(self, *gens, **args): - """See the cancel function in sympy.polys""" - from sympy.polys import cancel - return cancel(self, *gens, **args) -
    -
    [docs] def invert(self, g): - """See the invert function in sympy.polys""" - from sympy.polys import invert - return invert(self, g) -
    -
    [docs] def round(self, p=0): - """Return x rounded to the given decimal place. - - If a complex number would results, apply round to the real - and imaginary components of the number. - - Examples - ======== - >>> from sympy import pi, E, I, S, Add, Mul, Number - >>> S(10.5).round() - 11. - >>> pi.round() - 3. - >>> pi.round(2) - 3.14 - >>> (2*pi + E*I).round() #doctest: +SKIP - 6. + 3.*I - - The round method has a chopping effect: - - >>> (2*pi + I/10).round() - 6. - >>> (pi/10 + 2*I).round() #doctest: +SKIP - 2.*I - >>> (pi/10 + E*I).round(2) - 0.31 + 2.72*I - - Notes - ===== - - Do not confuse the Python builtin function, round, with the - SymPy method of the same name. The former always returns a float - (or raises an error if applied to a complex value) while the - latter returns either a Number or a complex number: - - >>> isinstance(round(S(123), -2), Number) - False - >>> isinstance(S(123).round(-2), Number) - True - >>> isinstance((3*I).round(), Mul) - True - >>> isinstance((1 + 3*I).round(), Add) - True - - """ - from sympy.functions.elementary.exponential import log - - x = self - if not x.is_number: - raise TypeError('%s is not a number' % x) - if not x.is_real: - i, r = x.as_real_imag() - return i.round(p) + S.ImaginaryUnit*r.round(p) - if not x: - return x - p = int(p) - - precs = [f._prec for f in x.atoms(C.Float)] - dps = prec_to_dps(max(precs)) if precs else None - - mag_first_dig = _mag(x) - allow = digits_needed = mag_first_dig + p - if dps is not None and allow > dps: - allow = dps - mag = Pow(10, p) # magnitude needed to bring digit p to units place - x += 1/(2*mag) # add the half for rounding - i10 = 10*mag*x.n((dps if dps is not None else digits_needed) + 1) - rv = Integer(i10)//10 - q = 1 - if p > 0: - q = mag - elif p < 0: - rv /= mag - rv = Rational(rv, q) - if rv.is_Integer: - # use str or else it won't be a float - return C.Float(str(rv), digits_needed) - else: - return C.Float(rv, allow) - -
    -
    [docs]class AtomicExpr(Atom, Expr): - """ - A parent class for object which are both atoms and Exprs. - - For example: Symbol, Number, Rational, Integer, ... - But not: Add, Mul, Pow, ... - """ - - is_Atom = True - - __slots__ = [] - - def _eval_derivative(self, s): - if self == s: - return S.One - return S.Zero - - def _eval_is_polynomial(self, syms): - return True - - def _eval_is_rational_function(self, syms): - return True - - def _eval_nseries(self, x, n, logx): - return self - -
    -def _mag(x): - """Return integer ``i`` such that .1 <= x/10**i < 1 - - Examples - ======== - >>> from sympy.core.expr import _mag - >>> from sympy import Float - >>> _mag(Float(.1)) - 0 - >>> _mag(Float(.01)) - -1 - >>> _mag(Float(1234)) - 4 - """ - from math import log10, ceil, log - xpos = abs(x.n()) - if not xpos: - return S.Zero - try: - mag_first_dig = int(ceil(log10(xpos))) - except (ValueError, OverflowError): - mag_first_dig = int(ceil(C.Float(mpf_log(xpos._mpf_, 53))/log(10))) - # check that we aren't off by 1 - if (xpos/10**mag_first_dig) >= 1: - assert 1 <= (xpos/10**mag_first_dig) < 10 - mag_first_dig += 1 - return mag_first_dig - -from .mul import Mul -from .add import Add -from .power import Pow -from .function import Derivative, expand_mul -from .mod import Mod -from .exprtools import factor_terms -from .numbers import Integer, Rational -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/core/exprtools.html b/dev-py3k/_modules/sympy/core/exprtools.html deleted file mode 100644 index 95d73e9b366..00000000000 --- a/dev-py3k/_modules/sympy/core/exprtools.html +++ /dev/null @@ -1,1002 +0,0 @@ - - - - - - - - - - sympy.core.exprtools — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.core.exprtools

    -"""Tools for manipulating of large commutative expressions. """
    -
    -from sympy.core.add import Add
    -from sympy.core.compatibility import iterable, is_sequence
    -from sympy.core.mul import Mul, _keep_coeff
    -from sympy.core.power import Pow
    -from sympy.core.basic import Basic, preorder_traversal
    -from sympy.core.expr import Expr
    -from sympy.core.sympify import sympify
    -from sympy.core.numbers import Rational, Integer
    -from sympy.core.singleton import S
    -from sympy.core.symbol import Dummy
    -from sympy.core.coreerrors import NonCommutativeExpression
    -from sympy.core.containers import Tuple, Dict
    -from sympy.utilities import default_sort_key
    -from sympy.utilities.iterables import (common_prefix, common_suffix,
    -        variations)
    -
    -from collections import defaultdict
    -
    -
    -def decompose_power(expr):
    -    """
    -    Decompose power into symbolic base and integer exponent.
    -
    -    This is strictly only valid if the exponent from which
    -    the integer is extracted is itself an integer or the
    -    base is positive. These conditions are assumed and not
    -    checked here.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.core.exprtools import decompose_power
    -    >>> from sympy.abc import x, y
    -
    -    >>> decompose_power(x)
    -    (x, 1)
    -    >>> decompose_power(x**2)
    -    (x, 2)
    -    >>> decompose_power(x**(2*y))
    -    (x**y, 2)
    -    >>> decompose_power(x**(2*y/3))
    -    (x**(y/3), 2)
    -
    -    """
    -    base, exp = expr.as_base_exp()
    -
    -    if exp.is_Number:
    -        if exp.is_Rational:
    -            if not exp.is_Integer:
    -                base = Pow(base, Rational(1, exp.q))
    -
    -            exp = exp.p
    -        else:
    -            base, exp = expr, 1
    -    else:
    -        exp, tail = exp.as_coeff_Mul(rational=True)
    -
    -        if exp is S.NegativeOne:
    -            base, exp = Pow(base, tail), -1
    -        elif exp is not S.One:
    -            tail = _keep_coeff(Rational(1, exp.q), tail)
    -            base, exp = Pow(base, tail), exp.p
    -        else:
    -            base, exp = expr, 1
    -
    -    return base, exp
    -
    -
    -class Factors(object):
    -    """Efficient representation of ``f_1*f_2*...*f_n``. """
    -
    -    __slots__ = ['factors', 'gens']
    -
    -    def __init__(self, factors=None):  # Factors
    -        if factors is None:
    -            factors = {}
    -
    -        self.factors = factors
    -        self.gens = frozenset(list(factors.keys()))
    -
    -    def __hash__(self):  # Factors
    -        return hash((tuple(self.factors), self.gens))
    -
    -    def __repr__(self):  # Factors
    -        return "Factors(%s)" % self.factors
    -
    -    def as_expr(self):  # Factors
    -        args = []
    -        for factor, exp in self.factors.items():
    -            if exp != 1:
    -                b, e = factor.as_base_exp()
    -                e = _keep_coeff(Integer(exp), e)
    -                args.append(b**e)
    -            else:
    -                args.append(factor)
    -        return Mul(*args)
    -
    -    def normal(self, other):  # Factors
    -        self_factors = dict(self.factors)
    -        other_factors = dict(other.factors)
    -
    -        for factor, self_exp in self.factors.items():
    -            try:
    -                other_exp = other.factors[factor]
    -            except KeyError:
    -                continue
    -
    -            exp = self_exp - other_exp
    -
    -            if not exp:
    -                del self_factors[factor]
    -                del other_factors[factor]
    -            else:
    -                if exp > 0:
    -                    self_factors[factor] = exp
    -                    del other_factors[factor]
    -                else:
    -                    del self_factors[factor]
    -                    other_factors[factor] = -exp
    -
    -        return Factors(self_factors), Factors(other_factors)
    -
    -    def mul(self, other):  # Factors
    -        factors = dict(self.factors)
    -
    -        for factor, exp in other.factors.items():
    -            if factor in factors:
    -                exp = factors[factor] + exp
    -
    -                if not exp:
    -                    del factors[factor]
    -                    continue
    -
    -            factors[factor] = exp
    -
    -        return Factors(factors)
    -
    -    def div(self, other):  # Factors
    -        quo, rem = dict(self.factors), {}
    -
    -        for factor, exp in other.factors.items():
    -            if factor in quo:
    -                exp = quo[factor] - exp
    -
    -                if exp <= 0:
    -                    del quo[factor]
    -
    -                if exp >= 0:
    -                    if exp:
    -                        quo[factor] = exp
    -
    -                    continue
    -
    -                exp = -exp
    -
    -            rem[factor] = exp
    -
    -        return Factors(quo), Factors(rem)
    -
    -    def quo(self, other):  # Factors
    -        return self.div(other)[0]
    -
    -    def rem(self, other):  # Factors
    -        return self.div(other)[1]
    -
    -    def pow(self, other):  # Factors
    -        if type(other) is int and other >= 0:
    -            factors = {}
    -
    -            if other:
    -                for factor, exp in self.factors.items():
    -                    factors[factor] = exp*other
    -
    -            return Factors(factors)
    -        else:
    -            raise ValueError("expected non-negative integer, got %s" % other)
    -
    -    def gcd(self, other):  # Factors
    -        factors = {}
    -
    -        for factor, exp in self.factors.items():
    -            if factor in other.factors:
    -                exp = min(exp, other.factors[factor])
    -                factors[factor] = exp
    -
    -        return Factors(factors)
    -
    -    def lcm(self, other):  # Factors
    -        factors = dict(self.factors)
    -
    -        for factor, exp in other.factors.items():
    -            if factor in factors:
    -                exp = max(exp, factors[factor])
    -
    -            factors[factor] = exp
    -
    -        return Factors(factors)
    -
    -    def __mul__(self, other):  # Factors
    -        if isinstance(other, Factors):
    -            return self.mul(other)
    -        else:
    -            return NotImplemented
    -
    -    def __divmod__(self, other):  # Factors
    -        if isinstance(other, Factors):
    -            return self.div(other)
    -        else:
    -            return NotImplemented
    -
    -    def __div__(self, other):  # Factors
    -        if isinstance(other, Factors):
    -            return self.quo(other)
    -        else:
    -            return NotImplemented
    -
    -    __truediv__ = __div__
    -
    -    def __mod__(self, other):  # Factors
    -        if isinstance(other, Factors):
    -            return self.rem(other)
    -        else:
    -            return NotImplemented
    -
    -    def __pow__(self, other):  # Factors
    -        if type(other) is int:
    -            return self.pow(other)
    -        else:
    -            return NotImplemented
    -
    -    def __eq__(self, other):  # Factors
    -        return self.factors == other.factors
    -
    -    def __ne__(self, other):  # Factors
    -        return not self.__eq__(other)
    -
    -
    -class Term(object):
    -    """Efficient representation of ``coeff*(numer/denom)``. """
    -
    -    __slots__ = ['coeff', 'numer', 'denom']
    -
    -    def __init__(self, term, numer=None, denom=None):  # Term
    -        if numer is None and denom is None:
    -            if not term.is_commutative:
    -                raise NonCommutativeExpression(
    -                    'commutative expression expected')
    -
    -            coeff, factors = term.as_coeff_mul()
    -            numer, denom = defaultdict(int), defaultdict(int)
    -
    -            for factor in factors:
    -                base, exp = decompose_power(factor)
    -
    -                if base.is_Add:
    -                    cont, base = base.primitive()
    -                    coeff *= cont**exp
    -
    -                if exp > 0:
    -                    numer[base] += exp
    -                else:
    -                    denom[base] += -exp
    -
    -            numer = Factors(numer)
    -            denom = Factors(denom)
    -        else:
    -            coeff = term
    -
    -            if numer is None:
    -                numer = Factors()
    -
    -            if denom is None:
    -                denom = Factors()
    -
    -        self.coeff = coeff
    -        self.numer = numer
    -        self.denom = denom
    -
    -    def __hash__(self):  # Term
    -        return hash((self.coeff, self.numer, self.denom))
    -
    -    def __repr__(self):  # Term
    -        return "Term(%s, %s, %s)" % (self.coeff, self.numer, self.denom)
    -
    -    def as_expr(self):  # Term
    -        return self.coeff*(self.numer.as_expr()/self.denom.as_expr())
    -
    -    def mul(self, other):  # Term
    -        coeff = self.coeff*other.coeff
    -        numer = self.numer.mul(other.numer)
    -        denom = self.denom.mul(other.denom)
    -
    -        numer, denom = numer.normal(denom)
    -
    -        return Term(coeff, numer, denom)
    -
    -    def inv(self):  # Term
    -        return Term(1/self.coeff, self.denom, self.numer)
    -
    -    def quo(self, other):  # Term
    -        return self.mul(other.inv())
    -
    -    def pow(self, other):  # Term
    -        if other < 0:
    -            return self.inv().pow(-other)
    -        else:
    -            return Term(self.coeff ** other,
    -                        self.numer.pow(other),
    -                        self.denom.pow(other))
    -
    -    def gcd(self, other):  # Term
    -        return Term(self.coeff.gcd(other.coeff),
    -                    self.numer.gcd(other.numer),
    -                    self.denom.gcd(other.denom))
    -
    -    def lcm(self, other):  # Term
    -        return Term(self.coeff.lcm(other.coeff),
    -                    self.numer.lcm(other.numer),
    -                    self.denom.lcm(other.denom))
    -
    -    def __mul__(self, other):  # Term
    -        if isinstance(other, Term):
    -            return self.mul(other)
    -        else:
    -            return NotImplemented
    -
    -    def __div__(self, other):  # Term
    -        if isinstance(other, Term):
    -            return self.quo(other)
    -        else:
    -            return NotImplemented
    -
    -    __truediv__ = __div__
    -
    -    def __pow__(self, other):  # Term
    -        if type(other) is int:
    -            return self.pow(other)
    -        else:
    -            return NotImplemented
    -
    -    def __eq__(self, other):  # Term
    -        return (self.coeff == other.coeff and
    -                self.numer == other.numer and
    -                self.denom == other.denom)
    -
    -    def __ne__(self, other):  # Term
    -        return not self.__eq__(other)
    -
    -
    -def _gcd_terms(terms, isprimitive=False, fraction=True):
    -    """Helper function for :func:`gcd_terms`.
    -
    -    If ``isprimitive`` is True then the call to primitive
    -    for an Add will be skipped. This is useful when the
    -    content has already been extrated.
    -
    -    If ``fraction`` is True then the expression will appear over a common
    -    denominator, the lcm of all term denominators.
    -    """
    -
    -    if isinstance(terms, Basic) and not isinstance(terms, Tuple):
    -        terms = Add.make_args(terms)
    -
    -    terms = list(map(Term, [t for t in terms if t]))
    -
    -    # there is some simplification that may happen if we leave this
    -    # here rather than duplicate it before the mapping of Term onto
    -    # the terms
    -    if len(terms) == 0:
    -        return S.Zero, S.Zero, S.One
    -
    -    if len(terms) == 1:
    -        cont = terms[0].coeff
    -        numer = terms[0].numer.as_expr()
    -        denom = terms[0].denom.as_expr()
    -
    -    else:
    -        cont = terms[0]
    -        for term in terms[1:]:
    -            cont = cont.gcd(term)
    -
    -        for i, term in enumerate(terms):
    -            terms[i] = term.quo(cont)
    -
    -        if fraction:
    -            denom = terms[0].denom
    -
    -            for term in terms[1:]:
    -                denom = denom.lcm(term.denom)
    -
    -            numers = []
    -            for term in terms:
    -                numer = term.numer.mul(denom.quo(term.denom))
    -                numers.append(term.coeff*numer.as_expr())
    -        else:
    -            numers = [t.as_expr() for t in terms]
    -            denom = Term(S(1)).numer
    -
    -        cont = cont.as_expr()
    -        numer = Add(*numers)
    -        denom = denom.as_expr()
    -
    -    if not isprimitive and numer.is_Add:
    -        _cont, numer = numer.primitive()
    -        cont *= _cont
    -
    -    return cont, numer, denom
    -
    -
    -
    [docs]def gcd_terms(terms, isprimitive=False, clear=True, fraction=True): - """Compute the GCD of ``terms`` and put them together. - - ``terms`` can be an expression or a non-Basic sequence of expressions - which will be handled as though they are terms from a sum. - - If ``isprimitive`` is True the _gcd_terms will not run the primitive - method on the terms. - - ``clear`` controls the removal of integers from the denominator of an Add - expression. When True (default), all numerical denominator will be cleared; - when False the denominators will be cleared only if all terms had numerical - denominators other than 1. - - ``fraction``, when True (default), will put the expression over a common - denominator. - - Examples - ======== - - >>> from sympy.core import gcd_terms - >>> from sympy.abc import x, y - - >>> gcd_terms((x + 1)**2*y + (x + 1)*y**2) - y*(x + 1)*(x + y + 1) - >>> gcd_terms(x/2 + 1) - (x + 2)/2 - >>> gcd_terms(x/2 + 1, clear=False) - x/2 + 1 - >>> gcd_terms(x/2 + y/2, clear=False) - (x + y)/2 - >>> gcd_terms(x/2 + 1/x) - (x**2 + 2)/(2*x) - >>> gcd_terms(x/2 + 1/x, fraction=False) - (x + 2/x)/2 - >>> gcd_terms(x/2 + 1/x, fraction=False, clear=False) - x/2 + 1/x - - >>> gcd_terms(x/2/y + 1/x/y) - (x**2 + 2)/(2*x*y) - >>> gcd_terms(x/2/y + 1/x/y, fraction=False, clear=False) - (x + 2/x)/(2*y) - - See Also - ======== - factor_terms, sympy.polys.polytools.terms_gcd - - """ - def mask(terms): - """replace nc portions of each term with a unique Dummy symbols - and return the replacements to restore them""" - args = [(a, []) if a.is_commutative else a.args_cnc() for a in terms] - reps = [] - for i, (c, nc) in enumerate(args): - if nc: - nc = Mul._from_args(nc) - d = Dummy() - reps.append((d, nc)) - c.append(d) - args[i] = Mul._from_args(c) - else: - args[i] = c - return args, dict(reps) - - isadd = isinstance(terms, Add) - addlike = isadd or not isinstance(terms, Basic) and \ - is_sequence(terms, include=set) and \ - not isinstance(terms, Dict) - - if addlike: - if isadd: # i.e. an Add - terms = list(terms.args) - else: - terms = sympify(terms) - terms, reps = mask(terms) - cont, numer, denom = _gcd_terms(terms, isprimitive, fraction) - numer = numer.xreplace(reps) - coeff, factors = cont.as_coeff_Mul() - return _keep_coeff(coeff, factors*numer/denom, clear=clear) - - if not isinstance(terms, Basic): - return terms - - if terms.is_Atom: - return terms - - if terms.is_Mul: - c, args = terms.as_coeff_mul() - return _keep_coeff(c, Mul(*[gcd_terms(i, isprimitive, clear, fraction) - for i in args]), clear=clear) - - def handle(a): - # don't treat internal args like terms of an Add - if not isinstance(a, Expr): - if isinstance(a, Basic): - return a.func(*[handle(i) for i in a.args]) - return type(a)([handle(i) for i in a]) - return gcd_terms(a, isprimitive, clear, fraction) - - if isinstance(terms, Dict): - return Dict(*[(k, handle(v)) for k, v in terms.args]) - return terms.func(*[handle(i) for i in terms.args]) - -
    -
    [docs]def factor_terms(expr, radical=False, clear=False, fraction=False): - """Remove common factors from terms in all arguments without - changing the underlying structure of the expr. No expansion or - simplification (and no processing of non-commutatives) is performed. - - If radical=True then a radical common to all terms will be factored - out of any Add sub-expressions of the expr. - - If clear=False (default) then coefficients will not be separated - from a single Add if they can be distributed to leave one or more - terms with integer coefficients. - - If fraction=True (default is False) then a common denominator will be - constructed for the expression. - - Examples - ======== - - >>> from sympy import factor_terms, Symbol, Mul, primitive - >>> from sympy.abc import x, y - >>> factor_terms(x + x*(2 + 4*y)**3) - x*(8*(2*y + 1)**3 + 1) - >>> A = Symbol('A', commutative=False) - >>> factor_terms(x*A + x*A + x*y*A) - x*(y*A + 2*A) - - When ``clear`` is False, a rational will only be factored out of an - Add expression if all terms of the Add have coefficients that are - fractions: - - >>> factor_terms(x/2 + 1, clear=False) - x/2 + 1 - >>> factor_terms(x/2 + 1, clear=True) - (x + 2)/2 - - This only applies when there is a single Add that the coefficient - multiplies: - - >>> factor_terms(x*y/2 + y, clear=True) - y*(x + 2)/2 - >>> factor_terms(x*y/2 + y, clear=False) == _ - True - - See Also - ======== - gcd_terms, sympy.polys.polytools.terms_gcd - - """ - - expr = sympify(expr) - is_iterable = iterable(expr) - - if not isinstance(expr, Basic) or expr.is_Atom: - if is_iterable: - return type(expr)([factor_terms(i, - radical=radical, - clear=clear, - fraction=fraction) for i in expr]) - return expr - - if expr.is_Pow or expr.is_Function or is_iterable or not hasattr(expr, 'args_cnc'): - args = expr.args - newargs = tuple([factor_terms(i, - radical=radical, - clear=clear, - fraction=fraction) for i in args]) - if newargs == args: - return expr - return expr.func(*newargs) - - cont, p = expr.as_content_primitive(radical=radical) - if p.is_Add: - list_args = [gcd_terms(a, - isprimitive=True, - clear=clear, - fraction=fraction) for a in Add.make_args(p)] - p = Add._from_args(list_args) # gcd_terms will fix up ordering - elif p.args: - p = p.func( - *[factor_terms(a, radical, clear, fraction) for a in p.args]) - p = gcd_terms(p, - isprimitive=True, - clear=clear, - fraction=fraction) - return _keep_coeff(cont, p, clear=clear) - -
    -def _mask_nc(eq): - """Return ``eq`` with non-commutative objects replaced with dummy - symbols. A dictionary that can be used to restore the original - values is returned: if it is None, the expression is - noncommutative and cannot be made commutative. The third value - returned is a list of any non-commutative symbols that appear - in the returned equation. - - Notes - ===== - All non-commutative objects other than Symbols are replaced with - a non-commutative Symbol. Identical objects will be identified - by identical symbols. - - If there is only 1 non-commutative object in an expression it will - be replaced with a commutative symbol. Otherwise, the non-commutative - entities are retained and the calling routine should handle - replacements in this case since some care must be taken to keep - track of the ordering of symbols when they occur within Muls. - - Examples - ======== - >>> from sympy.physics.secondquant import Commutator, NO, F, Fd - >>> from sympy import Dummy, symbols, Sum, Mul, Basic - >>> from sympy.abc import x, y - >>> from sympy.core.exprtools import _mask_nc - >>> A, B, C = symbols('A,B,C', commutative=False) - >>> Dummy._count = 0 # reset for doctest purposes - - One nc-symbol: - - >>> _mask_nc(A**2 - x**2) - (_0**2 - x**2, {_0: A}, []) - - Multiple nc-symbols: - - >>> _mask_nc(A**2 - B**2) - (A**2 - B**2, None, [A, B]) - - An nc-object with nc-symbols but no others outside of it: - - >>> _mask_nc(1 + x*Commutator(A, B)) - (_1*x + 1, {_1: Commutator(A, B)}, []) - >>> _mask_nc(NO(Fd(x)*F(y))) - (_2, {_2: NO(CreateFermion(x)*AnnihilateFermion(y))}, []) - - Multiple nc-objects: - - >>> eq = x*Commutator(A, B) + x*Commutator(A, C)*Commutator(A, B) - >>> _mask_nc(eq) - (x*_3 + x*_4*_3, {_3: Commutator(A, B), _4: Commutator(A, C)}, [_3, _4]) - - Multiple nc-objects and nc-symbols: - - >>> eq = A*Commutator(A, B) + B*Commutator(A, C) - >>> _mask_nc(eq) - (A*_5 + B*_6, {_5: Commutator(A, B), _6: Commutator(A, C)}, [_5, _6, A, B]) - - If there is an object that: - - - doesn't contain nc-symbols - - but has arguments which derive from Basic, not Expr - - and doesn't define an _eval_is_commutative routine - - then it will give False (or None?) for the is_commutative test. Such - objects are also removed by this routine: - - >>> from sympy import Basic, Mul - >>> eq = (1 + Mul(Basic(), Basic(), evaluate=False)) - >>> eq.is_commutative - False - >>> _mask_nc(eq) - (_7**2 + 1, {_7: Basic()}, []) - - """ - expr = eq - if expr.is_commutative: - return eq, {}, [] - - # identify nc-objects; symbols and other - rep = [] - nc_obj = set() - nc_syms = set() - pot = preorder_traversal(expr, keys=default_sort_key) - for i, a in enumerate(pot): - if any(a == r[0] for r in rep): - pot.skip() - elif not a.is_commutative: - if a.is_Symbol: - nc_syms.add(a) - elif not (a.is_Add or a.is_Mul or a.is_Pow): - if all(s.is_commutative for s in a.free_symbols): - rep.append((a, Dummy())) - else: - nc_obj.add(a) - pot.skip() - - # If there is only one nc symbol or object, it can be factored regularly - # but polys is going to complain, so replace it with a Dummy. - if len(nc_obj) == 1 and not nc_syms: - rep.append((nc_obj.pop(), Dummy())) - elif len(nc_syms) == 1 and not nc_obj: - rep.append((nc_syms.pop(), Dummy())) - - # Any remaining nc-objects will be replaced with an nc-Dummy and - # identified as an nc-Symbol to watch out for - nc_obj = sorted(nc_obj, key=default_sort_key) - for n in nc_obj: - nc = Dummy(commutative=False) - rep.append((n, nc)) - nc_syms.add(nc) - expr = expr.subs(rep) - - nc_syms = list(nc_syms) - nc_syms.sort(key=default_sort_key) - return expr, dict([(v, k) for k, v in rep]) or None, nc_syms - - -def factor_nc(expr): - """Return the factored form of ``expr`` while handling non-commutative - expressions. - - **examples** - >>> from sympy.core.exprtools import factor_nc - >>> from sympy import Symbol - >>> from sympy.abc import x - >>> A = Symbol('A', commutative=False) - >>> B = Symbol('B', commutative=False) - >>> factor_nc((x**2 + 2*A*x + A**2).expand()) - (x + A)**2 - >>> factor_nc(((x + A)*(x + B)).expand()) - (x + A)*(x + B) - """ - from sympy.simplify.simplify import _mexpand - from sympy.polys import gcd, factor - - expr = sympify(expr) - if not isinstance(expr, Expr) or not expr.args: - return expr - if not expr.is_Add: - return expr.func(*[factor_nc(a) for a in expr.args]) - - expr, rep, nc_symbols = _mask_nc(expr) - if rep: - return factor(expr).subs(rep) - else: - args = [a.args_cnc() for a in Add.make_args(expr)] - c = g = l = r = S.One - hit = False - # find any commutative gcd term - for i, a in enumerate(args): - if i == 0: - c = Mul._from_args(a[0]) - elif a[0]: - c = gcd(c, Mul._from_args(a[0])) - else: - c = S.One - if c is not S.One: - hit = True - c, g = c.as_coeff_Mul() - if g is not S.One: - for i, (cc, _) in enumerate(args): - cc = list(Mul.make_args(Mul._from_args(list(cc))/g)) - args[i][0] = cc - else: - for i, (cc, _) in enumerate(args): - cc[0] = cc[0]/c - args[i][0] = cc - # find any noncommutative common prefix - for i, a in enumerate(args): - if i == 0: - n = a[1][:] - else: - n = common_prefix(n, a[1]) - if not n: - # is there a power that can be extracted? - if not args[0][1]: - break - b, e = args[0][1][0].as_base_exp() - ok = False - if e.is_Integer: - for t in args: - if not t[1]: - break - bt, et = t[1][0].as_base_exp() - if et.is_Integer and bt == b: - e = min(e, et) - else: - break - else: - ok = hit = True - l = b**e - il = b**-e - for i, a in enumerate(args): - args[i][1][0] = il*args[i][1][0] - break - if not ok: - break - else: - hit = True - lenn = len(n) - l = Mul(*n) - for i, a in enumerate(args): - args[i][1] = args[i][1][lenn:] - # find any noncommutative common suffix - for i, a in enumerate(args): - if i == 0: - n = a[1][:] - else: - n = common_suffix(n, a[1]) - if not n: - # is there a power that can be extracted? - if not args[0][1]: - break - b, e = args[0][1][-1].as_base_exp() - ok = False - if e.is_Integer: - for t in args: - if not t[1]: - break - bt, et = t[1][-1].as_base_exp() - if et.is_Integer and bt == b: - e = min(e, et) - else: - break - else: - ok = hit = True - r = b**e - il = b**-e - for i, a in enumerate(args): - args[i][1][-1] = args[i][1][-1]*il - break - if not ok: - break - else: - hit = True - lenn = len(n) - r = Mul(*n) - for i, a in enumerate(args): - args[i][1] = a[1][:len(a[1]) - lenn] - if hit: - mid = Add(*[Mul(*cc)*Mul(*nc) for cc, nc in args]) - else: - mid = expr - - # sort the symbols so the Dummys would appear in the same - # order as the original symbols, otherwise you may introduce - # a factor of -1, e.g. A**2 - B**2) -- {A:y, B:x} --> y**2 - x**2 - # and the former factors into two terms, (A - B)*(A + B) while the - # latter factors into 3 terms, (-1)*(x - y)*(x + y) - rep1 = [(n, Dummy()) for n in sorted(nc_symbols, key=default_sort_key)] - unrep1 = [(v, k) for k, v in rep1] - unrep1.reverse() - new_mid, r2, _ = _mask_nc(mid.subs(rep1)) - new_mid = factor(new_mid) - - new_mid = new_mid.subs(r2).subs(unrep1) - - if new_mid.is_Pow: - return _keep_coeff(c, g*l*new_mid*r) - - if new_mid.is_Mul: - # XXX TODO there should be a way to inspect what order the terms - # must be in and just select the plausible ordering without - # checking permutations - cfac = [] - ncfac = [] - for f in new_mid.args: - if f.is_commutative: - cfac.append(f) - else: - b, e = f.as_base_exp() - assert e.is_Integer - ncfac.extend([b]*e) - pre_mid = g*Mul(*cfac)*l - target = _mexpand(expr/c) - for s in variations(ncfac, len(ncfac)): - ok = pre_mid*Mul(*s)*r - if _mexpand(ok) == target: - return _keep_coeff(c, ok) - - # mid was an Add that didn't factor successfully - return _keep_coeff(c, g*l*mid*r) -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/core/function.html b/dev-py3k/_modules/sympy/core/function.html deleted file mode 100644 index 427760dd153..00000000000 --- a/dev-py3k/_modules/sympy/core/function.html +++ /dev/null @@ -1,2328 +0,0 @@ - - - - - - - - - - sympy.core.function — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.core.function

    -"""
    -There are two types of functions:
    -1) defined function like exp or sin that has a name and body
    -   (in the sense that function can be evaluated).
    -    e = exp
    -2) undefined function with a name but no body. Undefined
    -   functions can be defined using a Function class as follows:
    -       f = Function('f')
    -   (the result will be a Function instance)
    -3) this isn't implemented yet: anonymous function or lambda function that has
    -   no name but has body with dummy variables. Examples of anonymous function
    -   creation:
    -       f = Lambda(x, exp(x)*x)
    -       f = Lambda(exp(x)*x) # free symbols of expr define the number of args
    -       f = Lambda(exp(x)*x)  # free symbols in the expression define the number
    -                             # of arguments
    -       f = exp * Lambda(x,x)
    -4) isn't implemented yet: composition of functions, like (sin+cos)(x), this
    -   works in sympy core, but needs to be ported back to SymPy.
    -
    -    Examples
    -    ========
    -
    -    >>> import sympy
    -    >>> f = sympy.Function("f")
    -    >>> from sympy.abc import x
    -    >>> f(x)
    -    f(x)
    -    >>> print(sympy.srepr(f(x).func))
    -    Function('f')
    -    >>> f(x).args
    -    (x,)
    -
    -"""
    -from .add import Add
    -from .assumptions import ManagedProperties
    -from .basic import Basic
    -from .cache import cacheit
    -from .compatibility import iterable, is_sequence
    -from .core import BasicMeta, C
    -from .decorators import _sympifyit
    -from .expr import Expr, AtomicExpr
    -from .numbers import Rational, Float
    -from .rules import Transform
    -from .singleton import S
    -from .sympify import sympify
    -
    -from sympy.core.containers import Tuple, Dict
    -from sympy.core.logic import fuzzy_and
    -from sympy.utilities import default_sort_key
    -from sympy.utilities.iterables import uniq
    -
    -from sympy import mpmath
    -import sympy.mpmath.libmp as mlib
    -
    -
    -def _coeff_isneg(a):
    -    """Return True if the leading Number is negative.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.core.function import _coeff_isneg
    -    >>> from sympy import S, Symbol, oo, pi
    -    >>> _coeff_isneg(-3*pi)
    -    True
    -    >>> _coeff_isneg(S(3))
    -    False
    -    >>> _coeff_isneg(-oo)
    -    True
    -    >>> _coeff_isneg(Symbol('n', negative=True)) # coeff is 1
    -    False
    -
    -    """
    -
    -    if a.is_Mul:
    -        a = a.args[0]
    -    return a.is_Number and a.is_negative
    -
    -
    -
    [docs]class PoleError(Exception): - pass - -
    -class ArgumentIndexError(ValueError): - def __str__(self): - return ("Invalid operation with argument number %s for Function %s" % - (self.args[1], self.args[0])) - - -
    [docs]class FunctionClass(ManagedProperties, metaclass=BasicMeta): - """ - Base class for function classes. FunctionClass is a subclass of type. - - Use Function('<function name>' [ , signature ]) to create - undefined function classes. - """ - - _new = type.__new__ - - def __repr__(cls): - return cls.__name__ - -
    -class Application(Basic, metaclass=FunctionClass): - """ - Base class for applied functions. - - Instances of Application represent the result of applying an application of - any type to any object. - """ - __slots__ = [] - - is_Function = True - - nargs = None - - @cacheit - def __new__(cls, *args, **options): - args = list(map(sympify, args)) - evaluate = options.pop('evaluate', True) - if options: - raise ValueError("Unknown options: %s" % options) - - if evaluate: - evaluated = cls.eval(*args) - if evaluated is not None: - return evaluated - return super(Application, cls).__new__(cls, *args) - - @classmethod - def eval(cls, *args): - """ - Returns a canonical form of cls applied to arguments args. - - The eval() method is called when the class cls is about to be - instantiated and it should return either some simplified instance - (possible of some other class), or if the class cls should be - unmodified, return None. - - Examples of eval() for the function "sign" - --------------------------------------------- - - @classmethod - def eval(cls, arg): - if arg is S.NaN: - return S.NaN - if arg is S.Zero: return S.Zero - if arg.is_positive: return S.One - if arg.is_negative: return S.NegativeOne - if isinstance(arg, C.Mul): - coeff, terms = arg.as_coeff_Mul(rational=True) - if coeff is not S.One: - return cls(coeff) * cls(terms) - - """ - return - - @property - def func(self): - return self.__class__ - - def _eval_subs(self, old, new): - if (old.is_Function and new.is_Function and - old == self.func and - (self.nargs == new.nargs or not new.nargs or - isinstance(new.nargs, tuple) and self.nargs in new.nargs)): - return new(*self.args) - - -
    [docs]class Function(Application, Expr): - """Base class for applied mathematical functions. - - It also serves as a constructor for undefined function classes. - - Examples - ======== - - First example shows how to use Function as a constructor for undefined - function classes: - - >>> from sympy import Function, Symbol - >>> x = Symbol('x') - >>> f = Function('f') - >>> g = Function('g')(x) - >>> f - f - >>> f(x) - f(x) - >>> g - g(x) - >>> f(x).diff(x) - Derivative(f(x), x) - >>> g.diff(x) - Derivative(g(x), x) - - In the following example Function is used as a base class for - ``my_func`` that represents a mathematical function *my_func*. Suppose - that it is well known, that *my_func(0)* is *1* and *my_func* at infinity - goes to *0*, so we want those two simplifications to occur automatically. - Suppose also that *my_func(x)* is real exactly when *x* is real. Here is - an implementation that honours those requirements: - - >>> from sympy import Function, S, oo, I, sin - >>> class my_func(Function): - ... - ... nargs = 1 - ... - ... @classmethod - ... def eval(cls, x): - ... if x.is_Number: - ... if x is S.Zero: - ... return S.One - ... elif x is S.Infinity: - ... return S.Zero - ... - ... def _eval_is_real(self): - ... return self.args[0].is_real - ... - >>> x = S('x') - >>> my_func(0) + sin(0) - 1 - >>> my_func(oo) - 0 - >>> my_func(3.54).n() # Not yet implemented for my_func. - my_func(3.54) - >>> my_func(I).is_real - False - - In order for ``my_func`` to become useful, several other methods would - need to be implemented. See source code of some of the already - implemented functions for more complete examples. - - """ - - @property - def _diff_wrt(self): - """Allow derivatives wrt functions. - - Examples - ======== - - >>> from sympy import Function, Symbol - >>> f = Function('f') - >>> x = Symbol('x') - >>> f(x)._diff_wrt - True - - """ - return True - - @cacheit - def __new__(cls, *args, **options): - # Handle calls like Function('f') - if cls is Function: - return UndefinedFunction(*args) - - if cls.nargs is not None: - if isinstance(cls.nargs, tuple): - nargs = cls.nargs - else: - nargs = (cls.nargs,) - - n = len(args) - - if n not in nargs: - # XXX: exception message must be in exactly this format to make - # it work with NumPy's functions like vectorize(). The ideal - # solution would be just to attach metadata to the exception - # and change NumPy to take advantage of this. - temp = ('%(name)s takes exactly %(args)s ' - 'argument%(plural)s (%(given)s given)') - raise TypeError(temp % { - 'name': cls, - 'args': cls.nargs, - 'plural': 's'*(n != 1), - 'given': n}) - - evaluate = options.get('evaluate', True) - result = super(Function, cls).__new__(cls, *args, **options) - if not evaluate or not isinstance(result, cls): - return result - - pr = max(cls._should_evalf(a) for a in result.args) - pr2 = min(cls._should_evalf(a) for a in result.args) - if pr2 > 0: - return result.evalf(mlib.libmpf.prec_to_dps(pr)) - return result - - @classmethod - def _should_evalf(cls, arg): - """ - Decide if the function should automatically evalf(). - - By default (in this implementation), this happens if (and only if) the - ARG is a floating point number. - This function is used by __new__. - """ - if arg.is_Float: - return arg._prec - if not arg.is_Add: - return -1 - re, im = arg.as_real_imag() - l = [a._prec for a in [re, im] if a.is_Float] - l.append(-1) - return max(l) - - @classmethod - def class_key(cls): - funcs = { - 'exp': 10, - 'log': 11, - 'sin': 20, - 'cos': 21, - 'tan': 22, - 'cot': 23, - 'sinh': 30, - 'cosh': 31, - 'tanh': 32, - 'coth': 33, - 'conjugate': 40, - 're': 41, - 'im': 42, - 'arg': 43, - } - name = cls.__name__ - - try: - i = funcs[name] - except KeyError: - nargs = cls.nargs - - i = 0 if nargs is None else 10000 - - return 4, i, name - - @property -
    [docs] def is_commutative(self): - """ - Returns whether the functon is commutative. - """ - if all(getattr(t, 'is_commutative') for t in self.args): - return True - else: - return False -
    - def _eval_evalf(self, prec): - # Lookup mpmath function based on name - fname = self.func.__name__ - try: - if not hasattr(mpmath, fname): - from sympy.utilities.lambdify import MPMATH_TRANSLATIONS - fname = MPMATH_TRANSLATIONS[fname] - func = getattr(mpmath, fname) - except (AttributeError, KeyError): - try: - return C.Float(self._imp_(*self.args), prec) - except (AttributeError, TypeError): - return - - # Convert all args to mpf or mpc - # Convert the arguments to *higher* precision than requested for the - # final result. - # XXX + 5 is a guess, it is similar to what is used in evalf.py. Should - # we be more intelligent about it? - try: - args = [arg._to_mpmath(prec + 5) for arg in self.args] - except ValueError: - return - - # Set mpmath precision and apply. Make sure precision is restored - # afterwards - orig = mpmath.mp.prec - try: - mpmath.mp.prec = prec - v = func(*args) - finally: - mpmath.mp.prec = orig - - return Expr._from_mpmath(v, prec) - - def _eval_derivative(self, s): - # f(x).diff(s) -> x.diff(s) * f.fdiff(1)(s) - i = 0 - l = [] - for a in self.args: - i += 1 - da = a.diff(s) - if da is S.Zero: - continue - try: - df = self.fdiff(i) - except ArgumentIndexError: - df = Function.fdiff(self, i) - l.append(df * da) - return Add(*l) - - def _eval_is_commutative(self): - return fuzzy_and(a.is_commutative for a in self.args) - -
    [docs] def as_base_exp(self): - """ - Returns the method as the 2-tuple (base, exponent). - """ - return self, S.One -
    - def _eval_aseries(self, n, args0, x, logx): - """ - Compute an asymptotic expansion around args0, in terms of self.args. - This function is only used internally by _eval_nseries and should not - be called directly; derived classes can overwrite this to implement - asymptotic expansions. - """ - from sympy.utilities.misc import filldedent - raise PoleError(filldedent(''' - Asymptotic expansion of %s around %s is - not implemented.''' % (type(self), args0))) - - def _eval_nseries(self, x, n, logx): - """ - This function does compute series for multivariate functions, - but the expansion is always in terms of *one* variable. - Examples - ======== - - >>> from sympy import atan2, O - >>> from sympy.abc import x, y - >>> atan2(x, y).series(x, n=2) - atan2(0, y) + x/y + O(x**2) - >>> atan2(x, y).series(y, n=2) - -y/x + atan2(x, 0) + O(y**2) - - This function also computes asymptotic expansions, if necessary - and possible: - - >>> from sympy import loggamma - >>> loggamma(1/x)._eval_nseries(x,0,None) - -1/x - log(x)/x + log(x)/2 + O(1) - - """ - if self.func.nargs is None: - from sympy.utilities.misc import filldedent - raise NotImplementedError(filldedent(''' - series for user-defined functions are not - supported.''')) - args = self.args - args0 = [t.limit(x, 0) for t in args] - if any(t.is_bounded is False for t in args0): - from sympy import oo, zoo, nan - # XXX could use t.as_leading_term(x) here but it's a little - # slower - a = [t.compute_leading_term(x, logx=logx) for t in args] - a0 = [t.limit(x, 0) for t in a] - if any([t.has(oo, -oo, zoo, nan) for t in a0]): - return self._eval_aseries(n, args0, x, logx - )._eval_nseries(x, n, logx) - # Careful: the argument goes to oo, but only logarithmically so. We - # are supposed to do a power series expansion "around the - # logarithmic term". e.g. - # f(1+x+log(x)) - # -> f(1+logx) + x*f'(1+logx) + O(x**2) - # where 'logx' is given in the argument - a = [t._eval_nseries(x, n, logx) for t in args] - z = [r - r0 for (r, r0) in zip(a, a0)] - p = [Dummy() for t in z] - q = [] - v = None - for ai, zi, pi in zip(a0, z, p): - if zi.has(x): - if v is not None: - raise NotImplementedError - q.append(ai + pi) - v = pi - else: - q.append(ai) - e1 = self.func(*q) - if v is None: - return e1 - s = e1._eval_nseries(v, n, logx) - o = s.getO() - s = s.removeO() - s = s.subs(v, zi).expand() + C.Order(o.expr.subs(v, zi), x) - return s - if (self.func.nargs == 1 and args0[0]) or self.func.nargs > 1: - e = self - e1 = e.expand() - if e == e1: - #for example when e = sin(x+1) or e = sin(cos(x)) - #let's try the general algorithm - term = e.subs(x, S.Zero) - if term.is_bounded is False or term is S.NaN: - raise PoleError("Cannot expand %s around 0" % (self)) - series = term - fact = S.One - for i in range(n - 1): - i += 1 - fact *= Rational(i) - e = e.diff(x) - subs = e.subs(x, S.Zero) - if subs is S.NaN: - # try to evaluate a limit if we have to - subs = e.limit(x, S.Zero) - if subs.is_bounded is False: - raise PoleError("Cannot expand %s around 0" % (self)) - term = subs*(x**i)/fact - term = term.expand() - series += term - return series + C.Order(x**n, x) - return e1.nseries(x, n=n, logx=logx) - arg = self.args[0] - l = [] - g = None - for i in range(n + 2): - g = self.taylor_term(i, arg, g) - g = g.nseries(x, n=n, logx=logx) - l.append(g) - return Add(*l) + C.Order(x**n, x) - - def _eval_rewrite(self, pattern, rule, **hints): - if hints.get('deep', False): - args = [a._eval_rewrite(pattern, rule, **hints) for a in self.args] - else: - args = self.args - - if pattern is None or isinstance(self.func, pattern): - if hasattr(self, rule): - rewritten = getattr(self, rule)(*args) - - if rewritten is not None: - return rewritten - - return self.func(*args) - -
    [docs] def fdiff(self, argindex=1): - """ - Returns the first derivative of the function. - """ - if self.nargs is not None: - if isinstance(self.nargs, tuple): - nargs = self.nargs[-1] - else: - nargs = self.nargs - if not (1 <= argindex <= nargs): - raise ArgumentIndexError(self, argindex) - if not self.args[argindex - 1].is_Symbol: - # See issue 1525 and issue 1620 and issue 2501 - arg_dummy = C.Dummy('xi_%i' % argindex) - return Subs(Derivative( - self.subs(self.args[argindex - 1], arg_dummy), - arg_dummy), arg_dummy, self.args[argindex - 1]) - return Derivative(self, self.args[argindex - 1], evaluate=False) -
    - def _eval_as_leading_term(self, x): - """Stub that should be overridden by new Functions to return - the first non-zero term in a series if ever an x-dependent - argument whose leading term vanishes as x -> 0 might be encountered. - See, for example, cos._eval_as_leading_term. - """ - args = [a.as_leading_term(x) for a in self.args] - o = C.Order(1, x) - if any(x in a.free_symbols and o.contains(a) for a in args): - # Whereas x and any finite number are contained in O(1, x), - # expressions like 1/x are not. If any arg simplified to a - # vanishing expression as x -> 0 (like x or x**2, but not - # 3, 1/x, etc...) then the _eval_as_leading_term is needed - # to supply the first non-zero term of the series, - # - # e.g. expression leading term - # ---------- ------------ - # cos(1/x) cos(1/x) - # cos(cos(x)) cos(1) - # cos(x) 1 <- _eval_as_leading_term needed - # sin(x) x <- _eval_as_leading_term needed - # - raise NotImplementedError( - '%s has no _eval_as_leading_term routine' % self.func) - else: - return self.func(*args) - - @classmethod -
    [docs] def taylor_term(cls, n, x, *previous_terms): - """General method for the taylor term. - - This method is slow, because it differentiates n-times. Subclasses can - redefine it to make it faster by using the "previous_terms". - """ - x = sympify(x) - return cls(x).diff(x, n).subs(x, 0) * x**n / C.factorial(n) - -
    -class AppliedUndef(Function): - """ - Base class for expressions resulting from the application of an undefined - function. - """ - def __new__(cls, *args, **options): - args = list(map(sympify, args)) - result = super(AppliedUndef, cls).__new__(cls, *args, **options) - result.nargs = len(args) - return result - - -class UndefinedFunction(FunctionClass): - """ - The (meta)class of undefined functions. - """ - def __new__(mcl, name): - return BasicMeta.__new__(mcl, name, (AppliedUndef,), {}) - - -
    [docs]class WildFunction(Function, AtomicExpr): - """ - A WildFunction function matches any function (with its arguments). - - Examples - ======== - - >>> from sympy import Wild, WildFunction, Function, cos - >>> from sympy.abc import x, y, z - >>> F = WildFunction('F') - >>> f = Function('f') - >>> x.match(F) - >>> F.match(F) - {F_: F_} - >>> f(x).match(F) - {F_: f(x)} - >>> cos(x).match(F) - {F_: cos(x)} - >>> f(x, y).match(F) - - To match functions with more than 1 arguments, set ``nargs`` to the - desired value: - - >>> F.nargs = 2 - >>> f(x, y).match(F) - {F_: f(x, y)} - - """ - - nargs = 1 - include = set() - - def __new__(cls, name, **assumptions): - obj = Function.__new__(cls, name, **assumptions) - obj.name = name - return obj - - def matches(self, expr, repl_dict={}, old=False): - if self.nargs is not None: - if not hasattr(expr, 'nargs') or self.nargs != expr.nargs: - return None - - repl_dict = repl_dict.copy() - repl_dict[self] = expr - return repl_dict - - @property - def is_number(self): - return False - -
    -
    [docs]class Derivative(Expr): - """ - Carries out differentiation of the given expression with respect to symbols. - - expr must define ._eval_derivative(symbol) method that returns - the differentiation result. This function only needs to consider the - non-trivial case where expr contains symbol and it should call the diff() - method internally (not _eval_derivative); Derivative should be the only - one to call _eval_derivative. - - Ordering of variables: - - If evaluate is set to True and the expression can not be evaluated, the - list of differentiation symbols will be sorted, that is, the expression is - assumed to have continuous derivatives up to the order asked. This sorting - assumes that derivatives wrt Symbols commute, derivatives wrt non-Symbols - commute, but Symbol and non-Symbol derivatives don't commute with each - other. - - Derivative wrt non-Symbols: - - This class also allows derivatives wrt non-Symbols that have _diff_wrt - set to True, such as Function and Derivative. When a derivative wrt a non- - Symbol is attempted, the non-Symbol is temporarily converted to a Symbol - while the differentiation is performed. - - Note that this may seem strange, that Derivative allows things like - f(g(x)).diff(g(x)), or even f(cos(x)).diff(cos(x)). The motivation for - allowing this syntax is to make it easier to work with variational calculus - (i.e., the Euler-Lagrange method). The best way to understand this is that - the action of derivative with respect to a non-Symbol is defined by the - above description: the object is substituted for a Symbol and the - derivative is taken with respect to that. This action is only allowed for - objects for which this can be done unambiguously, for example Function and - Derivative objects. Note that this leads to what may appear to be - mathematically inconsistent results. For example:: - - >>> from sympy import cos, sin, sqrt - >>> from sympy.abc import x - >>> (2*cos(x)).diff(cos(x)) - 2 - >>> (2*sqrt(1 - sin(x)**2)).diff(cos(x)) - 0 - - This appears wrong because in fact 2*cos(x) and 2*sqrt(1 - sin(x)**2) are - identically equal. However this is the wrong way to think of this. Think - of it instead as if we have something like this:: - - >>> from sympy.abc import c, s - >>> def F(u): - ... return 2*u - ... - >>> def G(u): - ... return 2*sqrt(1 - u**2) - ... - >>> F(cos(x)) - 2*cos(x) - >>> G(sin(x)) - 2*sqrt(-sin(x)**2 + 1) - >>> F(c).diff(c) - 2 - >>> F(c).diff(c) - 2 - >>> G(s).diff(c) - 0 - >>> G(sin(x)).diff(cos(x)) - 0 - - Here, the Symbols c and s act just like the functions cos(x) and sin(x), - respectively. Think of 2*cos(x) as f(c).subs(c, cos(x)) (or f(c) *at* - c = cos(x)) and 2*sqrt(1 - sin(x)**2) as g(s).subs(s, sin(x)) (or g(s) *at* - s = sin(x)), where f(u) == 2*u and g(u) == 2*sqrt(1 - u**2). Here, we - define the function first and evaluate it at the function, but we can - actually unambiguously do this in reverse in SymPy, because - expr.subs(Function, Symbol) is well-defined: just structurally replace the - function everywhere it appears in the expression. - - This is actually the same notational convenience used in the Euler-Lagrange - method when one says F(t, f(t), f'(t)).diff(f(t)). What is actually meant - is that the expression in question is represented by some F(t, u, v) at - u = f(t) and v = f'(t), and F(t, f(t), f'(t)).diff(f(t)) simply means - F(t, u, v).diff(u) at u = f(t). - - We do not allow to take derivative with respect to expressions where this - is not so well defined. For example, we do not allow expr.diff(x*y) - because there are multiple ways of structurally defining where x*y appears - in an expression, some of which may surprise the reader (for example, a - very strict definition would have that (x*y*z).diff(x*y) == 0). - - >>> from sympy.abc import x, y, z - >>> (x*y*z).diff(x*y) - Traceback (most recent call last): - ... - ValueError: Can't differentiate wrt the variable: x*y, 1 - - Note that this definition also fits in nicely with the definition of the - chain rule. Note how the chain rule in SymPy is defined using unevaluated - Subs objects:: - - >>> from sympy import symbols, Function - >>> f, g = symbols('f g', cls=Function) - >>> f(2*g(x)).diff(x) - 2*Derivative(g(x), x)*Subs(Derivative(f(_xi_1), _xi_1), - (_xi_1,), (2*g(x),)) - >>> f(g(x)).diff(x) - Derivative(g(x), x)*Subs(Derivative(f(_xi_1), _xi_1), - (_xi_1,), (g(x),)) - - Finally, note that, to be consistent with variational calculus, and to - ensure that the definition of substituting a Function for a Symbol in an - expression is well-defined, derivatives of functions are assumed to not be - related to the function. In other words, we have:: - - >>> from sympy import diff - >>> diff(f(x), x).diff(f(x)) - 0 - - The same is actually true for derivatives of different orders:: - - >>> diff(f(x), x, 2).diff(diff(f(x), x, 1)) - 0 - >>> diff(f(x), x, 1).diff(diff(f(x), x, 2)) - 0 - - Note, any class can allow derivatives to be taken with respect to itself. - See the docstring of Expr._diff_wrt. - - Examples - ======== - - Some basic examples: - - >>> from sympy import Derivative, Symbol, Function - >>> f = Function('f') - >>> g = Function('g') - >>> x = Symbol('x') - >>> y = Symbol('y') - - >>> Derivative(x**2, x, evaluate=True) - 2*x - >>> Derivative(Derivative(f(x,y), x), y) - Derivative(f(x, y), x, y) - >>> Derivative(f(x), x, 3) - Derivative(f(x), x, x, x) - >>> Derivative(f(x, y), y, x, evaluate=True) - Derivative(f(x, y), x, y) - - Now some derivatives wrt functions: - - >>> Derivative(f(x)**2, f(x), evaluate=True) - 2*f(x) - >>> Derivative(f(g(x)), x, evaluate=True) - Derivative(g(x), x)*Subs(Derivative(f(_xi_1), _xi_1), - (_xi_1,), (g(x),)) - """ - - is_Derivative = True - - @property - def _diff_wrt(self): - """Allow derivatives wrt Derivatives if it contains a function. - - Examples - ======== - - >>> from sympy import Function, Symbol, Derivative - >>> f = Function('f') - >>> x = Symbol('x') - >>> Derivative(f(x),x)._diff_wrt - True - >>> Derivative(x**2,x)._diff_wrt - False - """ - if self.expr.is_Function: - return True - else: - return False - - def __new__(cls, expr, *variables, **assumptions): - expr = sympify(expr) - - # There are no variables, we differentiate wrt all of the free symbols - # in expr. - if not variables: - variables = expr.free_symbols - if len(variables) != 1: - from sympy.utilities.misc import filldedent - raise ValueError(filldedent(''' - Since there is more than one variable in the - expression, the variable(s) of differentiation - must be supplied to differentiate %s''' % expr)) - - # Standardize the variables by sympifying them and making appending a - # count of 1 if there is only one variable: diff(e,x)->diff(e,x,1). - variables = list(sympify(variables)) - if not variables[-1].is_Integer or len(variables) == 1: - variables.append(S.One) - - # Split the list of variables into a list of the variables we are diff - # wrt, where each element of the list has the form (s, count) where - # s is the entity to diff wrt and count is the order of the - # derivative. - variable_count = [] - all_zero = True - i = 0 - while i < len(variables) - 1: # process up to final Integer - v, count = variables[i: i + 2] - iwas = i - if v._diff_wrt: - # We need to test the more specific case of count being an - # Integer first. - if count.is_Integer: - count = int(count) - i += 2 - elif count._diff_wrt: - count = 1 - i += 1 - - if i == iwas: # didn't get an update because of bad input - from sympy.utilities.misc import filldedent - raise ValueError(filldedent(''' - Can\'t differentiate wrt the variable: %s, %s''' % (v, count))) - - if all_zero and not count == 0: - all_zero = False - - if count: - variable_count.append((v, count)) - - # We make a special case for 0th derivative, because there is no - # good way to unambiguously print this. - if all_zero: - return expr - - # Pop evaluate because it is not really an assumption and we will need - # to track use it carefully below. - evaluate = assumptions.pop('evaluate', False) - - # Look for a quick exit if there are symbols that don't appear in - # expression at all. Note, this cannnot check non-symbols like - # functions and Derivatives as those can be created by intermediate - # derivatives. - if evaluate: - symbol_set = set(sc[0] for sc in variable_count if sc[0].is_Symbol) - if symbol_set.difference(expr.free_symbols): - return S.Zero - - # We make a generator so as to only generate a variable when necessary. - # If a high order of derivative is requested and the expr becomes 0 - # after a few differentiations, then we won't need the other variables. - variablegen = (v for v, count in variable_count for i in range(count)) - - # If we can't compute the derivative of expr (but we wanted to) and - # expr is itself not a Derivative, finish building an unevaluated - # derivative class by calling Expr.__new__. - if (not (hasattr(expr, '_eval_derivative') and evaluate) and - (not isinstance(expr, Derivative))): - variables = list(variablegen) - # If we wanted to evaluate, we sort the variables into standard - # order for later comparisons. This is too agressive if evaluate - # is False, so we don't do it in that case. - if evaluate: - #TODO: check if assumption of discontinuous derivatives exist - variables = cls._sort_variables(variables) - # Here we *don't* need to reinject evaluate into assumptions - # because we are done with it and it is not an assumption that - # Expr knows about. - obj = Expr.__new__(cls, expr, *variables, **assumptions) - return obj - - # Compute the derivative now by repeatedly calling the - # _eval_derivative method of expr for each variable. When this method - # returns None, the derivative couldn't be computed wrt that variable - # and we save the variable for later. - unhandled_variables = [] - - # Once we encouter a non_symbol that is unhandled, we stop taking - # derivatives entirely. This is because derivatives wrt functions - # don't commute with derivatives wrt symbols and we can't safely - # continue. - unhandled_non_symbol = False - for v in variablegen: - is_symbol = v.is_Symbol - - if unhandled_non_symbol: - obj = None - else: - if not is_symbol: - new_v = C.Dummy('xi_%i' % i) - expr = expr.subs(v, new_v) - old_v = v - v = new_v - obj = expr._eval_derivative(v) - if not is_symbol: - if obj is not None: - obj = obj.subs(v, old_v) - v = old_v - - if obj is None: - unhandled_variables.append(v) - if not is_symbol: - unhandled_non_symbol = True - elif obj is S.Zero: - return S.Zero - else: - expr = obj - - if unhandled_variables: - unhandled_variables = cls._sort_variables(unhandled_variables) - expr = Expr.__new__(cls, expr, *unhandled_variables, **assumptions) - else: - # We got a Derivative at the end of it all, and we rebuild it by - # sorting its variables. - if isinstance(expr, Derivative): - expr = Derivative( - expr.args[0], *cls._sort_variables(expr.args[1:]) - ) - - return expr - - @classmethod - def _sort_variables(cls, vars): - """Sort variables, but disallow sorting of non-symbols. - - When taking derivatives, the following rules usually hold: - - * Derivative wrt different symbols commute. - * Derivative wrt different non-symbols commute. - * Derivatives wrt symbols and non-symbols dont' commute. - - Examples - -------- - - >>> from sympy import Derivative, Function, symbols - >>> vsort = Derivative._sort_variables - >>> x, y, z = symbols('x y z') - >>> f, g, h = symbols('f g h', cls=Function) - - >>> vsort((x,y,z)) - [x, y, z] - - >>> vsort((h(x),g(x),f(x))) - [f(x), g(x), h(x)] - - >>> vsort((z,y,x,h(x),g(x),f(x))) - [x, y, z, f(x), g(x), h(x)] - - >>> vsort((x,f(x),y,f(y))) - [x, f(x), y, f(y)] - - >>> vsort((y,x,g(x),f(x),z,h(x),y,x)) - [x, y, f(x), g(x), z, h(x), x, y] - - >>> vsort((z,y,f(x),x,f(x),g(x))) - [y, z, f(x), x, f(x), g(x)] - - >>> vsort((z,y,f(x),x,f(x),g(x),z,z,y,x)) - [y, z, f(x), x, f(x), g(x), x, y, z, z] - """ - - sorted_vars = [] - symbol_part = [] - non_symbol_part = [] - for v in vars: - if not v.is_Symbol: - if len(symbol_part) > 0: - sorted_vars.extend(sorted(symbol_part, - key=default_sort_key)) - symbol_part = [] - non_symbol_part.append(v) - else: - if len(non_symbol_part) > 0: - sorted_vars.extend(sorted(non_symbol_part, - key=default_sort_key)) - non_symbol_part = [] - symbol_part.append(v) - if len(non_symbol_part) > 0: - sorted_vars.extend(sorted(non_symbol_part, - key=default_sort_key)) - if len(symbol_part) > 0: - sorted_vars.extend(sorted(symbol_part, - key=default_sort_key)) - return sorted_vars - - def _eval_is_commutative(self): - return self.expr.is_commutative - - def _eval_derivative(self, v): - # If the variable s we are diff wrt is not in self.variables, we - # assume that we might be able to take the derivative. - if v not in self.variables: - obj = self.expr.diff(v) - if obj is S.Zero: - return S.Zero - if isinstance(obj, Derivative): - return Derivative(obj.expr, *(self.variables + obj.variables)) - # The derivative wrt s could have simplified things such that the - # derivative wrt things in self.variables can now be done. Thus, - # we set evaluate=True to see if there are any other derivatives - # that can be done. The most common case is when obj is a simple - # number so that the derivative wrt anything else will vanish. - return Derivative(obj, *self.variables, **{'evaluate': True}) - # In this case s was in self.variables so the derivatve wrt s has - # already been attempted and was not computed, either because it - # couldn't be or evaluate=False originally. - return Derivative(self.expr, *(self.variables + (v, )), - **{'evaluate': False}) - - def doit(self, **hints): - expr = self.expr - if hints.get('deep', True): - expr = expr.doit(**hints) - hints['evaluate'] = True - return Derivative(expr, *self.variables, **hints) - - @_sympifyit('z0', NotImplementedError) -
    [docs] def doit_numerically(self, z0): - """ - Evaluate the derivative at z numerically. - - When we can represent derivatives at a point, this should be folded - into the normal evalf. For now, we need a special method. - """ - from sympy import mpmath - from sympy.core.expr import Expr - if len(self.free_symbols) != 1 or len(self.variables) != 1: - raise NotImplementedError('partials and higher order derivatives') - z = list(self.free_symbols)[0] - - def eval(x): - f0 = self.expr.subs(z, Expr._from_mpmath(x, prec=mpmath.mp.prec)) - f0 = f0.evalf(mlib.libmpf.prec_to_dps(mpmath.mp.prec)) - return f0._to_mpmath(mpmath.mp.prec) - return Expr._from_mpmath(mpmath.diff(eval, - z0._to_mpmath(mpmath.mp.prec)), - mpmath.mp.prec) -
    - @property - def expr(self): - return self._args[0] - - @property - def variables(self): - return self._args[1:] - - @property - def free_symbols(self): - return self.expr.free_symbols - - def _eval_subs(self, old, new): - if old in self.variables and not new.is_Symbol: - # Issue 1620 - return Subs(self, old, new) - return Derivative(*[x._subs(old, new) for x in self.args]) - - def _eval_lseries(self, x): - dx = self.args[1:] - for term in self.args[0].lseries(x): - yield Derivative(term, *dx) - - def _eval_nseries(self, x, n, logx): - arg = self.args[0].nseries(x, n=n, logx=logx) - o = arg.getO() - dx = self.args[1:] - rv = [Derivative(a, *dx) for a in Add.make_args(arg.removeO())] - if o: - rv.append(o/x) - return Add(*rv) - - def _eval_as_leading_term(self, x): - return self.args[0].as_leading_term(x) - -
    -
    [docs]class Lambda(Expr): - """ - Lambda(x, expr) represents a lambda function similar to Python's - 'lambda x: expr'. A function of several variables is written as - Lambda((x, y, ...), expr). - - A simple example: - - >>> from sympy import Lambda - >>> from sympy.abc import x - >>> f = Lambda(x, x**2) - >>> f(4) - 16 - - For multivariate functions, use: - - >>> from sympy.abc import y, z, t - >>> f2 = Lambda((x, y, z, t), x + y**z + t**z) - >>> f2(1, 2, 3, 4) - 73 - - A handy shortcut for lots of arguments: - - >>> p = x, y, z - >>> f = Lambda(p, x + y*z) - >>> f(*p) - x + y*z - - """ - is_Function = True - __slots__ = [] - - def __new__(cls, variables, expr): - try: - variables = Tuple(*variables) - except TypeError: - variables = Tuple(variables) - if len(variables) == 1 and variables[0] == expr: - return S.IdentityFunction - - #use dummy variables internally, just to be sure - new_variables = [C.Dummy(arg.name) for arg in variables] - expr = sympify(expr).xreplace(dict(list(zip(variables, new_variables)))) - - obj = Expr.__new__(cls, Tuple(*new_variables), expr) - return obj - - @property -
    [docs] def variables(self): - """The variables used in the internal representation of the function""" - return self._args[0] -
    - @property -
    [docs] def expr(self): - """The return value of the function""" - return self._args[1] -
    - @property - def free_symbols(self): - return self.expr.free_symbols - set(self.variables) - - @property -
    [docs] def nargs(self): - """The number of arguments that this function takes""" - return len(self._args[0]) -
    - def __call__(self, *args): - if len(args) != self.nargs: - from sympy.utilities.misc import filldedent - raise TypeError(filldedent(''' - %s takes %d arguments (%d given) - ''' % (self, self.nargs, len(args)))) - return self.expr.xreplace(dict(list(zip(self.variables, args)))) - - def __eq__(self, other): - if not isinstance(other, Lambda): - return False - if self.nargs != other.nargs: - return False - - selfexpr = self.args[1] - otherexpr = other.args[1] - otherexpr = otherexpr.xreplace(dict(list(zip(other.args[0], self.args[0])))) - return selfexpr == otherexpr - - def __ne__(self, other): - return not(self == other) - - def __hash__(self): - return super(Lambda, self).__hash__() - - def _hashable_content(self): - return (self.nargs, ) + tuple(sorted(self.free_symbols)) - - @property -
    [docs] def is_identity(self): - """Return ``True`` if this ``Lambda`` is an identity function. """ - if len(self.args) == 2: - return self.args[0] == self.args[1] - else: - return None - -
    -
    [docs]class Subs(Expr): - """ - Represents unevaluated substitutions of an expression. - - ``Subs(expr, x, x0)`` receives 3 arguments: an expression, a variable or - list of distinct variables and a point or list of evaluation points - corresponding to those variables. - - ``Subs`` objects are generally useful to represent unevaluated derivatives - calculated at a point. - - The variables may be expressions, but they are subjected to the limitations - of subs(), so it is usually a good practice to use only symbols for - variables, since in that case there can be no ambiguity. - - There's no automatic expansion - use the method .doit() to effect all - possible substitutions of the object and also of objects inside the - expression. - - When evaluating derivatives at a point that is not a symbol, a Subs object - is returned. One is also able to calculate derivatives of Subs objects - in - this case the expression is always expanded (for the unevaluated form, use - Derivative()). - - A simple example: - - >>> from sympy import Subs, Function, sin - >>> from sympy.abc import x, y, z - >>> f = Function('f') - >>> e = Subs(f(x).diff(x), x, y) - >>> e.subs(y, 0) - Subs(Derivative(f(x), x), (x,), (0,)) - >>> e.subs(f, sin).doit() - cos(y) - - An example with several variables: - - >>> Subs(f(x)*sin(y) + z, (x, y), (0, 1)) - Subs(z + f(x)*sin(y), (x, y), (0, 1)) - >>> _.doit() - z + f(0)*sin(1) - - """ - def __new__(cls, expr, variables, point, **assumptions): - from sympy import Symbol - if not is_sequence(variables, Tuple): - variables = [variables] - variables = list(sympify(variables)) - - if list(uniq(variables)) != variables: - repeated = [ v for v in set(variables) - if list(variables).count(v) > 1 ] - raise ValueError('cannot substitute expressions %s more than ' - 'once.' % repeated) - - point = Tuple(*(point if is_sequence(point, Tuple) else [point])) - - if len(point) != len(variables): - raise ValueError('Number of point values must be the same as ' - 'the number of variables.') - - expr = sympify(expr) - - # use symbols with names equal to the point value (with preppended _) - # to give a variable-independent expression - pre = "_" - pts = sorted(set(point), key=default_sort_key) - while 1: - s_pts = dict([(p, Symbol(pre + str(p))) for p in pts]) - reps = [(v, s_pts[p]) - for v, p in zip(variables, point)] - # if any underscore-preppended symbol is already a free symbol - # and is a variable with a different point value, then there - # is a clash, e.g. _0 clashes in Subs(_0 + _1, (_0, _1), (1, 0)) - # because the new symbol that would be created is _1 but _1 - # is already mapped to 0 so __0 and __1 are used for the new - # symbols - if any(r in expr.free_symbols and - r in variables and - Symbol(pre + str(point[variables.index(r)])) != r - for _, r in reps): - pre += "_" - continue - break - - obj = Expr.__new__(cls, expr, Tuple(*variables), point) - obj._expr = expr.subs(reps) - return obj - - def _eval_is_commutative(self): - return self.expr.is_commutative - - def doit(self): - return self.expr.doit().subs(list(zip(self.variables, self.point))) - - def evalf(self, prec=None, **options): - if prec is None: - return self.doit().evalf(**options) - else: - return self.doit().evalf(prec, **options) - - n = evalf - - @property -
    [docs] def variables(self): - """The variables to be evaluated""" - return self._args[1] -
    - @property -
    [docs] def expr(self): - """The expression on which the substitution operates""" - return self._args[0] -
    - @property -
    [docs] def point(self): - """The values for which the variables are to be substituted""" - return self._args[2] -
    - @property - def free_symbols(self): - return (self.expr.free_symbols - set(self.variables) | - set(self.point.free_symbols)) - - def __eq__(self, other): - if not isinstance(other, Subs): - return False - return self._expr == other._expr - - def __ne__(self, other): - return not(self == other) - - def __hash__(self): - return super(Subs, self).__hash__() - - def _hashable_content(self): - return (self._expr, ) - - def _eval_subs(self, old, new): - if old in self.variables: - pts = list(self.point.args) - pts[list(self.variables).index(old)] = new - return Subs(self.expr, self.variables, pts) - - def _eval_derivative(self, s): - if s not in self.free_symbols: - return S.Zero - return Subs(self.expr.diff(s), self.variables, self.point).doit() \ - + Add(*[ Subs(point.diff(s) * self.expr.diff(arg), - self.variables, self.point).doit() for arg, - point in zip(self.variables, self.point) ]) - -
    -
    [docs]def diff(f, *symbols, **kwargs): - """ - Differentiate f with respect to symbols. - - This is just a wrapper to unify .diff() and the Derivative class; its - interface is similar to that of integrate(). You can use the same - shortcuts for multiple variables as with Derivative. For example, - diff(f(x), x, x, x) and diff(f(x), x, 3) both return the third derivative - of f(x). - - You can pass evaluate=False to get an unevaluated Derivative class. Note - that if there are 0 symbols (such as diff(f(x), x, 0), then the result will - be the function (the zeroth derivative), even if evaluate=False. - - Examples - ======== - - >>> from sympy import sin, cos, Function, diff - >>> from sympy.abc import x, y - >>> f = Function('f') - - >>> diff(sin(x), x) - cos(x) - >>> diff(f(x), x, x, x) - Derivative(f(x), x, x, x) - >>> diff(f(x), x, 3) - Derivative(f(x), x, x, x) - >>> diff(sin(x)*cos(y), x, 2, y, 2) - sin(x)*cos(y) - - >>> type(diff(sin(x), x)) - cos - >>> type(diff(sin(x), x, evaluate=False)) - <class 'sympy.core.function.Derivative'> - >>> type(diff(sin(x), x, 0)) - sin - >>> type(diff(sin(x), x, 0, evaluate=False)) - sin - - >>> diff(sin(x)) - cos(x) - >>> diff(sin(x*y)) - Traceback (most recent call last): - ... - ValueError: specify differentiation variables to differentiate sin(x*y) - - Note that ``diff(sin(x))`` syntax is meant only for convenience - in interactive sessions and should be avoided in library code. - - References - ========== - - http://reference.wolfram.com/legacy/v5_2/Built-inFunctions/AlgebraicComputation/Calculus/D.html - - See Also - ======== - - Derivative - - """ - kwargs.setdefault('evaluate', True) - return Derivative(f, *symbols, **kwargs) - -
    -
    [docs]def expand(e, deep=True, modulus=None, power_base=True, power_exp=True, - mul=True, log=True, multinomial=True, basic=True, **hints): - """ - Expand an expression using methods given as hints. - - Hints evaluated unless explicitly set to False are: ``basic``, ``log``, - ``multinomial``, ``mul``, ``power_base``, and ``power_exp`` The following - hints are supported but not applied unless set to True: ``complex``, - ``func``, and ``trig``. In addition, the following meta-hints are - supported by some or all of the other hints: ``frac``, ``numer``, - ``denom``, ``modulus``, and ``force``. ``deep`` is supported by all - hints. Additionally, subclasses of Expr may define their own hints or - meta-hints. - - The ``basic`` hint is used for any special rewriting of an object that - should be done automatically (along with the other hints like ``mul``) - when expand is called. This is a catch-all hint to handle any sort of - expansion that may not be described by the existing hint names. To use - this hint an object should override the ``_eval_expand_basic`` method. - Objects may also define their own expand methods, which are not run by - default. See the API section below. - - If ``deep`` is set to ``True`` (the default), things like arguments of - functions are recursively expanded. Use ``deep=False`` to only expand on - the top level. - - If the ``force`` hint is used, assumptions about variables will be ignored - in making the expansion. - - Hints - ===== - - These hints are run by default - - mul - --- - - Distributes multiplication over addition: - - >>> from sympy import cos, exp, sin - >>> from sympy.abc import x, y, z - >>> (y*(x + z)).expand(mul=True) - x*y + y*z - - multinomial - ----------- - - Expand (x + y + ...)**n where n is a positive integer. - - >>> ((x + y + z)**2).expand(multinomial=True) - x**2 + 2*x*y + 2*x*z + y**2 + 2*y*z + z**2 - - power_exp - --------- - - Expand addition in exponents into multiplied bases. - - >>> exp(x + y).expand(power_exp=True) - exp(x)*exp(y) - >>> (2**(x + y)).expand(power_exp=True) - 2**x*2**y - - power_base - ---------- - - Split powers of multiplied bases. - - This only happens by default if assumptions allow, or if the - ``force`` meta-hint is used: - - >>> ((x*y)**z).expand(power_base=True) - (x*y)**z - >>> ((x*y)**z).expand(power_base=True, force=True) - x**z*y**z - >>> ((2*y)**z).expand(power_base=True) - 2**z*y**z - - Note that in some cases where this expansion always holds, SymPy performs - it automatically: - - >>> (x*y)**2 - x**2*y**2 - - log - --- - - Pull out power of an argument as a coefficient and split logs products - into sums of logs. - - Note that these only work if the arguments of the log function have the - proper assumptions--the arguments must be positive and the exponents must - be real--or else the ``force`` hint must be True: - - >>> from sympy import log, symbols, oo - >>> log(x**2*y).expand(log=True) - log(x**2*y) - >>> log(x**2*y).expand(log=True, force=True) - 2*log(x) + log(y) - >>> x, y = symbols('x,y', positive=True) - >>> log(x**2*y).expand(log=True) - 2*log(x) + log(y) - - basic - ----- - - This hint is intended primarily as a way for custom subclasses to enable - expansion by default. - - These hints are not run by default: - - complex - ------- - - Split an expression into real and imaginary parts. - - >>> x, y = symbols('x,y') - >>> (x + y).expand(complex=True) - re(x) + re(y) + I*im(x) + I*im(y) - >>> cos(x).expand(complex=True) - -I*sin(re(x))*sinh(im(x)) + cos(re(x))*cosh(im(x)) - - Note that this is just a wrapper around ``as_real_imag()``. Most objects - that wish to redefine ``_eval_expand_complex()`` should consider - redefining ``as_real_imag()`` instead. - - func - ---- - - Expand other functions. - - >>> from sympy import gamma - >>> gamma(x + 1).expand(func=True) - x*gamma(x) - - trig - ---- - - Do trigonometric expansions. - - >>> cos(x + y).expand(trig=True) - -sin(x)*sin(y) + cos(x)*cos(y) - >>> sin(2*x).expand(trig=True) - 2*sin(x)*cos(x) - - Note that the forms of ``sin(n*x)`` and ``cos(n*x)`` in terms of ``sin(x)`` - and ``cos(x)`` are not unique, due to the identity `\sin^2(x) + \cos^2(x) - = 1`. The current implementation uses the form obtained from Chebyshev - polynomials, but this may change. See `this MathWorld article - <http://mathworld.wolfram.com/Multiple-AngleFormulas.html>`_ for more - information. - - Notes - ===== - - - You can shut off unwanted methods:: - - >>> (exp(x + y)*(x + y)).expand() - x*exp(x)*exp(y) + y*exp(x)*exp(y) - >>> (exp(x + y)*(x + y)).expand(power_exp=False) - x*exp(x + y) + y*exp(x + y) - >>> (exp(x + y)*(x + y)).expand(mul=False) - (x + y)*exp(x)*exp(y) - - - Use deep=False to only expand on the top level:: - - >>> exp(x + exp(x + y)).expand() - exp(x)*exp(exp(x)*exp(y)) - >>> exp(x + exp(x + y)).expand(deep=False) - exp(x)*exp(exp(x + y)) - - - Hints are applied in an arbitrary, but consistent order (in the current - implementation, they are applied in alphabetical order, except - multinomial comes before mul, but this may change). Because of this, - some hints may prevent expansion by other hints if they are applied - first. For example, ``mul`` may distribute multiplications and prevent - ``log`` and ``power_base`` from expanding them. Also, if ``mul`` is - applied before ``multinomial`, the expression might not be fully - distributed. The solution is to use the various ``expand_hint`` helper - functions or to use ``hint=False`` to this function to finely control - which hints are applied. Here are some examples:: - - >>> from sympy import expand_log, expand, expand_mul, expand_power_base - >>> x, y, z = symbols('x,y,z', positive=True) - - >>> expand(log(x*(y + z))) - log(x) + log(y + z) - - Here, we see that ``log`` was applied before ``mul``. To get the log - expanded form, either of the following will work:: - - >>> expand_log(log(x*(y + z))) - log(x) + log(y + z) - >>> expand(log(x*(y + z)), mul=False) - log(x) + log(y + z) - - A similar thing can happen with the ``power_base`` hint:: - - >>> expand((x*(y + z))**x) - (x*y + x*z)**x - - To get the ``power_base`` expanded form, either of the following will - work:: - - >>> expand((x*(y + z))**x, mul=False) - x**x*(y + z)**x - >>> expand_power_base((x*(y + z))**x) - x**x*(y + z)**x - - >>> expand((x + y)*y/x) - y + y**2/x - - The parts of a rational expression can be targeted:: - - >>> expand((x + y)*y/x/(x + 1), frac=True) - (x*y + y**2)/(x**2 + x) - >>> expand((x + y)*y/x/(x + 1), numer=True) - (x*y + y**2)/(x*(x + 1)) - >>> expand((x + y)*y/x/(x + 1), denom=True) - y*(x + y)/(x**2 + x) - - - The ``modulus`` meta-hint can be used to reduce the coefficients of an - expression post-expansion:: - - >>> expand((3*x + 1)**2) - 9*x**2 + 6*x + 1 - >>> expand((3*x + 1)**2, modulus=5) - 4*x**2 + x + 1 - - - Either ``expand()`` the function or ``.expand()`` the method can be - used. Both are equivalent:: - - >>> expand((x + 1)**2) - x**2 + 2*x + 1 - >>> ((x + 1)**2).expand() - x**2 + 2*x + 1 - - API - === - - Objects can define their own expand hints by defining - ``_eval_expand_hint()``. The function should take the form:: - - def _eval_expand_hint(self, **hints): - # Only apply the method to the top-level expression - ... - - See also the example below. Objects should define ``_eval_expand_hint()`` - methods only if ``hint`` applies to that specific object. The generic - ``_eval_expand_hint()`` method defined in Expr will handle the no-op case. - - Each hint should be responsible for expanding that hint only. - Furthermore, the expansion should be applied to the top-level expression - only. ``expand()`` takes care of the recursion that happens when - ``deep=True``. - - You should only call ``_eval_expand_hint()`` methods directly if you are - 100% sure that the object has the method, as otherwise you are liable to - get unexpected ``AttributeError``s. Note, again, that you do not need to - recursively apply the hint to args of your object: this is handled - automatically by ``expand()``. ``_eval_expand_hint()`` should - generally not be used at all outside of an ``_eval_expand_hint()`` method. - If you want to apply a specific expansion from within another method, use - the public ``expand()`` function, method, or ``expand_hint()`` functions. - - In order for expand to work, objects must be rebuildable by their args, - i.e., ``obj.func(*obj.args) == obj`` must hold. - - Expand methods are passed ``**hints`` so that expand hints may use - 'metahints'--hints that control how different expand methods are applied. - For example, the ``force=True`` hint described above that causes - ``expand(log=True)`` to ignore assumptions is such a metahint. The - ``deep`` meta-hint is handled exclusively by ``expand()`` and is not - passed to ``_eval_expand_hint()`` methods. - - Note that expansion hints should generally be methods that perform some - kind of 'expansion'. For hints that simply rewrite an expression, use the - .rewrite() API. - - Example - ------- - - >>> from sympy import Expr, sympify - >>> class MyClass(Expr): - ... def __new__(cls, *args): - ... args = sympify(args) - ... return Expr.__new__(cls, *args) - ... - ... def _eval_expand_double(self, **hints): - ... ''' - ... Doubles the args of MyClass. - ... - ... If there more than four args, doubling is not performed, - ... unless force=True is also used (False by default). - ... ''' - ... force = hints.pop('force', False) - ... if not force and len(self.args) > 4: - ... return self - ... return self.func(*(self.args + self.args)) - ... - >>> a = MyClass(1, 2, MyClass(3, 4)) - >>> a - MyClass(1, 2, MyClass(3, 4)) - >>> a.expand(double=True) - MyClass(1, 2, MyClass(3, 4, 3, 4), 1, 2, MyClass(3, 4, 3, 4)) - >>> a.expand(double=True, deep=False) - MyClass(1, 2, MyClass(3, 4), 1, 2, MyClass(3, 4)) - - >>> b = MyClass(1, 2, 3, 4, 5) - >>> b.expand(double=True) - MyClass(1, 2, 3, 4, 5) - >>> b.expand(double=True, force=True) - MyClass(1, 2, 3, 4, 5, 1, 2, 3, 4, 5) - - See Also - ======== - - expand_log, expand_mul, expand_multinomial, expand_complex, expand_trig, - expand_power_base, expand_power_exp, expand_func, hyperexpand - - """ - # don't modify this; modify the Expr.expand method - hints['power_base'] = power_base - hints['power_exp'] = power_exp - hints['mul'] = mul - hints['log'] = log - hints['multinomial'] = multinomial - hints['basic'] = basic - return sympify(e).expand(deep=deep, modulus=modulus, **hints) - -# These are simple wrappers around single hints. - -
    -
    [docs]def expand_mul(expr, deep=True): - """ - Wrapper around expand that only uses the mul hint. See the expand - docstring for more information. - - Examples - ======== - - >>> from sympy import symbols, expand_mul, exp, log - >>> x, y = symbols('x,y', positive=True) - >>> expand_mul(exp(x+y)*(x+y)*log(x*y**2)) - x*exp(x + y)*log(x*y**2) + y*exp(x + y)*log(x*y**2) - - """ - return sympify(expr).expand(deep=deep, mul=True, power_exp=False, - power_base=False, basic=False, multinomial=False, log=False) - -
    -
    [docs]def expand_multinomial(expr, deep=True): - """ - Wrapper around expand that only uses the multinomial hint. See the expand - docstring for more information. - - Examples - ======== - - >>> from sympy import symbols, expand_multinomial, exp - >>> x, y = symbols('x y', positive=True) - >>> expand_multinomial((x + exp(x + 1))**2) - x**2 + 2*x*exp(x + 1) + exp(2*x + 2) - - """ - return sympify(expr).expand(deep=deep, mul=False, power_exp=False, - power_base=False, basic=False, multinomial=True, log=False) - -
    -
    [docs]def expand_log(expr, deep=True, force=False): - """ - Wrapper around expand that only uses the log hint. See the expand - docstring for more information. - - Examples - ======== - - >>> from sympy import symbols, expand_log, exp, log - >>> x, y = symbols('x,y', positive=True) - >>> expand_log(exp(x+y)*(x+y)*log(x*y**2)) - (x + y)*(log(x) + 2*log(y))*exp(x + y) - - """ - return sympify(expr).expand(deep=deep, log=True, mul=False, - power_exp=False, power_base=False, multinomial=False, - basic=False, force=force) - -
    -
    [docs]def expand_func(expr, deep=True): - """ - Wrapper around expand that only uses the func hint. See the expand - docstring for more information. - - Examples - ======== - - >>> from sympy import expand_func, gamma - >>> from sympy.abc import x - >>> expand_func(gamma(x + 2)) - x*(x + 1)*gamma(x) - - """ - return sympify(expr).expand(deep=deep, func=True, basic=False, - log=False, mul=False, power_exp=False, power_base=False, multinomial=False) - -
    -
    [docs]def expand_trig(expr, deep=True): - """ - Wrapper around expand that only uses the trig hint. See the expand - docstring for more information. - - Examples - ======== - - >>> from sympy import expand_trig, sin, cos - >>> from sympy.abc import x, y - >>> expand_trig(sin(x+y)*(x+y)) - (x + y)*(sin(x)*cos(y) + sin(y)*cos(x)) - - """ - return sympify(expr).expand(deep=deep, trig=True, basic=False, - log=False, mul=False, power_exp=False, power_base=False, multinomial=False) - -
    -
    [docs]def expand_complex(expr, deep=True): - """ - Wrapper around expand that only uses the complex hint. See the expand - docstring for more information. - - Examples - ======== - - >>> from sympy import expand_complex, exp, sqrt, I - >>> from sympy.abc import z - >>> expand_complex(exp(z)) - I*exp(re(z))*sin(im(z)) + exp(re(z))*cos(im(z)) - >>> expand_complex(sqrt(I)) - sqrt(2)/2 + sqrt(2)*I/2 - - See Also - ======== - Expr.as_real_imag - """ - return sympify(expr).expand(deep=deep, complex=True, basic=False, - log=False, mul=False, power_exp=False, power_base=False, multinomial=False) - -
    -
    [docs]def expand_power_base(expr, deep=True, force=False): - """ - Wrapper around expand that only uses the power_base hint. - - See the expand docstring for more information. - - A wrapper to expand(power_base=True) which separates a power with a base - that is a Mul into a product of powers, without performing any other - expansions, provided that assumptions about the power's base and exponent - allow. - - deep=False (default is True) will only apply to the top-level expression. - - force=True (default is False) will cause the expansion to ignore - assumptions about the base and exponent. When False, the expansion will - only happen if the base is non-negative or the exponent is an integer. - - >>> from sympy.abc import x, y, z - >>> from sympy import expand_power_base, sin, cos, exp - - >>> (x*y)**2 - x**2*y**2 - - >>> (2*x)**y - (2*x)**y - >>> expand_power_base(_) - 2**y*x**y - - >>> expand_power_base((x*y)**z) - (x*y)**z - >>> expand_power_base((x*y)**z, force=True) - x**z*y**z - >>> expand_power_base(sin((x*y)**z), deep=False) - sin((x*y)**z) - >>> expand_power_base(sin((x*y)**z), force=True) - sin(x**z*y**z) - - >>> expand_power_base((2*sin(x))**y + (2*cos(x))**y) - 2**y*sin(x)**y + 2**y*cos(x)**y - - >>> expand_power_base((2*exp(y))**x) - 2**x*exp(y)**x - - >>> expand_power_base((2*cos(x))**y) - 2**y*cos(x)**y - - Notice that sums are left untouched. If this is not the desired behavior, - apply full ``expand()`` to the expression: - - >>> expand_power_base(((x+y)*z)**2) - z**2*(x + y)**2 - >>> (((x+y)*z)**2).expand() - x**2*z**2 + 2*x*y*z**2 + y**2*z**2 - - >>> expand_power_base((2*y)**(1+z)) - 2**(z + 1)*y**(z + 1) - >>> ((2*y)**(1+z)).expand() - 2*2**z*y*y**z - - """ - return sympify(expr).expand(deep=deep, log=False, mul=False, - power_exp=False, power_base=True, multinomial=False, - basic=False, force=force) - -
    -
    [docs]def expand_power_exp(expr, deep=True): - """ - Wrapper around expand that only uses the power_exp hint. - - See the expand docstring for more information. - - Examples - ======== - - >>> from sympy import expand_power_exp - >>> from sympy.abc import x, y - >>> expand_power_exp(x**(y + 2)) - x**2*x**y - """ - return sympify(expr).expand(deep=deep, complex=False, basic=False, - log=False, mul=False, power_exp=True, power_base=False, multinomial=False) - -
    -
    [docs]def count_ops(expr, visual=False): - """ - Return a representation (integer or expression) of the operations in expr. - - If ``visual`` is ``False`` (default) then the sum of the coefficients of the - visual expression will be returned. - - If ``visual`` is ``True`` then the number of each type of operation is shown - with the core class types (or their virtual equivalent) multiplied by the - number of times they occur. - - If expr is an iterable, the sum of the op counts of the - items will be returned. - - Examples - ======== - - >>> from sympy.abc import a, b, x, y - >>> from sympy import sin, count_ops - - Although there isn't a SUB object, minus signs are interpreted as - either negations or subtractions: - - >>> (x - y).count_ops(visual=True) - SUB - >>> (-x).count_ops(visual=True) - NEG - - Here, there are two Adds and a Pow: - - >>> (1 + a + b**2).count_ops(visual=True) - 2*ADD + POW - - In the following, an Add, Mul, Pow and two functions: - - >>> (sin(x)*x + sin(x)**2).count_ops(visual=True) - ADD + MUL + POW + 2*SIN - - for a total of 5: - - >>> (sin(x)*x + sin(x)**2).count_ops(visual=False) - 5 - - Note that "what you type" is not always what you get. The expression - 1/x/y is translated by sympy into 1/(x*y) so it gives a DIV and MUL rather - than two DIVs: - - >>> (1/x/y).count_ops(visual=True) - DIV + MUL - - The visual option can be used to demonstrate the difference in - operations for expressions in different forms. Here, the Horner - representation is compared with the expanded form of a polynomial: - - >>> eq=x*(1 + x*(2 + x*(3 + x))) - >>> count_ops(eq.expand(), visual=True) - count_ops(eq, visual=True) - -MUL + 3*POW - - The count_ops function also handles iterables: - - >>> count_ops([x, sin(x), None, True, x + 2], visual=False) - 2 - >>> count_ops([x, sin(x), None, True, x + 2], visual=True) - ADD + SIN - >>> count_ops({x: sin(x), x + 2: y + 1}, visual=True) - 2*ADD + SIN - - """ - from sympy.simplify.simplify import fraction - - expr = sympify(expr) - if isinstance(expr, Expr): - - ops = [] - args = [expr] - NEG = C.Symbol('NEG') - DIV = C.Symbol('DIV') - SUB = C.Symbol('SUB') - ADD = C.Symbol('ADD') - while args: - a = args.pop() - if a.is_Rational: - #-1/3 = NEG + DIV - if a is not S.One: - if a.p < 0: - ops.append(NEG) - if a.q != 1: - ops.append(DIV) - continue - elif a.is_Mul: - if _coeff_isneg(a): - ops.append(NEG) - if a.args[0] is S.NegativeOne: - a = a.as_two_terms()[1] - else: - a = -a - n, d = fraction(a) - if n.is_Integer: - ops.append(DIV) - if n < 0: - ops.append(NEG) - args.append(d) - continue # won't be -Mul but could be Add - elif d is not S.One: - if not d.is_Integer: - args.append(d) - ops.append(DIV) - args.append(n) - continue # could be -Mul - elif a.is_Add: - aargs = list(a.args) - negs = 0 - for i, ai in enumerate(aargs): - if _coeff_isneg(ai): - negs += 1 - args.append(-ai) - if i > 0: - ops.append(SUB) - else: - args.append(ai) - if i > 0: - ops.append(ADD) - if negs == len(aargs): # -x - y = NEG + SUB - ops.append(NEG) - elif _coeff_isneg(aargs[0]): # -x + y = SUB, but already recorded ADD - ops.append(SUB - ADD) - continue - if a.is_Pow and a.exp is S.NegativeOne: - ops.append(DIV) - args.append(a.base) # won't be -Mul but could be Add - continue - if (a.is_Mul or - a.is_Pow or - a.is_Function or - isinstance(a, Derivative) or - isinstance(a, C.Integral)): - - o = C.Symbol(a.func.__name__.upper()) - # count the args - if (a.is_Mul or isinstance(a, C.LatticeOp)): - ops.append(o*(len(a.args) - 1)) - else: - ops.append(o) - if not a.is_Symbol: - args.extend(a.args) - - elif type(expr) is dict: - ops = [count_ops(k, visual=visual) + - count_ops(v, visual=visual) for k, v in expr.items()] - elif iterable(expr): - ops = [count_ops(i, visual=visual) for i in expr] - elif not isinstance(expr, Basic): - ops = [] - else: # it's Basic not isinstance(expr, Expr): - assert isinstance(expr, Basic) - ops = [count_ops(a, visual=visual) for a in expr.args] - - if not ops: - if visual: - return S.Zero - return 0 - - ops = Add(*ops) - - if visual: - return ops - - if ops.is_Number: - return int(ops) - - return sum(int((a.args or [1])[0]) for a in Add.make_args(ops)) - -
    -
    [docs]def nfloat(expr, n=15, exponent=False): - """Make all Rationals in expr Floats except those in exponents - (unless the exponents flag is set to True). - - Examples - ======== - - >>> from sympy.core.function import nfloat - >>> from sympy.abc import x, y - >>> from sympy import cos, pi, S, sqrt - >>> nfloat(x**4 + x/2 + cos(pi/3) + 1 + sqrt(y)) - x**4 + 0.5*x + sqrt(y) + 1.5 - >>> nfloat(x**4 + sqrt(y), exponent=True) - x**4.0 + y**0.5 - - """ - from sympy.core import Pow - from sympy.core.basic import _aresame - - if iterable(expr, exclude=str): - if isinstance(expr, (dict, Dict)): - return type(expr)([(k, nfloat(v, n, exponent)) for k, v in - expr.items()]) - return type(expr)([nfloat(a, n, exponent) for a in expr]) - rv = sympify(expr) - - if rv.is_Number: - return Float(rv, n) - elif rv.is_number: - # evalf doesn't always set the precision - rv = rv.n(n) - if rv.is_Number: - rv = Float(rv.n(n), n) - else: - pass # pure_complex(rv) is likely True - return rv - - if not exponent: - reps = [(p, Pow(p.base, Dummy())) for p in rv.atoms(Pow)] - rv = rv.xreplace(dict(reps)) - rv = rv.n(n) - if not exponent: - rv = rv.xreplace(dict([(d.exp, p.exp) for p, d in reps])) - else: - # Pow._eval_evalf special cases Integer exponents so if - # exponent is suppose to be handled we have to do so here - rv = rv.xreplace(Transform( - lambda x: Pow(x.base, Float(x.exp, n)), - lambda x: x.is_Pow and x.exp.is_Integer)) - - return rv.xreplace(Transform( - lambda x: x.func(*nfloat(x.args, n, exponent)), - lambda x: isinstance(x, Function))) - -
    -from sympy.core.symbol import Dummy -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/core/mod.html b/dev-py3k/_modules/sympy/core/mod.html deleted file mode 100644 index b5deda0d5f3..00000000000 --- a/dev-py3k/_modules/sympy/core/mod.html +++ /dev/null @@ -1,157 +0,0 @@ - - - - - - - - - - sympy.core.mod — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.core.mod

    -from .function import Function
    -from sympy.core.numbers import Float
    -from sympy.core.function import expand_mul
    -
    -
    -
    [docs]class Mod(Function): - """Represents a modulo operation on symbolic expressions. - - Receives two arguments, dividend p and divisor q. - - The convention used is the same as python's: the remainder always has the - same sign as the divisor. - - Examples - ======== - - >>> from sympy.abc import x, y - >>> x**2 % y - Mod(x**2, y) - >>> _.subs({x: 5, y: 6}) - 1 - - """ - nargs = 2 - - @classmethod - def eval(cls, p, q): - from sympy.simplify.simplify import nsimplify - if q.is_Number: - float = not q.is_Rational - pnew = expand_mul(p) - if pnew.is_Number: - float = float or not pnew.is_Rational - if not float: - return pnew % q - return Float(nsimplify(pnew) % nsimplify(q)) - elif pnew.is_Add and pnew.args[0].is_Number: - r, p = pnew.as_two_terms() - p += Mod(r, q) - return Mod(p, q, evaluate=False)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/core/mul.html b/dev-py3k/_modules/sympy/core/mul.html deleted file mode 100644 index 3d617b673a8..00000000000 --- a/dev-py3k/_modules/sympy/core/mul.html +++ /dev/null @@ -1,1622 +0,0 @@ - - - - - - - - - - sympy.core.mul — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.core.mul

    -from collections import defaultdict
    -import operator
    -
    -from sympy.core.sympify import sympify
    -from sympy.core.basic import Basic, C
    -from sympy.core.singleton import S
    -from sympy.core.operations import AssocOp
    -from sympy.core.cache import cacheit
    -from sympy.core.logic import fuzzy_not
    -from sympy.core.compatibility import cmp_to_key
    -from sympy.core.expr import Expr
    -from functools import reduce
    -
    -# internal marker to indicate:
    -#   "there are still non-commutative objects -- don't forget to process them"
    -
    -
    -class NC_Marker:
    -    is_Order = False
    -    is_Mul = False
    -    is_Number = False
    -    is_Poly = False
    -
    -    is_commutative = False
    -
    -
    -
    [docs]class Mul(Expr, AssocOp): - - __slots__ = [] - - is_Mul = True - - #identity = S.One - # cyclic import, so defined in numbers.py - - # Key for sorting commutative args in canonical order - _args_sortkey = cmp_to_key(Basic.compare) - - @classmethod -
    [docs] def flatten(cls, seq): - """Return commutative, noncommutative and order arguments by - combining related terms. - - Notes - ===== - * In an expression like ``a*b*c``, python process this through sympy - as ``Mul(Mul(a, b), c)``. This can have undesirable consequences. - - - Sometimes terms are not combined as one would like: - {c.f. http://code.google.com/p/sympy/issues/detail?id=1497} - - >>> from sympy import Mul, sqrt - >>> from sympy.abc import x, y, z - >>> 2*(x + 1) # this is the 2-arg Mul behavior - 2*x + 2 - >>> y*(x + 1)*2 - 2*y*(x + 1) - >>> 2*(x + 1)*y # 2-arg result will be obtained first - y*(2*x + 2) - >>> Mul(2, x + 1, y) # all 3 args simultaneously processed - 2*y*(x + 1) - >>> 2*((x + 1)*y) # parentheses can control this behavior - 2*y*(x + 1) - - Powers with compound bases may not find a single base to - combine with unless all arguments are processed at once. - Post-processing may be necessary in such cases. - {c.f. http://code.google.com/p/sympy/issues/detail?id=2629} - - >>> a = sqrt(x*sqrt(y)) - >>> a**3 - (x*sqrt(y))**(3/2) - >>> Mul(a,a,a) - (x*sqrt(y))**(3/2) - >>> a*a*a - x*sqrt(y)*sqrt(x*sqrt(y)) - >>> _.subs(a.base, z).subs(z, a.base) - (x*sqrt(y))**(3/2) - - - If more than two terms are being multiplied then all the - previous terms will be re-processed for each new argument. - So if each of ``a``, ``b`` and ``c`` were :class:`Mul` - expression, then ``a*b*c`` (or building up the product - with ``*=``) will process all the arguments of ``a`` and - ``b`` twice: once when ``a*b`` is computed and again when - ``c`` is multiplied. - - Using ``Mul(a, b, c)`` will process all arguments once. - - * The results of Mul are cached according to arguments, so flatten - will only be called once for ``Mul(a, b, c)``. If you can - structure a calculation so the arguments are most likely to be - repeats then this can save time in computing the answer. For - example, say you had a Mul, M, that you wished to divide by ``d[i]`` - and multiply by ``n[i]`` and you suspect there are many repeats - in ``n``. It would be better to compute ``M*n[i]/d[i]`` rather - than ``M/d[i]*n[i]`` since every time n[i] is a repeat, the - product, ``M*n[i]`` will be returned without flattening -- the - cached value will be returned. If you divide by the ``d[i]`` - first (and those are more unique than the ``n[i]``) then that will - create a new Mul, ``M/d[i]`` the args of which will be traversed - again when it is multiplied by ``n[i]``. - - {c.f. http://code.google.com/p/sympy/issues/detail?id=2607} - - This consideration is moot if the cache is turned off. - - NB - -- - The validity of the above notes depends on the implementation - details of Mul and flatten which may change at any time. Therefore, - you should only consider them when your code is highly performance - sensitive. - - Removal of 1 from the sequence is already handled by AssocOp.__new__. - """ - rv = None - if len(seq) == 2: - a, b = seq - if b.is_Rational: - a, b = b, a - assert not a is S.One - if a and a.is_Rational: - r, b = b.as_coeff_Mul() - a *= r - if b.is_Mul: - bargs, nc = b.args_cnc() - rv = bargs, nc, None - if a is not S.One: - bargs.insert(0, a) - - elif b.is_Add and b.is_commutative: - if a is S.One: - rv = [b], [], None - else: - r, b = b.as_coeff_Add() - bargs = [_keep_coeff(a, bi) for bi in Add.make_args(b)] - bargs.sort(key=hash) - ar = a*r - if ar: - bargs.insert(0, ar) - bargs = [Add._from_args(bargs)] - rv = bargs, [], None - if rv: - return rv - - # apply associativity, separate commutative part of seq - c_part = [] # out: commutative factors - nc_part = [] # out: non-commutative factors - - nc_seq = [] - - coeff = S.One # standalone term - # e.g. 3 * ... - - iu = [] # ImaginaryUnits, I - - c_powers = [] # (base,exp) n - # e.g. (x,n) for x - - num_exp = [] # (num-base, exp) y - # e.g. (3, y) for ... * 3 * ... - - neg1e = 0 # exponent on -1 extracted from Number-based Pow - - pnum_rat = {} # (num-base, Rat-exp) 1/2 - # e.g. (3, 1/2) for ... * 3 * ... - - order_symbols = None - - # --- PART 1 --- - # - # "collect powers and coeff": - # - # o coeff - # o c_powers - # o num_exp - # o neg1e - # o pnum_rat - # - # NOTE: this is optimized for all-objects-are-commutative case - for o in seq: - # O(x) - if o.is_Order: - o, order_symbols = o.as_expr_variables(order_symbols) - - # Mul([...]) - if o.is_Mul: - if o.is_commutative: - seq.extend(o.args) # XXX zerocopy? - - else: - # NCMul can have commutative parts as well - for q in o.args: - if q.is_commutative: - seq.append(q) - else: - nc_seq.append(q) - - # append non-commutative marker, so we don't forget to - # process scheduled non-commutative objects - seq.append(NC_Marker) - - continue - - # 3 - elif o.is_Number: - if o is S.NaN or coeff is S.ComplexInfinity and o is S.Zero: - # we know for sure the result will be nan - return [S.NaN], [], None - elif coeff.is_Number: # it could be zoo - coeff *= o - if coeff is S.NaN: - # we know for sure the result will be nan - return [S.NaN], [], None - continue - - elif o is S.ComplexInfinity: - if not coeff: - # 0 * zoo = NaN - return [S.NaN], [], None - if coeff is S.ComplexInfinity: - # zoo * zoo = zoo - return [S.ComplexInfinity], [], None - coeff = S.ComplexInfinity - continue - - elif o is S.ImaginaryUnit: - iu.append(o) - continue - - elif o.is_commutative: - # e - # o = b - b, e = o.as_base_exp() - - # y - # 3 - if o.is_Pow and b.is_Number: - - # get all the factors with numeric base so they can be - # combined below, but don't combine negatives unless - # the exponent is an integer - if e.is_Rational: - if e.is_Integer: - coeff *= Pow(b, e) # it is an unevaluated power - continue - elif e.is_negative: # also a sign of an unevaluated power - seq.append(Pow(b, e)) - continue - elif b.is_negative: - neg1e += e - b = -b - if b is not S.One: - pnum_rat.setdefault(b, []).append(e) - continue - elif b.is_positive or e.is_integer: - num_exp.append((b, e)) - continue - c_powers.append((b, e)) - - # NON-COMMUTATIVE - # TODO: Make non-commutative exponents not combine automatically - else: - if o is not NC_Marker: - nc_seq.append(o) - - # process nc_seq (if any) - while nc_seq: - o = nc_seq.pop(0) - if not nc_part: - nc_part.append(o) - continue - - # b c b+c - # try to combine last terms: a * a -> a - o1 = nc_part.pop() - b1, e1 = o1.as_base_exp() - b2, e2 = o.as_base_exp() - new_exp = e1 + e2 - # Only allow powers to combine if the new exponent is - # not an Add. This allow things like a**2*b**3 == a**5 - # if a.is_commutative == False, but prohibits - # a**x*a**y and x**a*x**b from combining (x,y commute). - if b1 == b2 and (not new_exp.is_Add): - o12 = b1 ** new_exp - - # now o12 could be a commutative object - if o12.is_commutative: - seq.append(o12) - continue - else: - nc_seq.insert(0, o12) - - else: - nc_part.append(o1) - nc_part.append(o) - - # handle the ImaginaryUnits - if iu: - if len(iu) == 1: - c_powers.append((iu[0], S.One)) - else: - # a product of I's has one of 4 values; select that value - # based on the length of iu: - # len(iu) % 4 of (0, 1, 2, 3) has a corresponding value of - # (1, I,-1,-I) - niu = len(iu) % 4 - if niu % 2: - c_powers.append((S.ImaginaryUnit, S.One)) - if niu in (2, 3): - coeff = -coeff - - # We do want a combined exponent if it would not be an Add, such as - # y 2y 3y - # x * x -> x - # We determine if two exponents have the same term by using - # as_coeff_Mul. - # - # Unfortunately, this isn't smart enough to consider combining into - # exponents that might already be adds, so things like: - # z - y y - # x * x will be left alone. This is because checking every possible - # combination can slow things down. - - # gather exponents of common bases... - def _gather(c_powers): - new_c_powers = [] - common_b = {} # b:e - for b, e in c_powers: - co = e.as_coeff_Mul() - common_b.setdefault(b, {}).setdefault(co[1], []).append(co[0]) - for b, d in list(common_b.items()): - for di, li in list(d.items()): - d[di] = Add(*li) - for b, e in list(common_b.items()): - for t, c in list(e.items()): - new_c_powers.append((b, c*t)) - return new_c_powers - - # in c_powers - c_powers = _gather(c_powers) - - # and in num_exp - num_exp = _gather(num_exp) - - # --- PART 2 --- - # - # o process collected powers (x**0 -> 1; x**1 -> x; otherwise Pow) - # o combine collected powers (2**x * 3**x -> 6**x) - # with numeric base - - # ................................ - # now we have: - # - coeff: - # - c_powers: (b, e) - # - num_exp: (2, e) - # - pnum_rat: {(1/3, [1/3, 2/3, 1/4])} - - # 0 1 - # x -> 1 x -> x - for b, e in c_powers: - if e is S.One: - if b.is_Number: - coeff *= b - else: - c_part.append(b) - elif e is not S.Zero: - c_part.append(Pow(b, e)) - - # x x x - # 2 * 3 -> 6 - inv_exp_dict = {} # exp:Mul(num-bases) x x - # e.g. x:6 for ... * 2 * 3 * ... - for b, e in num_exp: - inv_exp_dict.setdefault(e, []).append(b) - for e, b in list(inv_exp_dict.items()): - inv_exp_dict[e] = Mul(*b) - c_part.extend([Pow(b, e) for e, b in inv_exp_dict.items() if e]) - - # b, e -> e' = sum(e), b - # {(1/5, [1/3]), (1/2, [1/12, 1/4]} -> {(1/3, [1/5, 1/2])} - comb_e = {} - for b, e in pnum_rat.items(): - comb_e.setdefault(Add(*e), []).append(b) - del pnum_rat - # process them, reducing exponents to values less than 1 - # and updating coeff if necessary else adding them to - # num_rat for further processing - num_rat = [] - for e, b in comb_e.items(): - b = Mul(*b) - if e.q == 1: - coeff *= Pow(b, e) - continue - if e.p > e.q: - e_i, ep = divmod(e.p, e.q) - coeff *= Pow(b, e_i) - e = Rational(ep, e.q) - num_rat.append((b, e)) - del comb_e - - # extract gcd of bases in num_rat - # 2**(1/3)*6**(1/4) -> 2**(1/3+1/4)*3**(1/4) - pnew = defaultdict(list) - i = 0 # steps through num_rat which may grow - while i < len(num_rat): - bi, ei = num_rat[i] - grow = [] - for j in range(i + 1, len(num_rat)): - bj, ej = num_rat[j] - g = _rgcd(bi, bj) - if g is not S.One: - # 4**r1*6**r2 -> 2**(r1+r2) * 2**r1 * 3**r2 - # this might have a gcd with something else - e = ei + ej - if e.q == 1: - coeff *= Pow(g, e) - else: - if e.p > e.q: - e_i, ep = divmod(e.p, e.q) # change e in place - coeff *= Pow(g, e_i) - e = Rational(ep, e.q) - grow.append((g, e)) - # update the jth item - num_rat[j] = (bj/g, ej) - # update bi that we are checking with - bi = bi/g - if bi is S.One: - break - if bi is not S.One: - obj = Pow(bi, ei) - if obj.is_Number: - coeff *= obj - else: - # changes like sqrt(12) -> 2*sqrt(3) - for obj in Mul.make_args(obj): - if obj.is_Number: - coeff *= obj - else: - assert obj.is_Pow - bi, ei = obj.args - pnew[ei].append(bi) - - num_rat.extend(grow) - i += 1 - - # combine bases of the new powers - for e, b in pnew.items(): - pnew[e] = Mul(*b) - - # see if there is a base with matching coefficient - # that the -1 can be joined with - if neg1e: - p = Pow(S.NegativeOne, neg1e) - if p.is_Number: - coeff *= p - else: - c, p = p.as_coeff_Mul() - coeff *= c - if p.is_Pow and p.base is S.NegativeOne: - neg1e = p.exp - for e, b in pnew.items(): - if e == neg1e and b.is_positive: - pnew[e] = -b - break - else: - c_part.append(p) - - # add all the pnew powers - c_part.extend([Pow(b, e) for e, b in pnew.items()]) - - # oo, -oo - if (coeff is S.Infinity) or (coeff is S.NegativeInfinity): - def _handle_for_oo(c_part, coeff_sign): - new_c_part = [] - for t in c_part: - if t.is_positive: - continue - if t.is_negative: - coeff_sign *= -1 - continue - new_c_part.append(t) - return new_c_part, coeff_sign - c_part, coeff_sign = _handle_for_oo(c_part, 1) - nc_part, coeff_sign = _handle_for_oo(nc_part, coeff_sign) - coeff *= coeff_sign - - # zoo - if coeff is S.ComplexInfinity: - # zoo might be - # unbounded_real + bounded_im - # bounded_real + unbounded_im - # unbounded_real + unbounded_im - # and non-zero real or imaginary will not change that status. - c_part = [c for c in c_part if not (c.is_nonzero and - c.is_real is not None)] - nc_part = [c for c in nc_part if not (c.is_nonzero and - c.is_real is not None)] - - # 0 - elif coeff is S.Zero: - # we know for sure the result will be 0 - return [coeff], [], order_symbols - - # order commutative part canonically - c_part.sort(key=cls._args_sortkey) - - # current code expects coeff to be always in slot-0 - if coeff is not S.One: - c_part.insert(0, coeff) - - # we are done - if len(c_part) == 2 and c_part[0].is_Number and c_part[1].is_Add: - # 2*(1+a) -> 2 + 2 * a - coeff = c_part[0] - c_part = [Add(*[coeff*f for f in c_part[1].args])] - - return c_part, nc_part, order_symbols -
    - def _eval_power(b, e): - - # don't break up NC terms: (A*B)**3 != A**3*B**3, it is A*B*A*B*A*B - cargs, nc = b.args_cnc(split_1=False) - - if e.is_Integer: - return Mul(*[Pow(b, e, evaluate=False) for b in cargs]) * \ - Pow(Mul._from_args(nc), e, evaluate=False) - - p = Pow(b, e, evaluate=False) - - if e.is_Rational or e.is_Float: - return p._eval_expand_power_base() - - return p - - @classmethod - def class_key(cls): - return 3, 0, cls.__name__ - - def _eval_evalf(self, prec): - c, m = self.as_coeff_Mul() - if c is S.NegativeOne: - if m.is_Mul: - rv = -AssocOp._eval_evalf(m, prec) - else: - mnew = m._eval_evalf(prec) - if mnew is not None: - m = mnew - rv = -m - else: - rv = AssocOp._eval_evalf(self, prec) - if rv.is_number: - return rv.expand() - return rv - - @cacheit -
    [docs] def as_two_terms(self): - """Return head and tail of self. - - This is the most efficient way to get the head and tail of an - expression. - - - if you want only the head, use self.args[0]; - - if you want to process the arguments of the tail then use - self.as_coef_mul() which gives the head and a tuple containing - the arguments of the tail when treated as a Mul. - - if you want the coefficient when self is treated as an Add - then use self.as_coeff_add()[0] - - >>> from sympy.abc import x, y - >>> (3*x*y).as_two_terms() - (3, x*y) - """ - args = self.args - - if len(args) == 1: - return S.One, self - elif len(args) == 2: - return args - - else: - return args[0], self._new_rawargs(*args[1:]) -
    - @cacheit - def as_coeff_mul(self, *deps): - if deps: - l1 = [] - l2 = [] - for f in self.args: - if f.has(*deps): - l2.append(f) - else: - l1.append(f) - return self._new_rawargs(*l1), tuple(l2) - args = self.args - if args[0].is_Rational: - return args[0], args[1:] - elif args[0] is S.NegativeInfinity: - return S.NegativeOne, (-args[0],) + args[1:] - return S.One, args - -
    [docs] def as_coeff_Mul(self, rational=False): - """Efficiently extract the coefficient of a product. """ - coeff, args = self.args[0], self.args[1:] - - if coeff.is_Number and not (rational and not coeff.is_Rational): - if len(args) == 1: - return coeff, args[0] - else: - return coeff, self._new_rawargs(*args) - else: - return S.One, self -
    - def as_real_imag(self, deep=True, **hints): - other = [] - coeff = S(1) - for a in self.args: - if a.is_real: - coeff *= a - else: - other.append(a) - m = Mul(*other) - if hints.get('ignore') == m: - return None - else: - return (coeff*C.re(m), coeff*C.im(m)) - - @staticmethod - def _expandsums(sums): - """ - Helper function for _eval_expand_mul. - - sums must be a list of instances of Basic. - """ - - L = len(sums) - if L == 1: - return sums[0].args - terms = [] - left = Mul._expandsums(sums[:L//2]) - right = Mul._expandsums(sums[L//2:]) - - terms = [Mul(a, b) for a in left for b in right] - added = Add(*terms) - return Add.make_args(added) # it may have collapsed down to one term - - def _eval_expand_mul(self, **hints): - from sympy import fraction, expand_mul - - # Handle things like 1/(x*(x + 1)), which are automatically converted - # to 1/x*1/(x + 1) - expr = self - n, d = fraction(expr) - if d.is_Mul: - expr = n/d._eval_expand_mul(**hints) - if not expr.is_Mul: - return expand_mul(expr, deep=False) - - plain, sums, rewrite = [], [], False - for factor in expr.args: - if factor.is_Add: - sums.append(factor) - rewrite = True - else: - if factor.is_commutative: - plain.append(factor) - else: - sums.append(Basic(factor)) # Wrapper - - if not rewrite: - return expr - else: - plain = Mul(*plain) - if sums: - terms = Mul._expandsums(sums) - args = [] - for term in terms: - t = Mul(plain, term) - if t.is_Mul and any(a.is_Add for a in t.args): - t = t._eval_expand_mul() - args.append(t) - return Add(*args) - else: - return plain - - def _eval_derivative(self, s): - terms = list(self.args) - factors = [] - for i in range(len(terms)): - t = terms[i].diff(s) - if t is S.Zero: - continue - factors.append(Mul(*(terms[:i] + [t] + terms[i + 1:]))) - return Add(*factors) - - def _matches_simple(self, expr, repl_dict): - # handle (w*3).matches('x*5') -> {w: x*5/3} - coeff, terms = self.as_coeff_Mul() - terms = Mul.make_args(terms) - if len(terms) == 1: - newexpr = self.__class__._combine_inverse(expr, coeff) - return terms[0].matches(newexpr, repl_dict) - return - - def matches(self, expr, repl_dict={}, old=False): - expr = sympify(expr) - if self.is_commutative and expr.is_commutative: - return AssocOp._matches_commutative(self, expr, repl_dict, old) - elif self.is_commutative is not expr.is_commutative: - return None - c1, nc1 = self.args_cnc() - c2, nc2 = expr.args_cnc() - repl_dict = repl_dict.copy() - if c1: - if not c2: - c2 = [1] - a = Mul(*c1) - if isinstance(a, AssocOp): - repl_dict = a._matches_commutative(Mul(*c2), repl_dict, old) - else: - repl_dict = a.matches(Mul(*c2), repl_dict) - if repl_dict: - a = Mul(*nc1) - if isinstance(a, Mul): - repl_dict = a._matches(Mul(*nc2), repl_dict) - else: - repl_dict = a.matches(Mul(*nc2), repl_dict) - return repl_dict or None - - def _matches(self, expr, repl_dict={}): - # weed out negative one prefixes - sign = 1 - a, b = self.as_two_terms() - if a is S.NegativeOne: - if b.is_Mul: - sign = -sign - else: - # the remainder, b, is not a Mul anymore - return b.matches(-expr, repl_dict) - expr = sympify(expr) - if expr.is_Mul and expr.args[0] is S.NegativeOne: - expr = -expr - sign = -sign - - if not expr.is_Mul: - # expr can only match if it matches b and a matches +/- 1 - if len(self.args) == 2: - # quickly test for equality - if b == expr: - return a.matches(Rational(sign), repl_dict) - # do more expensive match - dd = b.matches(expr, repl_dict) - if dd is None: - return None - dd = a.matches(Rational(sign), dd) - return dd - return None - - d = repl_dict.copy() - - # weed out identical terms - pp = list(self.args) - ee = list(expr.args) - for p in self.args: - if p in expr.args: - ee.remove(p) - pp.remove(p) - - # only one symbol left in pattern -> match the remaining expression - if len(pp) == 1 and isinstance(pp[0], C.Wild): - if len(ee) == 1: - d[pp[0]] = sign * ee[0] - else: - d[pp[0]] = sign * expr.func(*ee) - return d - - if len(ee) != len(pp): - return None - - for p, e in zip(pp, ee): - d = p.xreplace(d).matches(e, d) - if d is None: - return None - return d - - @staticmethod - def _combine_inverse(lhs, rhs): - """ - Returns lhs/rhs, but treats arguments like symbols, so things like - oo/oo return 1, instead of a nan. - """ - if lhs == rhs: - return S.One - - def check(l, r): - if l.is_Float and r.is_comparable: - # if both objects are added to 0 they will share the same "normalization" - # and are more likely to compare the same. Since Add(foo, 0) will not allow - # the 0 to pass, we use __add__ directly. - return l.__add__(0) == r.evalf().__add__(0) - return False - if check(lhs, rhs) or check(rhs, lhs): - return S.One - if lhs.is_Mul and rhs.is_Mul: - a = list(lhs.args) - b = [1] - for x in rhs.args: - if x in a: - a.remove(x) - elif -x in a: - a.remove(-x) - b.append(-1) - else: - b.append(x) - return Mul(*a)/Mul(*b) - return lhs/rhs - - def as_powers_dict(self): - d = defaultdict(list) - for term in self.args: - b, e = term.as_base_exp() - d[b].append(e) - for b, e in d.items(): - if len(e) == 1: - e = e[0] - else: - e = Add(*e) - d[b] = e - return d - - def as_numer_denom(self): - # don't use _from_args to rebuild the numerators and denominators - # as the order is not guaranteed to be the same once they have - # been separated from each other - numers, denoms = list(zip(*[f.as_numer_denom() for f in self.args])) - return Mul(*numers), Mul(*denoms) - - def as_base_exp(self): - e1 = None - bases = [] - nc = 0 - for m in self.args: - b, e = m.as_base_exp() - if not b.is_commutative: - nc += 1 - if e1 is None: - e1 = e - elif e != e1 or nc > 1: - return self, S.One - bases.append(b) - return Mul(*bases), e1 - - def _eval_is_polynomial(self, syms): - return all(term._eval_is_polynomial(syms) for term in self.args) - - def _eval_is_rational_function(self, syms): - return all(term._eval_is_rational_function(syms) for term in self.args) - - _eval_is_bounded = lambda self: self._eval_template_is_attr('is_bounded') - _eval_is_integer = lambda self: self._eval_template_is_attr( - 'is_integer', when_multiple=None) - _eval_is_commutative = lambda self: self._eval_template_is_attr( - 'is_commutative') - - def _eval_is_polar(self): - has_polar = any(arg.is_polar for arg in self.args) - return has_polar and \ - all(arg.is_polar or arg.is_positive for arg in self.args) - - # I*I -> R, I*I*I -> -I - def _eval_is_real(self): - im_count = 0 - is_neither = False - for t in self.args: - if t.is_imaginary: - im_count += 1 - continue - t_real = t.is_real - if t_real: - continue - elif t_real is False: - if is_neither: - return None - else: - is_neither = True - else: - return None - if is_neither: - return False - - return (im_count % 2 == 0) - - def _eval_is_imaginary(self): - im_count = 0 - is_neither = False - for t in self.args: - if t.is_imaginary: - im_count += 1 - continue - t_real = t.is_real - if t_real: - continue - elif t_real is False: - if is_neither: - return None - else: - is_neither = True - else: - return None - if is_neither: - return False - - return (im_count % 2 == 1) - - def _eval_is_hermitian(self): - nc_count = 0 - im_count = 0 - is_neither = False - for t in self.args: - if not t.is_commutative: - nc_count += 1 - if nc_count > 1: - return None - if t.is_antihermitian: - im_count += 1 - continue - t_real = t.is_hermitian - if t_real: - continue - elif t_real is False: - if is_neither: - return None - else: - is_neither = True - else: - return None - if is_neither: - return False - - return (im_count % 2 == 0) - - def _eval_is_antihermitian(self): - nc_count = 0 - im_count = 0 - is_neither = False - for t in self.args: - if not t.is_commutative: - nc_count += 1 - if nc_count > 1: - return None - if t.is_antihermitian: - im_count += 1 - continue - t_real = t.is_hermitian - if t_real: - continue - elif t_real is False: - if is_neither: - return None - else: - is_neither = True - else: - return None - if is_neither: - return False - - return (im_count % 2 == 1) - - def _eval_is_irrational(self): - for t in self.args: - a = t.is_irrational - if a: - others = list(self.args) - others.remove(t) - if all(x.is_rational is True for x in others): - return True - return None - if a is None: - return - return False - - def _eval_is_zero(self): - zero = None - for a in self.args: - if a.is_zero: - zero = True - continue - bound = a.is_bounded - if not bound: - return bound - if zero: - return True - - def _eval_is_positive(self): - """Return True if self is positive, False if not, and None if it - cannot be determined. - - This algorithm is non-recursive and works by keeping track of the - sign which changes when a negative or nonpositive is encountered. - Whether a nonpositive or nonnegative is seen is also tracked since - the presence of these makes it impossible to return True, but - possible to return False if the end result is nonpositive. e.g. - - pos * neg * nonpositive -> pos or zero -> None is returned - pos * neg * nonnegative -> neg or zero -> False is returned - """ - - sign = 1 - saw_NON = False - for t in self.args: - if t.is_positive: - continue - elif t.is_negative: - sign = -sign - elif t.is_zero: - return False - elif t.is_nonpositive: - sign = -sign - saw_NON = True - elif t.is_nonnegative: - saw_NON = True - else: - return - if sign == 1 and saw_NON is False: - return True - if sign < 0: - return False - - def _eval_is_negative(self): - """Return True if self is negative, False if not, and None if it - cannot be determined. - - This algorithm is non-recursive and works by keeping track of the - sign which changes when a negative or nonpositive is encountered. - Whether a nonpositive or nonnegative is seen is also tracked since - the presence of these makes it impossible to return True, but - possible to return False if the end result is nonnegative. e.g. - - pos * neg * nonpositive -> pos or zero -> False is returned - pos * neg * nonnegative -> neg or zero -> None is returned - """ - - sign = 1 - saw_NON = False - for t in self.args: - if t.is_positive: - continue - elif t.is_negative: - sign = -sign - elif t.is_zero: - return False - elif t.is_nonpositive: - sign = -sign - saw_NON = True - elif t.is_nonnegative: - saw_NON = True - else: - return - if sign == -1 and saw_NON is False: - return True - if sign > 0: - return False - - def _eval_is_odd(self): - is_integer = self.is_integer - - if is_integer: - r = True - for t in self.args: - if t.is_even: - return False - if t.is_odd is None: - r = None - return r - - # !integer -> !odd - elif is_integer is False: - return False - - def _eval_is_even(self): - is_integer = self.is_integer - - if is_integer: - return fuzzy_not(self._eval_is_odd()) - - elif is_integer is False: - return False - - def _eval_subs(self, old, new): - from sympy import sign, multiplicity - from sympy.simplify.simplify import powdenest, fraction - - if not old.is_Mul: - return None - - if old.args[0] == -1: - return self._subs(-old, -new) - - def base_exp(a): - # if I and -1 are in a Mul, they get both end up with - # a -1 base (see issue 3322); all we want here are the - # true Pow or exp separated into base and exponent - if a.is_Pow or a.func is C.exp: - return a.as_base_exp() - return a, S.One - - def breakup(eq): - """break up powers of eq when treated as a Mul: - b**(Rational*e) -> b**e, Rational - commutatives come back as a dictionary {b**e: Rational} - noncommutatives come back as a list [(b**e, Rational)] - """ - - (c, nc) = (defaultdict(int), list()) - for a in Mul.make_args(eq): - a = powdenest(a) - (b, e) = base_exp(a) - if e is not S.One: - (co, _) = e.as_coeff_mul() - b = Pow(b, e/co) - e = co - if a.is_commutative: - c[b] += e - else: - nc.append([b, e]) - return (c, nc) - - def rejoin(b, co): - """ - Put rational back with exponent; in general this is not ok, but - since we took it from the exponent for analysis, it's ok to put - it back. - """ - - (b, e) = base_exp(b) - return Pow(b, e*co) - - def ndiv(a, b): - """if b divides a in an extractive way (like 1/4 divides 1/2 - but not vice versa, and 2/5 does not divide 1/3) then return - the integer number of times it divides, else return 0. - """ - if not b.q % a.q or not a.q % b.q: - return int(a/b) - return 0 - - # give Muls in the denominator a chance to be changed (see issue 2552) - # rv will be the default return value - rv = None - n, d = fraction(self) - if d is not S.One: - self2 = n._subs(old, new)/d._subs(old, new) - if not self2.is_Mul: - return self2._subs(old, new) - if self2 != self: - self = rv = self2 - - # Now continue with regular substitution. - - # handle the leading coefficient and use it to decide if anything - # should even be started; we always know where to find the Rational - # so it's a quick test - - co_self = self.args[0] - co_old = old.args[0] - co_xmul = None - if co_old.is_Rational and co_self.is_Rational: - # if coeffs are the same there will be no updating to do - # below after breakup() step; so skip (and keep co_xmul=None) - if co_old != co_self: - co_xmul = co_self.extract_multiplicatively(co_old) - elif co_old.is_Rational: - return rv - - # break self and old into factors - - (c, nc) = breakup(self) - (old_c, old_nc) = breakup(old) - - # update the coefficients if we had an extraction - # e.g. if co_self were 2*(3/35*x)**2 and co_old = 3/5 - # then co_self in c is replaced by (3/5)**2 and co_residual - # is 2*(1/7)**2 - - if co_xmul and co_xmul.is_Rational: - n_old, d_old = co_old.as_numer_denom() - n_self, d_self = co_self.as_numer_denom() - - def _multiplicity(p, n): - p = abs(p) - if p is S.One: - return S.Infinity - return multiplicity(p, abs(n)) - mult = S(min(_multiplicity(n_old, n_self), - _multiplicity(d_old, d_self))) - c.pop(co_self) - c[co_old] = mult - co_residual = co_self/co_old**mult - else: - co_residual = 1 - - # do quick tests to see if we can't succeed - - ok = True - if len(old_nc) > len(nc): - # more non-commutative terms - ok = False - elif len(old_c) > len(c): - # more commutative terms - ok = False - elif set(i[0] for i in old_nc).difference(set(i[0] for i in nc)): - # unmatched non-commutative bases - ok = False - elif set(old_c).difference(set(c)): - # unmatched commutative terms - ok = False - elif any(sign(c[b]) != sign(old_c[b]) for b in old_c): - # differences in sign - ok = False - if not ok: - return rv - - if not old_c: - cdid = None - else: - rat = [] - for (b, old_e) in list(old_c.items()): - c_e = c[b] - rat.append(ndiv(c_e, old_e)) - if not rat[-1]: - return rv - cdid = min(rat) - - if not old_nc: - ncdid = None - for i in range(len(nc)): - nc[i] = rejoin(*nc[i]) - else: - ncdid = 0 # number of nc replacements we did - take = len(old_nc) # how much to look at each time - limit = cdid or S.Infinity # max number that we can take - failed = [] # failed terms will need subs if other terms pass - i = 0 - while limit and i + take <= len(nc): - hit = False - - # the bases must be equivalent in succession, and - # the powers must be extractively compatible on the - # first and last factor but equal inbetween. - - rat = [] - for j in range(take): - if nc[i + j][0] != old_nc[j][0]: - break - elif j == 0: - rat.append(ndiv(nc[i + j][1], old_nc[j][1])) - elif j == take - 1: - rat.append(ndiv(nc[i + j][1], old_nc[j][1])) - elif nc[i + j][1] != old_nc[j][1]: - break - else: - rat.append(1) - j += 1 - else: - ndo = min(rat) - if ndo: - if take == 1: - if cdid: - ndo = min(cdid, ndo) - nc[i] = Pow(new, ndo)*rejoin(nc[i][0], - nc[i][1] - ndo*old_nc[0][1]) - else: - ndo = 1 - - # the left residual - - l = rejoin(nc[i][0], nc[i][1] - ndo* - old_nc[0][1]) - - # eliminate all middle terms - - mid = new - - # the right residual (which may be the same as the middle if take == 2) - - ir = i + take - 1 - r = (nc[ir][0], nc[ir][1] - ndo* - old_nc[-1][1]) - if r[1]: - if i + take < len(nc): - nc[i:i + take] = [l*mid, r] - else: - r = rejoin(*r) - nc[i:i + take] = [l*mid*r] - else: - - # there was nothing left on the right - - nc[i:i + take] = [l*mid] - - limit -= ndo - ncdid += ndo - hit = True - if not hit: - - # do the subs on this failing factor - - failed.append(i) - i += 1 - else: - - if not ncdid: - return rv - - # although we didn't fail, certain nc terms may have - # failed so we rebuild them after attempting a partial - # subs on them - - failed.extend(list(range(i, len(nc)))) - for i in failed: - nc[i] = rejoin(*nc[i]).subs(old, new) - - # rebuild the expression - - if cdid is None: - do = ncdid - elif ncdid is None: - do = cdid - else: - do = min(ncdid, cdid) - - margs = [] - for b in c: - if b in old_c: - - # calculate the new exponent - - e = c[b] - old_c[b]*do - margs.append(rejoin(b, e)) - else: - margs.append(rejoin(b.subs(old, new), c[b])) - if cdid and not ncdid: - - # in case we are replacing commutative with non-commutative, - # we want the new term to come at the front just like the - # rest of this routine - - margs = [Pow(new, cdid)] + margs - return co_residual*Mul(*margs)*Mul(*nc) - - def _eval_nseries(self, x, n, logx): - from sympy import powsimp - terms = [t.nseries(x, n=n, logx=logx) for t in self.args] - return powsimp(Mul(*terms).expand(), combine='exp', deep=True) - - def _eval_as_leading_term(self, x): - return Mul(*[t.as_leading_term(x) for t in self.args]) - - def _eval_conjugate(self): - return Mul(*[t.conjugate() for t in self.args]) - - def _eval_transpose(self): - return Mul(*[t.transpose() for t in self.args[::-1]]) - - def _eval_adjoint(self): - return Mul(*[t.adjoint() for t in self.args[::-1]]) - - def _sage_(self): - s = 1 - for x in self.args: - s *= x._sage_() - return s - -
    [docs] def as_content_primitive(self, radical=False): - """Return the tuple (R, self/R) where R is the positive Rational - extracted from self. - - Examples - ======== - - >>> from sympy import sqrt - >>> (-3*sqrt(2)*(2 - 2*sqrt(2))).as_content_primitive() - (6, -sqrt(2)*(-sqrt(2) + 1)) - - See docstring of Expr.as_content_primitive for more examples. - """ - - coef = S.One - args = [] - for i, a in enumerate(self.args): - c, p = a.as_content_primitive(radical=radical) - coef *= c - if p is not S.One: - args.append(p) - # don't use self._from_args here to reconstruct args - # since there may be identical args now that should be combined - # e.g. (2+2*x)*(3+3*x) should be (6, (1 + x)**2) not (6, (1+x)*(1+x)) - return coef, Mul(*args) -
    -
    [docs] def as_ordered_factors(self, order=None): - """Transform an expression into an ordered list of factors. - - Examples - ======== - - >>> from sympy import sin, cos - >>> from sympy.abc import x, y - - >>> (2*x*y*sin(x)*cos(x)).as_ordered_factors() - [2, x, y, sin(x), cos(x)] - - """ - cpart, ncpart = self.args_cnc() - cpart.sort(key=lambda expr: expr.sort_key(order=order)) - return cpart + ncpart -
    - @property - def _sorted_args(self): - return self.as_ordered_factors() - -
    -
    [docs]def prod(a, start=1): - """Return product of elements of a. Start with int 1 so if only - ints are included then an int result is returned. - - Examples - ======== - - >>> from sympy import prod, S - >>> prod(list(range(3))) - 0 - >>> type(_) is int - True - >>> prod([S(2), 3]) - 6 - >>> _.is_Integer - True - - You can start the product at something other than 1: - >>> prod([1, 2], 3) - 6 - - """ - return reduce(operator.mul, a, start) - -
    -def _keep_coeff(coeff, factors, clear=True): - """Return ``coeff*factors`` unevaluated if necessary. - - If clear is False, do not keep the coefficient as a factor - if it can be distributed on a single factor such that one or - more terms will still have integer coefficients. - - Examples - ======== - - >>> from sympy.core.mul import _keep_coeff - >>> from sympy.abc import x, y - >>> from sympy import S - - >>> _keep_coeff(S.Half, x + 2) - (x + 2)/2 - >>> _keep_coeff(S.Half, x + 2, clear=False) - x/2 + 1 - >>> _keep_coeff(S.Half, (x + 2)*y, clear=False) - y*(x + 2)/2 - """ - - if not coeff.is_Number: - if factors.is_Number: - factors, coeff = coeff, factors - else: - return coeff*factors - if coeff is S.One: - return factors - elif coeff is S.NegativeOne: # don't keep sign? - return -factors - elif factors.is_Add: - if not clear and coeff.is_Rational and coeff.q != 1: - q = S(coeff.q) - for i in factors.args: - c, t = i.as_coeff_Mul() - r = c/q - if r == int(r): - return coeff*factors - return Mul._from_args((coeff, factors)) - elif factors.is_Mul: - margs = list(factors.args) - if margs[0].is_Number: - margs[0] *= coeff - if margs[0] == 1: - margs.pop(0) - else: - margs.insert(0, coeff) - return Mul._from_args(margs) - else: - return coeff*factors - -from .numbers import Rational, igcd, ilcm, Integer -def _rgcd(a, b): - return Rational(Integer(igcd(a.p, b.p)), Integer(ilcm(a.q, b.q))) - -from .power import Pow -from .add import Add -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/core/multidimensional.html b/dev-py3k/_modules/sympy/core/multidimensional.html deleted file mode 100644 index 38a3e91215a..00000000000 --- a/dev-py3k/_modules/sympy/core/multidimensional.html +++ /dev/null @@ -1,247 +0,0 @@ - - - - - - - - - - sympy.core.multidimensional — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.core.multidimensional

    -"""
    -Provides functionality for multidimensional usage of scalar-functions.
    -
    -Read the vectorize docstring for more details.
    -"""
    -from sympy.core.decorators import wraps
    -
    -
    -def apply_on_element(f, args, kwargs, n):
    -    """
    -    Returns a structure with the same dimension as the specified argument,
    -    where each basic element is replaced by the function f applied on it. All
    -    other arguments stay the same.
    -    """
    -    # Get the specified argument.
    -    if isinstance(n, int):
    -        structure = args[n]
    -        is_arg = True
    -    elif isinstance(n, str):
    -        structure = kwargs[n]
    -        is_arg = False
    -
    -    # Define reduced function that is only dependend of the specified argument.
    -    def f_reduced(x):
    -        if hasattr(x, "__iter__"):
    -            return list(map(f_reduced, x))
    -        else:
    -            if is_arg:
    -                args[n] = x
    -            else:
    -                kwargs[n] = x
    -            return f(*args, **kwargs)
    -
    -    # f_reduced will call itself recursively so that in the end f is applied to
    -    # all basic elements.
    -    return list(map(f_reduced, structure))
    -
    -
    -def iter_copy(structure):
    -    """
    -    Returns a copy of an iterable object (also copying all embedded iterables).
    -    """
    -    l = []
    -    for i in structure:
    -        if hasattr(i, "__iter__"):
    -            l.append(iter_copy(i))
    -        else:
    -            l.append(i)
    -    return l
    -
    -
    -def structure_copy(structure):
    -    """
    -    Returns a copy of the given structure (numpy-array, list, iterable, ..).
    -    """
    -    if hasattr(structure, "copy"):
    -        return structure.copy()
    -    return iter_copy(structure)
    -
    -
    -
    [docs]class vectorize: - """ - Generalizes a function taking scalars to accept multidimensional arguments. - - For example:: - - (1) @vectorize(0) - def sin(x): - .... - - sin([1, x, y]) - --> [sin(1), sin(x), sin(y)] - - (2) @vectorize(0,1) - def diff(f(y), y) - .... - - diff([f(x,y,z),g(x,y,z),h(x,y,z)], [x,y,z]) - --> [[d/dx f, d/dy f, d/dz f], [d/dx g, d/dy g, d/dz g], [d/dx h, d/dy h, d/dz h]] - """ - def __init__(self, *mdargs): - """ - The given numbers and strings characterize the arguments that will be - treated as data structures, where the decorated function will be applied - to every single element. - If no argument is given, everything is treated multidimensional. - """ - for a in mdargs: - assert isinstance(a, (int, str)) - self.mdargs = mdargs - - def __call__(self, f): - """ - Returns a wrapper for the one-dimensional function that can handle - multidimensional arguments. - """ - @wraps(f) - def wrapper(*args, **kwargs): - # Get arguments that should be treated multidimensional - if self.mdargs: - mdargs = self.mdargs - else: - mdargs = list(range(len(args))) + list(kwargs.keys()) - - arglength = len(args) - - for n in mdargs: - if isinstance(n, int): - if n >= arglength: - continue - entry = args[n] - is_arg = True - elif isinstance(n, str): - try: - entry = kwargs[n] - except KeyError: - continue - is_arg = False - if hasattr(entry, "__iter__"): - # Create now a copy of the given array and manipulate then - # the entries directly. - if is_arg: - args = list(args) - args[n] = structure_copy(entry) - else: - kwargs[n] = structure_copy(entry) - result = apply_on_element(wrapper, args, kwargs, n) - return result - return f(*args, **kwargs) - return wrapper
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/core/numbers.html b/dev-py3k/_modules/sympy/core/numbers.html deleted file mode 100644 index 3ddacf1fb99..00000000000 --- a/dev-py3k/_modules/sympy/core/numbers.html +++ /dev/null @@ -1,2820 +0,0 @@ - - - - - - - - - - sympy.core.numbers — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.core.numbers

    -from .core import C
    -from .sympify import converter, sympify, _sympify, SympifyError
    -from .basic import Basic
    -from .singleton import S, Singleton
    -from .expr import Expr, AtomicExpr
    -from .decorators import _sympifyit, deprecated, call_highest_priority
    -from .cache import cacheit, clear_cache
    -from sympy.core.compatibility import as_int
    -import sympy.mpmath as mpmath
    -import sympy.mpmath.libmp as mlib
    -from sympy.mpmath.libmp import mpf_pow, mpf_pi, mpf_e, phi_fixed
    -from sympy.mpmath.ctx_mp import mpnumeric
    -from sympy.mpmath.libmp.libmpf import (
    -    fnan as _mpf_nan, fzero as _mpf_zero, _normalize as mpf_normalize)
    -
    -import decimal
    -import math
    -import re as regex
    -from collections import defaultdict
    -
    -rnd = mlib.round_nearest
    -
    -_LOG2 = math.log(2)
    -
    -
    -def mpf_norm(mpf, prec):
    -    """Return the mpf tuple normalized appropriately for the indicated
    -    precision.
    -
    -    This also contains a portion of code to not return zero if
    -    the mantissa is 0; this is a necessary (but not sufficient) test of
    -    zero since the mantissa for mpf's values "+inf", "-inf" and "nan" have
    -    a mantissa of zero, too.
    -    """
    -    sign, man, expt, bc = mpf
    -    if not man:
    -        # hack for mpf_normalize which does not do this;
    -        # it assumes that if man is zero the result is 0
    -        # (see issue 3540)
    -        if not bc:
    -            return _mpf_zero
    -        else:
    -            # don't change anything; this should already
    -            # be a well formed mpf tuple
    -            return mpf
    -    rv = mpf_normalize(sign, man, expt, bc, prec, rnd)
    -    return rv
    -
    -# TODO: we should use the warnings module
    -_errdict = {"divide": False}
    -
    -
    -
    [docs]def seterr(divide=False): - """ - Should sympy raise an exception on 0/0 or return a nan? - - divide == True .... raise an exception - divide == False ... return nan - """ - if _errdict["divide"] != divide: - clear_cache() - _errdict["divide"] = divide - -
    -def _as_integer_ratio(p): - """compatibility implementation for python < 2.6""" - neg_pow, man, expt, bc = getattr(p, '_mpf_', mpmath.mpf(p)._mpf_) - p = [1, -1][neg_pow % 2]*man - if expt < 0: - q = 2**-expt - else: - q = 1 - p *= 2**expt - return p, q - - -def _decimal_to_Rational_prec(dec): - """Convert an ordinary decimal instance to a Rational.""" - # _is_special is needed for Python 2.5 support; is_finite for Python 3.3 - # support - nonfinite = getattr(dec, '_is_special', None) - if nonfinite is None: - nonfinite = not dec.is_finite() - if nonfinite: - raise TypeError("dec must be finite, got %s." % dec) - s, d, e = dec.as_tuple() - prec = len(d) - if e >= 0: # it's an integer - rv = Integer(int(dec)) - else: - s = (-1)**s - d = sum([di*10**i for i, di in enumerate(reversed(d))]) - rv = Rational(s*d, 10**-e) - return rv, prec - - -def _literal_float(f): - """Return True if n can be interpreted as a floating point number.""" - pat = r"[-+]?((\d*\.\d+)|(\d+\.?))(eE[-+]?\d+)?" - return bool(regex.match(pat, f)) - -# (a,b) -> gcd(a,b) -_gcdcache = {} - -# TODO caching with decorator, but not to degrade performance - - -
    [docs]def igcd(a, b): - """Computes positive, integer greatest common divisor of two numbers. - - The algorithm is based on the well known Euclid's algorithm. To - improve speed, igcd() has its own caching mechanism implemented. - """ - try: - return _gcdcache[(a, b)] - except KeyError: - a, b = as_int(a), as_int(b) - - if a and b: - if b < 0: - b = -b - - while b: - a, b = b, a % b - else: - a = abs(a or b) - - _gcdcache[(a, b)] = a - return a - -
    -
    [docs]def ilcm(a, b): - """Computes integer least common multiple of two numbers. """ - if a == 0 and b == 0: - return 0 - else: - return a*b // igcd(a, b) - -
    -def igcdex(a, b): - """Returns x, y, g such that g = x*a + y*b = gcd(a, b). - - >>> from sympy.core.numbers import igcdex - >>> igcdex(2, 3) - (-1, 1, 1) - >>> igcdex(10, 12) - (-1, 1, 2) - - >>> x, y, g = igcdex(100, 2004) - >>> x, y, g - (-20, 1, 4) - >>> x*100 + y*2004 - 4 - - """ - if (not a) and (not b): - return (0, 1, 0) - - if not a: - return (0, b//abs(b), abs(b)) - if not b: - return (a//abs(a), 0, abs(a)) - - if a < 0: - a, x_sign = -a, -1 - else: - x_sign = 1 - - if b < 0: - b, y_sign = -b, -1 - else: - y_sign = 1 - - x, y, r, s = 1, 0, 0, 1 - - while b: - (c, q) = (a % b, a // b) - (a, b, r, s, x, y) = (b, c, x - q*r, y - q*s, r, s) - - return (x*x_sign, y*y_sign, a) - - -
    [docs]class Number(AtomicExpr): - """ - Represents any kind of number in sympy. - - Floating point numbers are represented by the Float class. - Integer numbers (of any size), together with rational numbers (again, - there is no limit on their size) are represented by the Rational class. - - If you want to represent, for example, ``1+sqrt(2)``, then you need to do:: - - Rational(1) + sqrt(Rational(2)) - """ - is_commutative = True - is_bounded = True - is_finite = True - is_number = True - - __slots__ = [] - - # Used to make max(x._prec, y._prec) return x._prec when only x is a float - _prec = -1 - - is_Number = True - - def __new__(cls, *obj): - if len(obj) == 1: - obj = obj[0] - - if isinstance(obj, Number): - return obj - if isinstance(obj, int): - return Integer(obj) - if isinstance(obj, tuple) and len(obj) == 2: - return Rational(*obj) - if isinstance(obj, (float, mpmath.mpf, decimal.Decimal)): - return Float(obj) - if isinstance(obj, str): - val = sympify(obj) - if isinstance(val, Number): - return val - else: - raise ValueError('String "%s" does not denote a Number' % obj) - if isinstance(obj, Number): - return obj - msg = "expected str|int|long|float|Decimal|Number object but got %r" - raise TypeError(msg % type(obj).__name__) - - def __divmod__(self, other): - from .containers import Tuple - from sympy.functions.elementary.complexes import sign - - try: - other = Number(other) - except TypeError: - msg = "unsupported operand type(s) for divmod(): '%s' and '%s'" - raise TypeError(msg % (type(self).__name__, type(other).__name__)) - if not other: - raise ZeroDivisionError('modulo by zero') - if self.is_Integer and other.is_Integer: - return Tuple(*divmod(self.p, other.p)) - else: - rat = self/other - w = sign(rat)*int(abs(rat)) # = rat.floor() - r = self - other*w - #w*other + r == self - return Tuple(w, r) - - def __rdivmod__(self, other): - try: - other = Number(other) - except TypeError: - msg = "unsupported operand type(s) for divmod(): '%s' and '%s'" - raise TypeError(msg % (type(other).__name__, type(self).__name__)) - return divmod(other, self) - - def __round__(self, *args): - return round(float(self), *args) - - def _as_mpf_val(self, prec): - """Evaluation of mpf tuple accurate to at least prec bits.""" - raise NotImplementedError('%s needs ._as_mpf_val() method' % - (self.__class__.__name__)) - - def _eval_evalf(self, prec): - return Float._new(self._as_mpf_val(prec), prec) - - def _as_mpf_op(self, prec): - prec = max(prec, self._prec) - return self._as_mpf_val(prec), prec - - def __float__(self): - return mlib.to_float(self._as_mpf_val(53)) - - def _eval_conjugate(self): - return self - - def _eval_order(self, *symbols): - # Order(5, x, y) -> Order(1,x,y) - return C.Order(S.One, *symbols) - - def _eval_subs(self, old, new): - if old == -self: - return -new - return self # there is no other possibility - - @classmethod - def class_key(cls): - return 1, 0, 'Number' - - @cacheit - def sort_key(self, order=None): - return self.class_key(), (0, ()), (), self - - @_sympifyit('other', NotImplemented) - def __add__(self, other): - if isinstance(other, Number): - if other is S.NaN: - return S.NaN - elif other is S.Infinity: - return S.Infinity - elif other is S.NegativeInfinity: - return S.NegativeInfinity - return AtomicExpr.__add__(self, other) - - @_sympifyit('other', NotImplemented) - def __sub__(self, other): - if isinstance(other, Number): - if other is S.NaN: - return S.NaN - elif other is S.Infinity: - return S.NegativeInfinity - elif other is S.NegativeInfinity: - return S.Infinity - return AtomicExpr.__sub__(self, other) - - @_sympifyit('other', NotImplemented) - def __mul__(self, other): - if isinstance(other, Number): - if other is S.NaN: - return S.NaN - elif other is S.Infinity: - if self.is_zero: - return S.NaN - elif self.is_positive: - return S.Infinity - else: - return S.NegativeInfinity - elif other is S.NegativeInfinity: - if self.is_zero: - return S.NaN - elif self.is_positive: - return S.NegativeInfinity - else: - return S.Infinity - return AtomicExpr.__mul__(self, other) - - @_sympifyit('other', NotImplemented) - def __div__(self, other): - if isinstance(other, Number): - if other is S.NaN: - return S.NaN - elif other is S.Infinity or other is S.NegativeInfinity: - return S.Zero - return AtomicExpr.__div__(self, other) - - __truediv__ = __div__ - - def __eq__(self, other): - raise NotImplementedError('%s needs .__eq__() method' % - (self.__class__.__name__)) - - def __ne__(self, other): - raise NotImplementedError('%s needs .__ne__() method' % - (self.__class__.__name__)) - - def __lt__(self, other): - raise NotImplementedError('%s needs .__lt__() method' % - (self.__class__.__name__)) - - def __le__(self, other): - raise NotImplementedError('%s needs .__le__() method' % - (self.__class__.__name__)) - - def __gt__(self, other): - return _sympify(other).__lt__(self) - - def __ge__(self, other): - return _sympify(other).__le__(self) - - def __hash__(self): - return super(Number, self).__hash__() - - def is_constant(self, *wrt, **flags): - return True - - @property - def is_number(self): - return True - - def as_coeff_mul(self, *deps): - # a -> c*t - if self.is_Rational: - return self, tuple() - elif self.is_negative: - return S.NegativeOne, (-self,) - return S.One, (self,) - - def as_coeff_add(self, *deps): - # a -> c + t - if self.is_Rational: - return self, tuple() - return S.Zero, (self,) - -
    [docs] def as_coeff_Mul(self, rational=False): - """Efficiently extract the coefficient of a product. """ - if rational and not self.is_Rational: - return S.One, self - return self, S.One -
    -
    [docs] def as_coeff_Add(self): - """Efficiently extract the coefficient of a summation. """ - return self, S.Zero -
    -
    [docs] def gcd(self, other): - """Compute GCD of `self` and `other`. """ - from sympy.polys import gcd - return gcd(self, other) -
    -
    [docs] def lcm(self, other): - """Compute LCM of `self` and `other`. """ - from sympy.polys import lcm - return lcm(self, other) -
    -
    [docs] def cofactors(self, other): - """Compute GCD and cofactors of `self` and `other`. """ - from sympy.polys import cofactors - return cofactors(self, other) - -
    -
    [docs]class Float(Number): - """ - Represents a floating point number. It is capable of representing - arbitrary-precision floating-point numbers. - - Examples - ======== - - >>> from sympy import Float, pi - >>> Float(3.5) - 3.50000000000000 - >>> Float(3) - 3.00000000000000 - - Floats can be created from a string representations of Python floats - to force ints to Float or to enter high-precision (> 15 significant - digits) values: - - >>> Float('.0010') - 0.00100000000000000 - >>> Float('1e-3') - 0.00100000000000000 - >>> Float('1e-3', 3) - 0.00100 - - Float can automatically count significant figures if a null string - is sent for the precision; space are also allowed in the string. (Auto- - counting is only allowed for strings, ints and longs). - - >>> Float('123 456 789 . 123 456', '') - 123456789.123456 - >>> Float('12e-3', '') - 0.012 - >>> Float(3, '') - 3. - - If a number is written in scientific notation, only the digits before the - exponent are considered significant if a decimal appears, otherwise the - "e" signifies only how to move the decimal: - - >>> Float('60.e2', '') # 2 digits significant - 6.0e+3 - >>> Float('60e2', '') # 4 digits significant - 6000. - >>> Float('600e-2', '') # 3 digits significant - 6.00 - - Notes - ===== - - Floats are inexact by their nature unless their value is a binary-exact - value. - - >>> approx, exact = Float(.1, 1), Float(.125, 1) - - For calculation purposes, evalf needs to be able to change the precision - but this will not increase the accuracy of the inexact value. The - following is the most accurate 5-digit approximation of a value of 0.1 - that had only 1 digit of precision: - - >>> approx.evalf(5) - 0.099609 - - By contrast, 0.125 is exact in binary (as it is in base 10) and so it - can be passed to Float or evalf to obtain an arbitrary precision with - matching accuracy: - - >>> Float(exact, 5) - 0.12500 - >>> exact.evalf(20) - 0.12500000000000000000 - - Trying to make a high-precision Float from a float is not disallowed, - but one must keep in mind that the *underlying float* (not the apparent - decimal value) is being obtained with high precision. For example, 0.3 - does not have a finite binary representation. The closest rational is - the fraction 5404319552844595/2**54. So if you try to obtain a Float of - 0.3 to 20 digits of precision you will not see the same thing as 0.3 - followed by 19 zeros: - - >>> Float(0.3, 20) - 0.29999999999999998890 - - If you want a 20-digit value of the decimal 0.3 (not the floating point - approximation of 0.3) you should send the 0.3 as a string. The underlying - representation is still binary but a higher precision than Python's float - is used: - - >>> Float('0.3', 20) - 0.30000000000000000000 - - Although you can increase the precision of an existing Float using Float - it will not increase the accuracy -- the underlying value is not changed: - - >>> def show(f): # binary rep of Float - ... from sympy import Mul, Pow - ... s, m, e, b = f._mpf_ - ... v = Mul(m, Pow(2, e, evaluate=False), evaluate=False) - ... print('%s at prec=%s' % (v, f._prec)) - ... - >>> t = Float('0.3', 3) - >>> show(t) - 4915/2**14 at prec=13 - >>> show(Float(t, 20)) # higher prec, not higher accuracy - 4915/2**14 at prec=70 - >>> show(Float(t, 2)) # lower prec - 307/2**10 at prec=10 - - The same thing happens when evalf is used on a Float: - - >>> show(t.evalf(20)) - 4915/2**14 at prec=70 - >>> show(t.evalf(2)) - 307/2**10 at prec=10 - - Finally, Floats can be instantiated with an mpf tuple (n, c, p) to - produce the number (-1)**n*c*2**p: - - >>> n, c, p = 1, 5, 0 - >>> (-1)**n*c*2**p - -5 - >>> Float((1, 5, 0)) - -5.00000000000000 - - An actual mpf tuple also contains the number of bits in c as the last - element of the tuple, but this is not needed for instantiation: - - >>> _._mpf_ - (1, 5, 0, 3) - - That last value can be used to create the corresponding Float, however, - by passing the tuple and prec='' to Float to copy the precision from the - mpf tuple: - - >>> Float(pi.n(5)._mpf_, '') - 3.1416 - - """ - __slots__ = ['_mpf_', '_prec'] - - is_real = True - is_irrational = False - is_integer = False - - is_Float = True - - def __new__(cls, num, prec=15): - if isinstance(num, str): - num = num.replace(' ', '') - if num.startswith('.') and len(num) > 1: - num = '0' + num - elif num.startswith('-.') and len(num) > 2: - num = '-0.' + num[2:] - elif isinstance(num, float) and num == 0: - num = '0' - elif isinstance(num, (int, Integer)): - num = str(num) # faster than mlib.from_int - elif isinstance(num, mpmath.mpf): - num = num._mpf_ - - if prec == '': - if type(num) is tuple and len(num) == 4: - return Float._new(num, num[-1]) - elif not isinstance(num, str): - raise ValueError('The null string can only be used when ' - 'the number to Float is passed as a string or is a tuple ' - 'with length of 4.') - ok = None - if _literal_float(num): - try: - Num = decimal.Decimal(num) - except decimal.InvalidOperation: - pass - else: - isint = '.' not in num - num, dps = _decimal_to_Rational_prec(Num) - if num.is_Integer and isint: - dps = max(dps, len(str(num).lstrip('-'))) - ok = True - if ok is None: - raise ValueError('string-float not recognized: %s' % num) - else: - dps = prec - - prec = mlib.libmpf.dps_to_prec(dps) - if isinstance(num, float): - _mpf_ = mlib.from_float(num, prec, rnd) - elif isinstance(num, str): - _mpf_ = mlib.from_str(num, prec, rnd) - elif isinstance(num, decimal.Decimal): - _mpf_ = mlib.from_str(str(num), prec, rnd) - elif isinstance(num, Rational): - _mpf_ = mlib.from_rational(num.p, num.q, prec, rnd) - elif isinstance(num, tuple) and len(num) in (3, 4): - if type(num[1]) is str: - # it's a hexadecimal (coming from a pickled object) - # assume that it is in standard form - num = list(num) - num[1] = int(num[1], 16) - _mpf_ = tuple(num) - else: - if not num[1] and len(num) == 4: - # handle normalization hack - return Float._new(num, prec) - else: - _mpf_ = mpmath.mpf( - S.NegativeOne**num[0]*num[1]*2**num[2])._mpf_ - elif isinstance(num, Float): - _mpf_ = num._mpf_ - if prec < num._prec: - _mpf_ = mpf_norm(_mpf_, prec) - else: - _mpf_ = mpmath.mpf(num)._mpf_ - - # special cases - if _mpf_ == _mpf_zero: - pass # we want a Float - elif _mpf_ == _mpf_nan: - return S.NaN - - obj = Expr.__new__(cls) - obj._mpf_ = _mpf_ - obj._prec = prec - return obj - - @classmethod - def _new(cls, _mpf_, _prec): - # special cases - if _mpf_ == _mpf_zero: - return S.Zero # XXX this is different from Float which gives 0.0 - elif _mpf_ == _mpf_nan: - return S.NaN - - # the new Float should be normalized unless it is - # an integer because Float doesn't return Floats - # for Integers. If Integers can become Floats then - # all the following (up to the first 'obj =' line - # can be replaced with ok = mpf_norm(_mpf_, _prec) - - sign, man, expt, bc = _mpf_ - if not man: - # hack for mpf_normalize which does not do this - if not bc: - ok = _mpf_zero - else: - ok = (sign % 2, int(man), expt, bc) - elif expt < 0: - # this is the non-hack normalization - ok = mpf_normalize(sign, man, expt, bc, _prec, rnd) - else: - ok = _mpf_ - - obj = Expr.__new__(cls) - obj._mpf_ = ok - obj._prec = _prec - return obj - - # mpz can't be pickled - def __getnewargs__(self): - return (mlib.to_pickable(self._mpf_),) - - def __getstate__(self): - return {'_prec': self._prec} - - def _hashable_content(self): - return (self._mpf_, self._prec) - - def floor(self): - return C.Integer(int(mlib.to_int( - mlib.mpf_floor(self._mpf_, self._prec)))) - - def ceiling(self): - return C.Integer(int(mlib.to_int( - mlib.mpf_ceil(self._mpf_, self._prec)))) - - @property - def num(self): - return mpmath.mpf(self._mpf_) - - def _as_mpf_val(self, prec): - rv = mpf_norm(self._mpf_, prec) - # uncomment to see failures - #if rv != was._mpf_ and self._prec == prec: - # print was._mpf_, rv - return rv - - def _as_mpf_op(self, prec): - return self._mpf_, max(prec, self._prec) - - def _eval_is_positive(self): - return self.num > 0 - - def _eval_is_negative(self): - return self.num < 0 - - def __bool__(self): - # do not base answer on `man` alone: - # >>> mpf('0')._mpf_ - # (0, 0L, 0, 0) - # >>> mpf('nan')._mpf_ - # (0, 0L, -123, -1) - # >>> mpf('inf')._mpf_ - # (0, 0L, -456, -2) - # >>> mpf('-inf')._mpf_ - # (1, 0L, -789, -3) - sign, man, expt, bc = self._mpf_ - return not (man == 0 and bc == 0) - - def __neg__(self): - return Float._new(mlib.mpf_neg(self._mpf_), self._prec) - - @_sympifyit('other', NotImplemented) - def __add__(self, other): - if isinstance(other, Number): - rhs, prec = other._as_mpf_op(self._prec) - return Float._new(mlib.mpf_add(self._mpf_, rhs, prec, rnd), prec) - return Number.__add__(self, other) - - @_sympifyit('other', NotImplemented) - def __sub__(self, other): - if isinstance(other, Number): - rhs, prec = other._as_mpf_op(self._prec) - return Float._new(mlib.mpf_sub(self._mpf_, rhs, prec, rnd), prec) - return Number.__sub__(self, other) - - @_sympifyit('other', NotImplemented) - def __mul__(self, other): - if isinstance(other, Number): - rhs, prec = other._as_mpf_op(self._prec) - return Float._new(mlib.mpf_mul(self._mpf_, rhs, prec, rnd), prec) - return Number.__mul__(self, other) - - @_sympifyit('other', NotImplemented) - def __div__(self, other): - if isinstance(other, Number): - rhs, prec = other._as_mpf_op(self._prec) - return Float._new(mlib.mpf_div(self._mpf_, rhs, prec, rnd), prec) - return Number.__div__(self, other) - - __truediv__ = __div__ - - @_sympifyit('other', NotImplemented) - def __mod__(self, other): - if isinstance(other, Number): - rhs, prec = other._as_mpf_op(self._prec) - return Float._new(mlib.mpf_mod(self._mpf_, rhs, prec, rnd), prec) - return Number.__mod__(self, other) - - @_sympifyit('other', NotImplemented) - def __rmod__(self, other): - if isinstance(other, Number): - rhs, prec = other._as_mpf_op(self._prec) - return Float._new(mlib.mpf_mod(rhs, self._mpf_, prec, rnd), prec) - return Number.__rmod__(self, other) - - def _eval_power(self, expt): - """ - expt is symbolic object but not equal to 0, 1 - - (-p)**r -> exp(r*log(-p)) -> exp(r*(log(p) + I*Pi)) -> - -> p**r*(sin(Pi*r) + cos(Pi*r)*I) - """ - if isinstance(expt, Number): - if isinstance(expt, Integer): - prec = self._prec - return Float._new( - mlib.mpf_pow_int(self._mpf_, expt.p, prec, rnd), prec) - expt, prec = expt._as_mpf_op(self._prec) - self = self._mpf_ - try: - y = mpf_pow(self, expt, prec, rnd) - return Float._new(y, prec) - except mlib.ComplexResult: - re, im = mlib.mpc_pow( - (self, _mpf_zero), (expt, _mpf_zero), prec, rnd) - return Float._new(re, prec) + \ - Float._new(im, prec)*S.ImaginaryUnit - - def __abs__(self): - return Float._new(mlib.mpf_abs(self._mpf_), self._prec) - - def __int__(self): - return int(mlib.to_int(self._mpf_)) # uses round_fast = round_down - - def __eq__(self, other): - if isinstance(other, float): - # coerce to Float at same precision - o = Float(other) - try: - ompf = o._as_mpf_val(self._prec) - except ValueError: - return False - return bool(mlib.mpf_eq(self._mpf_, ompf)) - try: - other = _sympify(other) - except SympifyError: - return False # sympy != other --> not == - if isinstance(other, NumberSymbol): - if other.is_irrational: - return False - return other.__eq__(self) - if isinstance(other, Float): - return bool(mlib.mpf_eq(self._mpf_, other._mpf_)) - if isinstance(other, Number): - # numbers should compare at the same precision; - # all _as_mpf_val routines should be sure to abide - # by the request to change the prec if necessary; if - # they don't, the equality test will fail since it compares - # the mpf tuples - ompf = other._as_mpf_val(self._prec) - return bool(mlib.mpf_eq(self._mpf_, ompf)) - return False # Float != non-Number - - def __ne__(self, other): - return not self.__eq__(other) - - def __gt__(self, other): - try: - other = _sympify(other) - except SympifyError: - return False # sympy > other - if isinstance(other, NumberSymbol): - return other.__le__(self) - if other.is_comparable: - other = other.evalf() - if isinstance(other, Number): - return bool(mlib.mpf_gt(self._mpf_, other._as_mpf_val(self._prec))) - return Expr.__gt__(self, other) - - def __ge__(self, other): - try: - other = _sympify(other) - except SympifyError: - return False # sympy > other --> ! <= - if isinstance(other, NumberSymbol): - return other.__lt__(self) - if other.is_comparable: - other = other.evalf() - if isinstance(other, Number): - return bool(mlib.mpf_ge(self._mpf_, other._as_mpf_val(self._prec))) - return Expr.__ge__(self, other) - - def __lt__(self, other): - try: - other = _sympify(other) - except SympifyError: - return False # sympy > other - if isinstance(other, NumberSymbol): - return other.__ge__(self) - if other.is_real and other.is_number: - other = other.evalf() - if isinstance(other, Number): - return bool(mlib.mpf_lt(self._mpf_, other._as_mpf_val(self._prec))) - return Expr.__lt__(self, other) - - def __le__(self, other): - try: - other = _sympify(other) - except SympifyError: - return False # sympy > other --> ! <= - if isinstance(other, NumberSymbol): - return other.__gt__(self) - if other.is_real and other.is_number: - other = other.evalf() - if isinstance(other, Number): - return bool(mlib.mpf_le(self._mpf_, other._as_mpf_val(self._prec))) - return Expr.__le__(self, other) - - def __hash__(self): - return super(Float, self).__hash__() - - def epsilon_eq(self, other, epsilon="10e-16"): - return abs(self - other) < Float(epsilon) - - def _sage_(self): - import sage.all as sage - return sage.RealNumber(str(self)) - -# Add sympify converters
    -converter[float] = converter[decimal.Decimal] = Float - -# this is here to work nicely in Sage -RealNumber = Float - - -@deprecated(useinstead="Float", issue=1721, deprecated_since_version="0.7.0") -
    [docs]def Real(*args, **kwargs): # pragma: no cover - """Deprecated alias for the Float constructor.""" - return Float(*args, **kwargs) - -
    -
    [docs]class Rational(Number): - """Represents integers and rational numbers (p/q) of any size. - - Examples - ======== - - >>> from sympy import Rational, nsimplify, S, pi - >>> from sympy.abc import x, y - >>> Rational(3) - 3 - >>> Rational(1, 2) - 1/2 - - Rational is unprejudiced in accepting input. If a float is passed, the - underlying value of the binary representation will be returned: - - >>> Rational(.5) - 1/2 - >>> Rational(.2) - 3602879701896397/18014398509481984 - - If the simpler representation of the float is desired then consider - limiting the denominator to the desired value or convert the float to - a string (which is roughly equivalent to limiting the denominator to - 10**12): - - >>> Rational(str(.2)) - 1/5 - >>> Rational(.2).limit_denominator(10**12) - 1/5 - - An arbitrarily precise Rational is obtained when a string literal is - passed: - - >>> Rational("1.23") - 123/100 - >>> Rational('1e-2') - 1/100 - >>> Rational(".1") - 1/10 - >>> Rational('1e-2/3.2') - 1/320 - - The conversion of other types of strings can be handled by - the sympify() function, and conversion of floats to expressions - or simple fractions can be handled with nsimplify: - - >>> S('.[3]') # repeating digits in brackets - 1/3 - >>> S('3**2/10') # general expressions - 9/10 - >>> nsimplify(.3) # numbers that have a simple form - 3/10 - - But if the input does not reduce to a literal Rational, an error will - be raised: - - >>> Rational(pi) - Traceback (most recent call last): - ... - TypeError: invalid input: pi - - - Low-level - --------- - - Access numerator and denominator as .p and .q: - - >>> r = Rational(3, 4) - >>> r - 3/4 - >>> r.p - 3 - >>> r.q - 4 - - Note that p and q return integers (not sympy Integers) so some care - is needed when using them in expressions: - - >>> r.p//r.q - 0 - - See Also - ======== - sympify, sympy.simplify.simplify.nsimplify - """ - is_real = True - is_integer = False - is_rational = True - - __slots__ = ['p', 'q'] - - is_Rational = True - - @cacheit - def __new__(cls, p, q=None): - if q is None: - if isinstance(p, Rational): - return p - - if isinstance(p, str): - p = p.replace(' ', '') - try: - # we might have a Float - neg_pow, digits, expt = decimal.Decimal(p).as_tuple() - p = [1, -1][neg_pow]*int("".join(str(x) for x in digits)) - if expt > 0: - # TODO: this branch needs a test - return Rational(p*Pow(10, expt), 1) - return Rational(p, Pow(10, -expt)) - except decimal.InvalidOperation: - f = regex.match('^([-+]?[0-9]+)/([0-9]+)$', p) - if f: - n, d = f.groups() - return Rational(int(n), int(d)) - elif p.count('/') == 1: - p, q = p.split('/') - return Rational(Rational(p), Rational(q)) - else: - pass # error will raise below - else: - if isinstance(p, decimal.Decimal): - rv = _decimal_to_Rational_prec(p)[0] - try: - if isinstance(p, fractions.Fraction): - return Rational(p.numerator, p.denominator) - except NameError: - pass # error will raise below - - if isinstance(p, (float, Float)): - return Rational(*_as_integer_ratio(p)) - - if not isinstance(p, (int, Rational)): - raise TypeError('invalid input: %s' % p) - q = S.One - else: - p = Rational(p) - q = Rational(q) - - if isinstance(q, Rational): - p *= q.q - q = q.p - if isinstance(p, Rational): - q *= p.q - p = p.p - - # p and q are now integers - if q == 0: - if p == 0: - if _errdict["divide"]: - raise ValueError("Indeterminate 0/0") - else: - return S.NaN - if p < 0: - return S.NegativeInfinity - return S.Infinity - if q < 0: - q = -q - p = -p - n = igcd(abs(p), q) - if n > 1: - p //= n - q //= n - if q == 1: - return Integer(p) - if p == 1 and q == 2: - return S.Half - obj = Expr.__new__(cls) - obj.p = p - obj.q = q - #obj._args = (p, q) - return obj - -
    [docs] def limit_denominator(self, max_denominator=1000000): - """Closest Rational to self with denominator at most max_denominator. - - >>> from sympy import Rational - >>> Rational('3.141592653589793').limit_denominator(10) - 22/7 - >>> Rational('3.141592653589793').limit_denominator(100) - 311/99 - - """ - # Algorithm notes: For any real number x, define a *best upper - # approximation* to x to be a rational number p/q such that: - # - # (1) p/q >= x, and - # (2) if p/q > r/s >= x then s > q, for any rational r/s. - # - # Define *best lower approximation* similarly. Then it can be - # proved that a rational number is a best upper or lower - # approximation to x if, and only if, it is a convergent or - # semiconvergent of the (unique shortest) continued fraction - # associated to x. - # - # To find a best rational approximation with denominator <= M, - # we find the best upper and lower approximations with - # denominator <= M and take whichever of these is closer to x. - # In the event of a tie, the bound with smaller denominator is - # chosen. If both denominators are equal (which can happen - # only when max_denominator == 1 and self is midway between - # two integers) the lower bound---i.e., the floor of self, is - # taken. - - if max_denominator < 1: - raise ValueError("max_denominator should be at least 1") - if self.q <= max_denominator: - return self - - p0, q0, p1, q1 = 0, 1, 1, 0 - n, d = self.p, self.q - while True: - a = n//d - q2 = q0 + a*q1 - if q2 > max_denominator: - break - p0, q0, p1, q1 = p1, q1, p0 + a*p1, q2 - n, d = d, n - a*d - - k = (max_denominator - q0)//q1 - bound1 = Rational(p0 + k*p1, q0 + k*q1) - bound2 = Rational(p1, q1) - if abs(bound2 - self) <= abs(bound1 - self): - return bound2 - else: - return bound1 -
    - def __getnewargs__(self): - return (self.p, self.q) - - def _hashable_content(self): - return (self.p, self.q) - - def _eval_is_positive(self): - return self.p > 0 - - def _eval_is_zero(self): - return self.p == 0 - - def __neg__(self): - return Rational(-self.p, self.q) - - @_sympifyit('other', NotImplemented) - def __add__(self, other): - if isinstance(other, Rational): - return Rational(self.p*other.q + self.q*other.p, self.q*other.q) - elif isinstance(other, Float): - return other + self - else: - return Number.__add__(self, other) - - @_sympifyit('other', NotImplemented) - def __sub__(self, other): - if isinstance(other, Rational): - return Rational(self.p*other.q - self.q*other.p, self.q*other.q) - elif isinstance(other, Float): - return -other + self - else: - return Number.__sub__(self, other) - - @_sympifyit('other', NotImplemented) - def __mul__(self, other): - if isinstance(other, Rational): - return Rational(self.p*other.p, self.q*other.q) - elif isinstance(other, Float): - return other*self - else: - return Number.__mul__(self, other) - - @_sympifyit('other', NotImplemented) - def __div__(self, other): - if isinstance(other, Rational): - return Rational(self.p*other.q, self.q*other.p) - elif isinstance(other, Float): - return self*(1/other) - else: - return Number.__div__(self, other) - - __truediv__ = __div__ - - @_sympifyit('other', NotImplemented) - def __mod__(self, other): - if isinstance(other, Rational): - n = (self.p*other.q) // (other.p*self.q) - return Rational(self.p*other.q - n*other.p*self.q, self.q*other.q) - if isinstance(other, Float): - evalf = self.evalf() - # In case self.evalf() does not return Float. - if isinstance(evalf, Float): - return evalf % other - return Number.__mod__(self, other) - - @_sympifyit('other', NotImplemented) - def __rmod__(self, other): - if isinstance(other, Rational): - return Rational.__mod__(other, self) - if isinstance(other, Float): - return other % self.evalf() - return Number.__rmod__(self, other) - - def _eval_power(self, expt): - if isinstance(expt, Number): - if isinstance(expt, Float): - return self._eval_evalf(expt._prec)**expt - if expt.is_negative: - # (3/4)**-2 -> (4/3)**2 - ne = -expt - if (ne is S.One): - return Rational(self.q, self.p) - if self.is_negative: - if expt.q != 1: - return -(S.NegativeOne)**((expt.p % expt.q) / - S(expt.q))*Rational(self.q, -self.p)**ne - else: - return S.NegativeOne**ne*Rational(self.q, -self.p)**ne - else: - return Rational(self.q, self.p)**ne - if expt is S.Infinity: # -oo already caught by test for negative - if self.p > self.q: - # (3/2)**oo -> oo - return S.Infinity - if self.p < -self.q: - # (-3/2)**oo -> oo + I*oo - return S.Infinity + S.Infinity*S.ImaginaryUnit - return S.Zero - if isinstance(expt, Integer): - # (4/3)**2 -> 4**2 / 3**2 - return Rational(self.p**expt.p, self.q**expt.p) - if isinstance(expt, Rational): - if self.p != 1: - # (4/3)**(5/6) -> 4**(5/6)*3**(-5/6) - return Integer(self.p)**expt*Integer(self.q)**(-expt) - # as the above caught negative self.p, now self is positive - return Integer(self.q)**Rational( - expt.p*(expt.q - 1), expt.q) / \ - Integer(self.q)**Integer(expt.p) - - if self.is_negative and expt.is_even: - return (-self)**expt - - return - - def _as_mpf_val(self, prec): - return mlib.from_rational(self.p, self.q, prec, rnd) - - def _mpmath_(self, prec, rnd): - return mpmath.make_mpf(mlib.from_rational(self.p, self.q, prec, rnd)) - - def __abs__(self): - return Rational(abs(self.p), self.q) - - def __int__(self): - p, q = self.p, self.q - if p < 0: - return -(-p//q) - return p//q - - def __eq__(self, other): - try: - other = _sympify(other) - except SympifyError: - return False # sympy != other --> not == - if isinstance(other, NumberSymbol): - if other.is_irrational: - return False - return other.__eq__(self) - if isinstance(other, Number): - if isinstance(other, Float): - return mlib.mpf_eq(self._as_mpf_val(other._prec), other._mpf_) - elif isinstance(other, Rational): - return self.p == other.p and self.q == other.q - return False - - def __ne__(self, other): - return not self.__eq__(other) - - def __gt__(self, other): - try: - other = _sympify(other) - except SympifyError: - return False # sympy > other --> not < - if isinstance(other, NumberSymbol): - return other.__le__(self) - if other.is_real and other.is_number and not isinstance(other, Rational): - other = other.evalf() - if isinstance(other, Number): - if isinstance(other, Float): - return bool(mlib.mpf_gt( - self._as_mpf_val(other._prec), other._mpf_)) - return bool(self.p*other.q > self.q*other.p) - return Expr.__gt__(self, other) - - def __ge__(self, other): - try: - other = _sympify(other) - except SympifyError: - return False # sympy > other --> not <= - if isinstance(other, NumberSymbol): - return other.__lt__(self) - if other.is_real and other.is_number and not isinstance(other, Rational): - other = other.evalf() - if isinstance(other, Number): - if isinstance(other, Float): - return bool(mlib.mpf_ge( - self._as_mpf_val(other._prec), other._mpf_)) - return bool(self.p*other.q >= self.q*other.p) - return Expr.__ge__(self, other) - - def __lt__(self, other): - try: - other = _sympify(other) - except SympifyError: - return False # sympy > other --> not < - if isinstance(other, NumberSymbol): - return other.__ge__(self) - if other.is_real and other.is_number and not isinstance(other, Rational): - other = other.evalf() - if isinstance(other, Number): - if isinstance(other, Float): - return bool(mlib.mpf_lt( - self._as_mpf_val(other._prec), other._mpf_)) - return bool(self.p*other.q < self.q*other.p) - return Expr.__lt__(self, other) - - def __le__(self, other): - try: - other = _sympify(other) - except SympifyError: - return False # sympy > other --> not <= - if isinstance(other, NumberSymbol): - return other.__gt__(self) - if other.is_real and other.is_number and not isinstance(other, Rational): - other = other.evalf() - if isinstance(other, Number): - if isinstance(other, Float): - return bool(mlib.mpf_le( - self._as_mpf_val(other._prec), other._mpf_)) - return bool(self.p*other.q <= self.q*other.p) - return Expr.__le__(self, other) - - def __hash__(self): - return super(Rational, self).__hash__() - -
    [docs] def factors(self, limit=None, use_trial=True, use_rho=False, - use_pm1=False, verbose=False, visual=False): - """A wrapper to factorint which return factors of self that are - smaller than limit (or cheap to compute). Special methods of - factoring are disabled by default so that only trial division is used. - """ - from sympy.ntheory import factorint - - f = factorint(self.p, limit=limit, use_trial=use_trial, - use_rho=use_rho, use_pm1=use_pm1, - verbose=verbose).copy() - f = defaultdict(int, f) - for p, e in list(factorint(self.q, limit=limit, - use_trial=use_trial, - use_rho=use_rho, - use_pm1=use_pm1, - verbose=verbose).items()): - f[p] += -e - - if len(f) > 1 and 1 in f: - del f[1] - if not f: - f = {1: 1} - if not visual: - return dict(f) - else: - if -1 in f: - f.pop(-1) - args = [S.NegativeOne] - else: - args = [] - args.extend([Pow(*i, **{'evaluate':False}) - for i in sorted(f.items())]) - return Mul(*args, **{'evaluate': False}) -
    - def as_numer_denom(self): - return Integer(self.p), Integer(self.q) - - def _sage_(self): - import sage.all as sage - return sage.Integer(self.p)/sage.Integer(self.q) - -
    [docs] def as_content_primitive(self, radical=False): - """Return the tuple (R, self/R) where R is the positive Rational - extracted from self. - - Examples - ======== - - >>> from sympy import S - >>> (S(-3)/2).as_content_primitive() - (3/2, -1) - - See docstring of Expr.as_content_primitive for more examples. - """ - - if self: - if self.is_positive: - return self, S.One - return -self, S.NegativeOne - return S.One, self - -# int -> Integer
    -_intcache = {} - - -# TODO move this tracing facility to sympy/core/trace.py ? -def _intcache_printinfo(): - ints = sorted(_intcache.keys()) - nhit = _intcache_hits - nmiss = _intcache_misses - - if nhit == 0 and nmiss == 0: - print() - print('Integer cache statistic was not collected') - return - - miss_ratio = float(nmiss) / (nhit + nmiss) - - print() - print('Integer cache statistic') - print('-----------------------') - print() - print('#items: %i' % len(ints)) - print() - print(' #hit #miss #total') - print() - print('%5i %5i (%7.5f %%) %5i' % ( - nhit, nmiss, miss_ratio*100, nhit + nmiss)) - print() - print(ints) - -_intcache_hits = 0 -_intcache_misses = 0 - - -def int_trace(f): - import os - if os.getenv('SYMPY_TRACE_INT', 'no').lower() != 'yes': - return f - - def Integer_tracer(cls, i): - global _intcache_hits, _intcache_misses - - try: - _intcache_hits += 1 - return _intcache[i] - except KeyError: - _intcache_hits -= 1 - _intcache_misses += 1 - - return f(cls, i) - - # also we want to hook our _intcache_printinfo into sys.atexit - import atexit - atexit.register(_intcache_printinfo) - - return Integer_tracer - - -
    [docs]class Integer(Rational): - - q = 1 - is_integer = True - - is_Integer = True - - __slots__ = ['p'] - - def _as_mpf_val(self, prec): - return mlib.from_int(self.p, prec) - - def _mpmath_(self, prec, rnd): - return mpmath.make_mpf(self._as_mpf_val(prec)) - - # TODO caching with decorator, but not to degrade performance - @int_trace - def __new__(cls, i): - if isinstance(i, str): - i = i.replace(' ', '') - # whereas we cannot, in general, make a Rational from an - # arbitrary expression, we can make an Integer unambiguously - # (except when a non-integer expression happens to round to - # an integer). So we proceed by taking int() of the input and - # let the int routines determine whether the expression can - # be made into an int or whether an error should be raised. - try: - ival = int(i) - except TypeError: - raise TypeError( - 'Integer can only work with integer expressions.') - try: - return _intcache[ival] - except KeyError: - # We only work with well-behaved integer types. This converts, for - # example, numpy.int32 instances. - obj = Expr.__new__(cls) - obj.p = ival - - _intcache[ival] = obj - return obj - - def __getnewargs__(self): - return (self.p,) - - # Arithmetic operations are here for efficiency - def __int__(self): - return self.p - - def __neg__(self): - return Integer(-self.p) - - def __abs__(self): - if self.p >= 0: - return self - else: - return Integer(-self.p) - - def __divmod__(self, other): - from .containers import Tuple - if isinstance(other, Integer): - return Tuple(*(divmod(self.p, other.p))) - else: - return Number.__divmod__(self, other) - - def __rdivmod__(self, other): - from .containers import Tuple - if isinstance(other, int): - return Tuple(*(divmod(other, self.p))) - else: - try: - other = Number(other) - except TypeError: - msg = "unsupported operand type(s) for divmod(): '%s' and '%s'" - oname = type(other).__name__ - sname = type(self).__name__ - raise TypeError(msg % (oname, sname)) - return Number.__divmod__(other, self) - - # TODO make it decorator + bytecodehacks? - def __add__(self, other): - if isinstance(other, int): - return Integer(self.p + other) - elif isinstance(other, Integer): - return Integer(self.p + other.p) - return Rational.__add__(self, other) - - def __radd__(self, other): - if isinstance(other, int): - return Integer(other + self.p) - return Rational.__add__(self, other) - - def __sub__(self, other): - if isinstance(other, int): - return Integer(self.p - other) - elif isinstance(other, Integer): - return Integer(self.p - other.p) - return Rational.__sub__(self, other) - - def __rsub__(self, other): - if isinstance(other, int): - return Integer(other - self.p) - return Rational.__rsub__(self, other) - - def __mul__(self, other): - if isinstance(other, int): - return Integer(self.p*other) - elif isinstance(other, Integer): - return Integer(self.p*other.p) - return Rational.__mul__(self, other) - - def __rmul__(self, other): - if isinstance(other, int): - return Integer(other*self.p) - return Rational.__mul__(self, other) - - def __mod__(self, other): - if isinstance(other, int): - return Integer(self.p % other) - elif isinstance(other, Integer): - return Integer(self.p % other.p) - return Rational.__mod__(self, other) - - def __rmod__(self, other): - if isinstance(other, int): - return Integer(other % self.p) - elif isinstance(other, Integer): - return Integer(other.p % self.p) - return Rational.__rmod__(self, other) - - def __eq__(self, other): - if isinstance(other, int): - return (self.p == other) - elif isinstance(other, Integer): - return (self.p == other.p) - return Rational.__eq__(self, other) - - def __ne__(self, other): - return not self.__eq__(other) - - def __gt__(self, other): - if isinstance(other, int): - return (self.p > other) - elif isinstance(other, Integer): - return (self.p > other.p) - return Rational.__gt__(self, other) - - def __lt__(self, other): - if isinstance(other, int): - return (self.p < other) - elif isinstance(other, Integer): - return (self.p < other.p) - return Rational.__lt__(self, other) - - def __ge__(self, other): - if isinstance(other, int): - return (self.p >= other) - elif isinstance(other, Integer): - return (self.p >= other.p) - return Rational.__ge__(self, other) - - def __le__(self, other): - if isinstance(other, int): - return (self.p <= other) - elif isinstance(other, Integer): - return (self.p <= other.p) - return Rational.__le__(self, other) - - def __hash__(self): - return super(Integer, self).__hash__() - - def __index__(self): - return self.p - - ######################################## - - def _eval_is_odd(self): - return bool(self.p % 2) - - def _eval_power(self, expt): - """ - Tries to do some simplifications on self**expt - - Returns None if no further simplifications can be done - - When exponent is a fraction (so we have for example a square root), - we try to find a simpler representation by factoring the argument - up to factors of 2**15, e.g. - - - sqrt(4) becomes 2 - - sqrt(-4) becomes 2*I - - (2**(3+7)*3**(6+7))**Rational(1,7) becomes 6*18**(3/7) - - Further simplification would require a special call to factorint on - the argument which is not done here for sake of speed. - - """ - from sympy import perfect_power - - if expt is S.Infinity: - if self.p > S.One: - return S.Infinity - # cases -1, 0, 1 are done in their respective classes - return S.Infinity + S.ImaginaryUnit*S.Infinity - if expt is S.NegativeInfinity: - return Rational(1, self)**S.Infinity - if not isinstance(expt, Number): - # simplify when expt is even - # (-2)**k --> 2**k - if self.is_negative and expt.is_even: - return (-self)**expt - if not isinstance(expt, Rational): - return - if expt is S.Half and self.is_negative: - # we extract I for this special case since everyone is doing so - return S.ImaginaryUnit*Pow(-self, expt) - if expt.is_negative: - # invert base and change sign on exponent - ne = -expt - if self.is_negative: - if expt.q != 1: - return -(S.NegativeOne)**((expt.p % expt.q) / - S(expt.q))*Rational(1, -self)**ne - else: - return (S.NegativeOne)**ne*Rational(1, -self)**ne - else: - return Rational(1, self.p)**ne - # see if base is a perfect root, sqrt(4) --> 2 - x, xexact = integer_nthroot(abs(self.p), expt.q) - if xexact: - # if it's a perfect root we've finished - result = Integer(x**abs(expt.p)) - if self.is_negative: - result *= S.NegativeOne**expt - return result - - # The following is an algorithm where we collect perfect roots - # from the factors of base. - - # if it's not an nth root, it still might be a perfect power - b_pos = int(abs(self.p)) - p = perfect_power(b_pos) - if p is not False: - dict = {p[0]: p[1]} - else: - dict = Integer(self).factors(limit=2**15) - - # now process the dict of factors - if self.is_negative: - dict[-1] = 1 - out_int = 1 # integer part - out_rad = 1 # extracted radicals - sqr_int = 1 - sqr_gcd = 0 - sqr_dict = {} - for prime, exponent in list(dict.items()): - exponent *= expt.p - # remove multiples of expt.q: (2**12)**(1/10) -> 2*(2**2)**(1/10) - div_e, div_m = divmod(exponent, expt.q) - if div_e > 0: - out_int *= prime**div_e - if div_m > 0: - # see if the reduced exponent shares a gcd with e.q - # (2**2)**(1/10) -> 2**(1/5) - g = igcd(div_m, expt.q) - if g != 1: - out_rad *= Pow(prime, Rational(div_m//g, expt.q//g)) - else: - sqr_dict[prime] = div_m - # identify gcd of remaining powers - for p, ex in sqr_dict.items(): - if sqr_gcd == 0: - sqr_gcd = ex - else: - sqr_gcd = igcd(sqr_gcd, ex) - if sqr_gcd == 1: - break - for k, v in sqr_dict.items(): - sqr_int *= k**(v//sqr_gcd) - if sqr_int == self and out_int == 1 and out_rad == 1: - result = None - else: - result = out_int*out_rad*Pow(sqr_int, Rational(sqr_gcd, expt.q)) - return result - - def _eval_is_prime(self): - from sympy.ntheory import isprime - - return isprime(self) - - def as_numer_denom(self): - return self, S.One - - def __floordiv__(self, other): - return Integer(self.p // Integer(other).p) - - def __rfloordiv__(self, other): - return Integer(Integer(other).p // self.p) - -# Add sympify converters
    -converter[int] = converter[int] = Integer - - -class RationalConstant(Rational): - """ - Abstract base class for rationals with specific behaviors - - Derived classes must define class attributes p and q and should probably all - be singletons. - """ - __slots__ = [] - - def __new__(cls): - return AtomicExpr.__new__(cls) - - -class IntegerConstant(Integer): - __slots__ = [] - - def __new__(cls): - return AtomicExpr.__new__(cls) - - -class Zero(IntegerConstant, metaclass=Singleton): - p = 0 - q = 1 - is_positive = False - is_negative = False - is_finite = False - is_zero = True - is_composite = False - - __slots__ = [] - - @staticmethod - def __abs__(): - return S.Zero - - @staticmethod - def __neg__(): - return S.Zero - - @_sympifyit('other', NotImplemented) - @call_highest_priority('__rmul__') - def __mul__(self, other): - if other is S.NaN or \ - other is S.NegativeInfinity or \ - other is S.Infinity or \ - other is S.ComplexInfinity: - return S.NaN - return S.Zero - - def _eval_power(self, expt): - if expt.is_positive: - return self - if expt.is_negative: - return S.Infinity - if expt.is_real is False: - return S.NaN - # infinities are already handled with pos and neg - # tests above; now throw away leading numbers on Mul - # exponent - coeff, terms = expt.as_coeff_Mul() - if coeff.is_negative: - return S.Infinity**terms - if coeff is not S.One: # there is a Number to discard - return self**terms - - def _eval_order(self, *symbols): - # Order(0,x) -> 0 - return self - - def __bool__(self): - return False - - -class One(IntegerConstant, metaclass=Singleton): - p = 1 - q = 1 - - __slots__ = [] - - @staticmethod - def __abs__(): - return S.One - - @staticmethod - def __neg__(): - return S.NegativeOne - - def _eval_power(self, expt): - return self - - def _eval_order(self, *symbols): - return - - @staticmethod - def factors(limit=None, use_trial=True, use_rho=False, use_pm1=False, - verbose=False, visual=False): - if visual: - return S.One - return {1: 1} - - -class NegativeOne(IntegerConstant, metaclass=Singleton): - p = -1 - q = 1 - - __slots__ = [] - - @staticmethod - def __abs__(): - return S.One - - @staticmethod - def __neg__(): - return S.One - - def _eval_power(self, expt): - if expt.is_odd: - return S.NegativeOne - if expt.is_even: - return S.One - if isinstance(expt, Number): - if isinstance(expt, Float): - return Float(-1.0)**expt - if expt is S.NaN: - return S.NaN - if expt is S.Infinity or expt is S.NegativeInfinity: - return S.NaN - if expt is S.Half: - return S.ImaginaryUnit - if isinstance(expt, Rational): - if expt.q == 2: - return S.ImaginaryUnit**Integer(expt.p) - i, r = divmod(expt.p, expt.q) - if i: - return self**i*self**Rational(r, expt.q) - return - - -class Half(RationalConstant, metaclass=Singleton): - p = 1 - q = 2 - - __slots__ = [] - - @staticmethod - def __abs__(): - return S.Half - - -class Infinity(Number, metaclass=Singleton): - is_commutative = True - is_positive = True - is_bounded = False - is_finite = False - is_infinitesimal = False - is_integer = None - is_rational = None - is_odd = None - - __slots__ = [] - - def __new__(cls): - return AtomicExpr.__new__(cls) - - @_sympifyit('other', NotImplemented) - def __add__(self, other): - if isinstance(other, Number): - if other is S.NegativeInfinity or other is S.NaN: - return S.NaN - elif other.is_Float: - if other == Float('-inf'): - return S.NaN - else: - return Float('inf') - else: - return S.Infinity - return NotImplemented - __radd__ = __add__ - - @_sympifyit('other', NotImplemented) - def __sub__(self, other): - if isinstance(other, Number): - if other is S.Infinity or other is S.NaN: - return S.NaN - elif other.is_Float: - if other == Float('inf'): - return S.NaN - else: - return Float('inf') - else: - return S.Infinity - return NotImplemented - - @_sympifyit('other', NotImplemented) - def __mul__(self, other): - if isinstance(other, Number): - if other is S.Zero or other is S.NaN: - return S.NaN - elif other.is_Float: - if other == 0: - return S.NaN - if other > 0: - return Float('inf') - else: - return Float('-inf') - else: - if other > 0: - return S.Infinity - else: - return S.NegativeInfinity - return NotImplemented - __rmul__ = __mul__ - - @_sympifyit('other', NotImplemented) - def __div__(self, other): - if isinstance(other, Number): - if other is S.Infinity or \ - other is S.NegativeInfinity or \ - other is S.NaN: - return S.NaN - elif other.is_Float: - if other == Float('-inf') or \ - other == Float('inf'): - return S.NaN - elif other.is_nonnegative: - return Float('inf') - else: - return Float('-inf') - else: - if other >= 0: - return S.Infinity - else: - return S.NegativeInfinity - return NotImplemented - - __truediv__ = __div__ - - def __abs__(self): - return S.Infinity - - def __neg__(self): - return S.NegativeInfinity - - def _eval_power(self, expt): - """ - ``expt`` is symbolic object but not equal to 0 or 1. - - ================ ======= ============================== - Expression Result Notes - ================ ======= ============================== - ``oo ** nan`` ``nan`` - ``oo ** -p`` ``0`` ``p`` is number, ``oo`` - ================ ======= ============================== - - """ - if expt.is_positive: - return S.Infinity - if expt.is_negative: - return S.Zero - if expt is S.NaN: - return S.NaN - - if expt.is_number: - return self**expt.evalf() - - def _as_mpf_val(self, prec): - return mlib.finf - - def _sage_(self): - import sage.all as sage - return sage.oo - - def __hash__(self): - return super(Infinity, self).__hash__() - - def __eq__(self, other): - return other is S.Infinity - - def __ne__(self, other): - return other is not S.Infinity - - def __lt__(self, other): - return False - - def __le__(self, other): - return other is S.Infinity - - def __gt__(self, other): - return other is not S.Infinity - - def __ge__(self, other): - return True - - def __mod__(self, other): - return S.NaN - - __rmod__ = __mod__ - -oo = S.Infinity - - -class NegativeInfinity(Number, metaclass=Singleton): - is_commutative = True - is_real = True - is_positive = False - is_bounded = False - is_finite = False - is_infinitesimal = False - is_integer = None - is_rational = None - - __slots__ = [] - - def __new__(cls): - return AtomicExpr.__new__(cls) - - @_sympifyit('other', NotImplemented) - def __add__(self, other): - if isinstance(other, Number): - if other is S.Infinity or other is S.NaN: - return S.NaN - elif other.is_Float: - if other == Float('inf'): - return Float('nan') - else: - return Float('-inf') - else: - return S.NegativeInfinity - return NotImplemented - __radd__ = __add__ - - @_sympifyit('other', NotImplemented) - def __sub__(self, other): - if isinstance(other, Number): - if other is S.NegativeInfinity or other is S.NaN: - return S.NaN - elif other.is_Float: - if other == Float('-inf'): - return Float('nan') - else: - return Float('-inf') - else: - return S.NegativeInfinity - return NotImplemented - - @_sympifyit('other', NotImplemented) - def __mul__(self, other): - if isinstance(other, Number): - if other is S.Zero or other is S.NaN: - return S.NaN - elif other.is_Float: - if other is S.NaN or other.is_zero: - return S.NaN - elif other.is_positive: - return Float('-inf') - else: - return Float('inf') - else: - if other.is_positive: - return S.NegativeInfinity - else: - return S.Infinity - return NotImplemented - __rmul__ = __mul__ - - @_sympifyit('other', NotImplemented) - def __div__(self, other): - if isinstance(other, Number): - if other is S.Infinity or \ - other is S.NegativeInfinity or \ - other is S.NaN: - return S.NaN - elif other.is_Float: - if other == Float('-inf') or \ - other == Float('inf') or \ - other is S.NaN: - return S.NaN - elif other.is_nonnegative: - return Float('-inf') - else: - return Float('inf') - else: - if other >= 0: - return S.NegativeInfinity - else: - return S.Infinity - return NotImplemented - - __truediv__ = __div__ - - def __abs__(self): - return S.Infinity - - def __neg__(self): - return S.Infinity - - def _eval_power(self, expt): - """ - ``expt`` is symbolic object but not equal to 0 or 1. - - ================ ======= ============================== - Expression Result Notes - ================ ======= ============================== - ``(-oo) ** nan`` ``nan`` - ``(-oo) ** oo`` ``nan`` - ``(-oo) ** -oo`` ``nan`` - ``(-oo) ** e`` ``oo`` ``e`` is positive even integer - ``(-oo) ** o`` ``-oo`` ``o`` is positive odd integer - ================ ======= ============================== - - """ - if isinstance(expt, Number): - if expt is S.NaN or \ - expt is S.Infinity or \ - expt is S.NegativeInfinity: - return S.NaN - - if isinstance(expt, Integer) and expt.is_positive: - if expt.is_odd: - return S.NegativeInfinity - else: - return S.Infinity - - return S.NegativeOne**expt*S.Infinity**expt - - def _as_mpf_val(self, prec): - return mlib.fninf - - def _sage_(self): - import sage.all as sage - return -(sage.oo) - - def __hash__(self): - return super(NegativeInfinity, self).__hash__() - - def __eq__(self, other): - return other is S.NegativeInfinity - - def __ne__(self, other): - return other is not S.NegativeInfinity - - def __lt__(self, other): - return other is not S.NegativeInfinity - - def __le__(self, other): - return True - - def __gt__(self, other): - return False - - def __ge__(self, other): - return other is S.NegativeInfinity - - -class NaN(Number, metaclass=Singleton): - """ - Not a Number. - - This represents the corresponding data type to floating point nan, which - is defined in the IEEE 754 floating point standard, and corresponds to the - Python ``float('nan')``. - - NaN serves as a place holder for numeric values that are indeterminate, - but not infinite. Most operations on nan, produce another nan. Most - indeterminate forms, such as ``0/0`` or ``oo - oo` produce nan. Three - exceptions are ``0**0``, ``1**oo``, and ``oo**0``, which all produce ``1`` - (this is consistent with Python's float). - - NaN is a singleton, and can be accessed by ``S.NaN``, or can be imported - as ``nan``. - - Examples - ======== - - >>> from sympy import nan, S, oo - >>> nan is S.NaN - True - >>> oo - oo - nan - >>> nan + 1 - nan - - References - ========== - - - http://en.wikipedia.org/wiki/NaN - - """ - - is_commutative = True - is_real = None - is_rational = None - is_integer = None - is_comparable = False - is_finite = None - is_bounded = None - is_zero = None - is_prime = None - is_positive = None - is_negative = None - - __slots__ = [] - - def __new__(cls): - return AtomicExpr.__new__(cls) - - @_sympifyit('other', NotImplemented) - def __add__(self, other): - return self - - @_sympifyit('other', NotImplemented) - def __sub__(self, other): - return self - - @_sympifyit('other', NotImplemented) - def __mul__(self, other): - return self - - @_sympifyit('other', NotImplemented) - def __div__(self, other): - return self - - __truediv__ = __div__ - - def _as_mpf_val(self, prec): - return _mpf_nan - - def _sage_(self): - import sage.all as sage - return sage.NaN - - def __hash__(self): - return super(NaN, self).__hash__() - - def __eq__(self, other): - return other is S.NaN - - def __ne__(self, other): - return other is not S.NaN - - def __gt__(self, other): - return False - - def __ge__(self, other): - return False - - def __lt__(self, other): - return False - - def __le__(self, other): - return False - -nan = S.NaN - - -class ComplexInfinity(AtomicExpr, metaclass=Singleton): - is_commutative = True - is_bounded = False - is_real = None - is_number = False - - __slots__ = [] - - def __new__(cls): - return AtomicExpr.__new__(cls) - - @staticmethod - def __abs__(): - return S.Infinity - - @staticmethod - def __neg__(): - return S.ComplexInfinity - - def _eval_power(self, expt): - if expt is S.ComplexInfinity: - return S.NaN - - if isinstance(expt, Number): - if expt is S.Zero: - return S.NaN - else: - if expt.is_positive: - return S.ComplexInfinity - else: - return S.Zero - -zoo = S.ComplexInfinity - - -
    [docs]class NumberSymbol(AtomicExpr, metaclass=Singleton): - is_commutative = True - is_bounded = True - is_finite = True - is_number = True - - __slots__ = [] - - is_NumberSymbol = True - - def __new__(cls): - return AtomicExpr.__new__(cls) - -
    [docs] def approximation(self, number_cls): - """ Return an interval with number_cls endpoints - that contains the value of NumberSymbol. - If not implemented, then return None. - """ -
    - def _eval_evalf(self, prec): - return Float._new(self._as_mpf_val(prec), prec) - - def __eq__(self, other): - try: - other = _sympify(other) - except SympifyError: - return False # sympy != other --> not == - if self is other: - return True - if isinstance(other, Number) and self.is_irrational: - return False - - return False # NumberSymbol != non-(Number|self) - - def __ne__(self, other): - return not self.__eq__(other) - - def __lt__(self, other): - try: - other = _sympify(other) - except SympifyError: - return False # sympy > other --> not < - if self is other: - return False - if isinstance(other, Number): - approx = self.approximation_interval(other.__class__) - if approx is not None: - l, u = approx - if other < l: - return False - if other > u: - return True - return self.evalf() < other - if other.is_real and other.is_number: - other = other.evalf() - return self.evalf() < other - return Expr.__lt__(self, other) - - def __le__(self, other): - try: - other = _sympify(other) - except SympifyError: - return False # sympy > other --> not <= - if self is other: - return True - if other.is_real and other.is_number: - other = other.evalf() - if isinstance(other, Number): - return self.evalf() <= other - return Expr.__le__(self, other) - - def __gt__(self, other): - return (-self) < (-other) - - def __ge__(self, other): - return (-self) <= (-other) - - def __int__(self): - # subclass with appropriate return value - raise NotImplementedError - - def __hash__(self): - return super(NumberSymbol, self).__hash__() - -
    -class Exp1(NumberSymbol, metaclass=Singleton): - is_real = True - is_positive = True - is_negative = False # XXX Forces is_negative/is_nonnegative - is_irrational = True - - __slots__ = [] - - @staticmethod - def __abs__(): - return S.Exp1 - - def __int__(self): - return 2 - - def _as_mpf_val(self, prec): - return mpf_e(prec) - - def approximation_interval(self, number_cls): - if issubclass(number_cls, Integer): - return (Integer(2), Integer(3)) - elif issubclass(number_cls, Rational): - pass - - def _eval_power(self, expt): - return C.exp(expt) - - def _sage_(self): - import sage.all as sage - return sage.e -E = S.Exp1 - - -class Pi(NumberSymbol, metaclass=Singleton): - is_real = True - is_positive = True - is_negative = False - is_irrational = True - - __slots__ = [] - - @staticmethod - def __abs__(): - return S.Pi - - def __int__(self): - return 3 - - def _as_mpf_val(self, prec): - return mpf_pi(prec) - - def approximation_interval(self, number_cls): - if issubclass(number_cls, Integer): - return (Integer(3), Integer(4)) - elif issubclass(number_cls, Rational): - return (Rational(223, 71), Rational(22, 7)) - - def _sage_(self): - import sage.all as sage - return sage.pi -pi = S.Pi - - -class GoldenRatio(NumberSymbol, metaclass=Singleton): - is_real = True - is_positive = True - is_negative = False - is_irrational = True - - __slots__ = [] - - def __int__(self): - return 1 - - def _as_mpf_val(self, prec): - # XXX track down why this has to be increased - rv = mlib.from_man_exp(phi_fixed(prec + 10), -prec - 10) - return mpf_norm(rv, prec) - - def _eval_expand_func(self, **hints): - from sympy import sqrt - return S.Half + S.Half*sqrt(5) - - def approximation_interval(self, number_cls): - if issubclass(number_cls, Integer): - return (S.One, Rational(2)) - elif issubclass(number_cls, Rational): - pass - - def _sage_(self): - import sage.all as sage - return sage.golden_ratio - - -class EulerGamma(NumberSymbol, metaclass=Singleton): - is_real = True - is_positive = True - is_negative = False - is_irrational = None - - __slots__ = [] - - def __int__(self): - return 0 - - def _as_mpf_val(self, prec): - # XXX track down why this has to be increased - v = mlib.libhyper.euler_fixed(prec + 10) - rv = mlib.from_man_exp(v, -prec - 10) - return mpf_norm(rv, prec) - - def approximation_interval(self, number_cls): - if issubclass(number_cls, Integer): - return (S.Zero, S.One) - elif issubclass(number_cls, Rational): - return (S.Half, Rational(3, 5)) - - def _sage_(self): - import sage.all as sage - return sage.euler_gamma - - -class Catalan(NumberSymbol, metaclass=Singleton): - is_real = True - is_positive = True - is_negative = False - is_irrational = None - - __slots__ = [] - - def __int__(self): - return 0 - - def _as_mpf_val(self, prec): - # XXX track down why this has to be increased - v = mlib.catalan_fixed(prec + 10) - rv = mlib.from_man_exp(v, -prec - 10) - return mpf_norm(rv, prec) - - def approximation_interval(self, number_cls): - if issubclass(number_cls, Integer): - return (S.Zero, S.One) - elif issubclass(number_cls, Rational): - return (Rational(9, 10), S.One) - - def _sage_(self): - import sage.all as sage - return sage.catalan - - -class ImaginaryUnit(AtomicExpr, metaclass=Singleton): - is_commutative = True - is_imaginary = True - is_bounded = True - is_finite = True - is_number = True - - __slots__ = [] - - @staticmethod - def __abs__(): - return S.One - - def _eval_evalf(self, prec): - return self - - def _eval_conjugate(self): - return -S.ImaginaryUnit - - def _eval_power(self, expt): - """ - b is I = sqrt(-1) - e is symbolic object but not equal to 0, 1 - - I**r -> (-1)**(r/2) -> exp(r/2*Pi*I) -> sin(Pi*r/2) + cos(Pi*r/2)*I, r is decimal - I**0 mod 4 -> 1 - I**1 mod 4 -> I - I**2 mod 4 -> -1 - I**3 mod 4 -> -I - """ - - if isinstance(expt, Number): - if isinstance(expt, Integer): - expt = expt.p % 4 - if expt == 0: - return S.One - if expt == 1: - return S.ImaginaryUnit - if expt == 2: - return -S.One - return -S.ImaginaryUnit - return (S.NegativeOne)**(expt*S.Half) - return - - def as_base_exp(self): - return S.NegativeOne, S.Half - - def _sage_(self): - import sage.all as sage - return sage.I - -I = S.ImaginaryUnit - -try: - # fractions is only available for python 2.6+ - import fractions - - def sympify_fractions(f): - return Rational(f.numerator, f.denominator) - - converter[fractions.Fraction] = sympify_fractions -except ImportError: - pass - -try: - import gmpy - - def sympify_mpz(x): - return Integer(int(x)) - - def sympify_mpq(x): - return Rational(int(x.numer()), int(x.denom())) - - converter[type(gmpy.mpz(1))] = sympify_mpz - converter[type(gmpy.mpq(1, 2))] = sympify_mpq -except ImportError: - pass - - -def sympify_mpmath(x): - return Expr._from_mpmath(x, x.context.prec) - -converter[mpnumeric] = sympify_mpmath - - -def sympify_complex(a): - real, imag = list(map(sympify, (a.real, a.imag))) - return real + S.ImaginaryUnit*imag - -converter[complex] = sympify_complex - -_intcache[0] = S.Zero -_intcache[1] = S.One -_intcache[-1] = S.NegativeOne - -from .power import Pow, integer_nthroot -from .mul import Mul -Mul.identity = One() -from .add import Add -Add.identity = Zero() -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/core/power.html b/dev-py3k/_modules/sympy/core/power.html deleted file mode 100644 index 72f1fc17719..00000000000 --- a/dev-py3k/_modules/sympy/core/power.html +++ /dev/null @@ -1,1204 +0,0 @@ - - - - - - - - - - sympy.core.power — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.core.power

    -from math import log as _log
    -
    -from .sympify import _sympify
    -from .cache import cacheit
    -from .core import C
    -from .singleton import S
    -from .expr import Expr
    -
    -from sympy.core.function import (_coeff_isneg, expand_complex,
    -    expand_multinomial, expand_mul)
    -from sympy.core.logic import fuzzy_bool
    -from sympy.core.compatibility import as_int
    -
    -from sympy.mpmath.libmp import sqrtrem as mpmath_sqrtrem
    -from sympy.utilities.iterables import sift
    -
    -
    -
    [docs]def integer_nthroot(y, n): - """ - Return a tuple containing x = floor(y**(1/n)) - and a boolean indicating whether the result is exact (that is, - whether x**n == y). - - >>> from sympy import integer_nthroot - >>> integer_nthroot(16,2) - (4, True) - >>> integer_nthroot(26,2) - (5, False) - - """ - y, n = int(y), int(n) - if y < 0: - raise ValueError("y must be nonnegative") - if n < 1: - raise ValueError("n must be positive") - if y in (0, 1): - return y, True - if n == 1: - return y, True - if n == 2: - x, rem = mpmath_sqrtrem(y) - return int(x), not rem - if n > y: - return 1, False - # Get initial estimate for Newton's method. Care must be taken to - # avoid overflow - try: - guess = int(y**(1./n) + 0.5) - except OverflowError: - exp = _log(y, 2)/n - if exp > 53: - shift = int(exp - 53) - guess = int(2.0**(exp - shift) + 1) << shift - else: - guess = int(2.0**exp) - #print n - if guess > 2**50: - # Newton iteration - xprev, x = -1, guess - while 1: - t = x**(n - 1) - #xprev, x = x, x - (t*x-y)//(n*t) - xprev, x = x, ((n - 1)*x + y//t)//n - #print n, x-xprev, abs(x-xprev) < 2 - if abs(x - xprev) < 2: - break - else: - x = guess - # Compensate - t = x**n - while t < y: - x += 1 - t = x**n - while t > y: - x -= 1 - t = x**n - return x, t == y - -
    -
    [docs]class Pow(Expr): - - is_Pow = True - - __slots__ = ['is_commutative'] - - @cacheit - def __new__(cls, b, e, evaluate=True): - # don't optimize "if e==0; return 1" here; it's better to handle that - # in the calling routine so this doesn't get called - b = _sympify(b) - e = _sympify(e) - if evaluate: - if e is S.Zero: - return S.One - elif e is S.One: - return b - elif S.NaN in (b, e): - if b is S.One: # already handled e == 0 above - return S.One - return S.NaN - else: - obj = b._eval_power(e) - if obj is not None: - return obj - obj = Expr.__new__(cls, b, e) - obj.is_commutative = (b.is_commutative and e.is_commutative) - return obj - - @property - def base(self): - return self._args[0] - - @property - def exp(self): - return self._args[1] - - @classmethod - def class_key(cls): - return 3, 2, cls.__name__ - - def _eval_power(self, other): - from sympy.functions.elementary.exponential import log - - b, e = self.as_base_exp() - b_nneg = b.is_nonnegative - if b.is_real and not b_nneg and e.is_even: - b = abs(b) - b_nneg = True - smallarg = (abs(e) <= abs(S.Pi/log(b))) - if (other.is_Rational and other.q == 2 and - e.is_real is False and smallarg is False): - return -Pow(b, e*other) - if (other.is_integer or - e.is_real and (b_nneg or abs(e) < 1) or - e.is_real is False and smallarg is True or - b.is_polar): - return Pow(b, e*other) - - def _eval_is_even(self): - if self.exp.is_integer and self.exp.is_positive: - return self.base.is_even - - def _eval_is_positive(self): - if self.base.is_positive: - if self.exp.is_real: - return True - elif self.base.is_negative: - if self.exp.is_even: - return True - if self.exp.is_odd: - return False - elif self.base.is_nonpositive: - if self.exp.is_odd: - return False - - def _eval_is_negative(self): - if self.base.is_negative: - if self.exp.is_odd: - return True - if self.exp.is_even: - return False - elif self.base.is_positive: - if self.exp.is_real: - return False - elif self.base.is_nonnegative: - if self.exp.is_real: - return False - elif self.base.is_nonpositive: - if self.exp.is_even: - return False - elif self.base.is_real: - if self.exp.is_even: - return False - - def _eval_is_integer(self): - b, e = self.args - c1 = b.is_integer - c2 = e.is_integer - if c1 is None or c2 is None: - return None - if not c1 and e.is_nonnegative: # rat**nonneg - return False - if c1 and c2: # int**int - if e.is_nonnegative or e.is_positive: - return True - if self.exp.is_negative: - return False - if c1 and e.is_negative and e.is_bounded: # int**neg - return False - if b.is_Number and e.is_Number: - # int**nonneg or rat**? - check = Pow(*self.args) - return check.is_Integer - - def _eval_is_real(self): - real_b = self.base.is_real - if real_b is None: - return - real_e = self.exp.is_real - if real_e is None: - return - if real_b and real_e: - if self.base.is_positive: - return True - else: # negative or zero (or positive) - if self.exp.is_integer: - return True - elif self.base.is_negative: - if self.exp.is_Rational: - return False - im_b = self.base.is_imaginary - im_e = self.exp.is_imaginary - if im_b: - if self.exp.is_integer: - if self.exp.is_even: - return True - elif self.exp.is_odd: - return False - elif (self.exp in [S.ImaginaryUnit, -S.ImaginaryUnit] and - self.base in [S.ImaginaryUnit, -S.ImaginaryUnit]): - return True - elif self.exp.is_Add: - c, a = self.exp.as_coeff_Add() - if c and c.is_Integer: - return C.Mul( - self.base**c, self.base**a, evaluate=False).is_real - if real_b and im_e: - if self.base is S.NegativeOne: - return True - c = self.exp.coeff(S.ImaginaryUnit) - if c: - ok = (c*C.log(self.base)/S.Pi).is_Integer - if ok is not None: - return ok - - def _eval_is_odd(self): - if self.exp.is_integer: - if self.exp.is_positive: - return self.base.is_odd - elif self.exp.is_nonnegative and self.base.is_odd: - return True - - def _eval_is_bounded(self): - if self.exp.is_negative: - if self.base.is_infinitesimal: - return False - if self.base.is_unbounded: - return True - c1 = self.base.is_bounded - if c1 is None: - return - c2 = self.exp.is_bounded - if c2 is None: - return - if c1 and c2: - if self.exp.is_nonnegative or self.base.is_nonzero: - return True - - def _eval_is_polar(self): - return self.base.is_polar - - def _eval_subs(self, old, new): - if old.func is self.func and self.base == old.base: - coeff1, terms1 = self.exp.as_independent(C.Symbol, as_Add=False) - coeff2, terms2 = old.exp.as_independent(C.Symbol, as_Add=False) - if terms1 == terms2: - pow = coeff1/coeff2 - ok = False # True if int(pow) == pow OR self.base.is_positive - try: - pow = as_int(pow) - ok = True - except ValueError: - ok = self.base.is_positive - if ok: - # issue 2081 - return Pow(new, pow) # (x**(6*y)).subs(x**(3*y),z)->z**2 - if old.func is C.exp and self.exp.is_real and self.base.is_positive: - coeff1, terms1 = old.args[0].as_independent(C.Symbol, as_Add=False) - # we can only do this when the base is positive AND the exponent - # is real - coeff2, terms2 = (self.exp*C.log(self.base)).as_independent( - C.Symbol, as_Add=False) - if terms1 == terms2: - pow = coeff1/coeff2 - if pow == int(pow) or self.base.is_positive: - return Pow(new, pow) # (2**x).subs(exp(x*log(2)), z) -> z - -
    [docs] def as_base_exp(self): - """Return base and exp of self. - - If base is 1/Integer, then return Integer, -exp. If this extra - processing is not needed, the base and exp properties will - give the raw arguments - - Examples - ======== - - >>> from sympy import Pow, S - >>> p = Pow(S.Half, 2, evaluate=False) - >>> p.as_base_exp() - (2, -2) - >>> p.args - (1/2, 2) - - """ - - b, e = self.args - if b.is_Rational and b.p == 1: - return Integer(b.q), -e - return b, e -
    - def _eval_adjoint(self): - from sympy.functions.elementary.complexes import adjoint - i, p = self.exp.is_integer, self.base.is_positive - if i: - return adjoint(self.base)**self.exp - if p: - return self.base**adjoint(self.exp) - if i is False and p is False: - expanded = expand_complex(self) - if expanded != self: - return adjoint(expanded) - - def _eval_conjugate(self): - from sympy.functions.elementary.complexes import conjugate as c - i, p = self.exp.is_integer, self.base.is_positive - if i: - return c(self.base)**self.exp - if p: - return self.base**c(self.exp) - if i is False and p is False: - expanded = expand_complex(self) - if expanded != self: - return c(expanded) - - def _eval_transpose(self): - from sympy.functions.elementary.complexes import transpose - i, p = self.exp.is_integer, self.base.is_complex - if p: - return self.base**self.exp - if i: - return transpose(self.base)**self.exp - if i is False and p is False: - expanded = expand_complex(self) - if expanded != self: - return transpose(expanded) - - def _eval_expand_power_exp(self, **hints): - """a**(n+m) -> a**n*a**m""" - b = self.base - e = self.exp - if e.is_Add and e.is_commutative: - expr = [] - for x in e.args: - expr.append(Pow(self.base, x)) - return Mul(*expr) - return Pow(b, e) - - def _eval_expand_power_base(self, **hints): - """(a*b)**n -> a**n * b**n""" - force = hints.get('force', False) - - b = self.base - e = self.exp - if not b.is_Mul: - return self - - cargs, nc = b.args_cnc(split_1=False) - - # expand each term - this is top-level-only - # expansion but we have to watch out for things - # that don't have an _eval_expand method - if nc: - nc = [i._eval_expand_power_base(**hints) - if hasattr(i, '_eval_expand_power_base') else i - for i in nc] - - if e.is_Integer: - if e.is_positive: - rv = Mul(*nc*e) - else: - rv = 1/Mul(*nc*-e) - assert not cargs - return rv - - if not cargs: - return Pow(Mul(*nc), e, evaluate=False) - - nc = [Mul(*nc)] - - # sift the commutative bases - def pred(x): - if x is S.ImaginaryUnit: - return S.ImaginaryUnit - polar = x.is_polar - if polar: - return True - if polar is None: - return fuzzy_bool(x.is_nonnegative) - sifted = sift(cargs, pred) - nonneg = sifted[True] - other = sifted[None] - neg = sifted[False] - imag = sifted[S.ImaginaryUnit] - if imag: - I = S.ImaginaryUnit - i = len(imag) % 4 - if i == 0: - pass - elif i == 1: - other.append(I) - elif i == 2: - if neg: - nonn = -neg.pop() - if nonn is not S.One: - nonneg.append(nonn) - else: - neg.append(S.NegativeOne) - else: - if neg: - nonn = -neg.pop() - if nonn is not S.One: - nonneg.append(nonn) - else: - neg.append(S.NegativeOne) - other.append(I) - del imag - - # bring out the bases that can be separated from the base - - if force or e.is_integer: - # treat all commutatives the same and put nc in other - cargs = nonneg + neg + other - other = nc - else: - # this is just like what is happening automatically, except - # that now we are doing it for an arbitrary exponent for which - # no automatic expansion is done - - assert not e.is_Integer - - # handle negatives by making them all positive and putting - # the residual -1 in other - if len(neg) > 1: - o = S.One - if not other and neg[0].is_Number: - o *= neg.pop(0) - if len(neg) % 2: - o = -o - for n in neg: - nonneg.append(-n) - if o is not S.One: - other.append(o) - elif neg and other: - if neg[0].is_Number and neg[0] is not S.NegativeOne: - other.append(S.NegativeOne) - nonneg.append(-neg[0]) - else: - other.extend(neg) - else: - other.extend(neg) - del neg - - cargs = nonneg - other += nc - - rv = S.One - if cargs: - rv *= Mul(*[Pow(b, e, evaluate=False) for b in cargs]) - if other: - rv *= Pow(Mul(*other), e, evaluate=False) - return rv - - def _eval_expand_multinomial(self, **hints): - """(a+b+..) ** n -> a**n + n*a**(n-1)*b + .., n is nonzero integer""" - b = self.base - e = self.exp - - if b is None: - base = self.base - else: - base = b - - if e is None: - exp = self.exp - else: - exp = e - - if e is not None or b is not None: - result = Pow(base, exp) - - if result.is_Pow: - base, exp = result.base, result.exp - else: - return result - else: - result = None - - if exp.is_Rational and exp.p > 0 and base.is_Add: - if not exp.is_Integer: - n = Integer(exp.p // exp.q) - - if not n: - return Pow(base, exp) - else: - radical, result = Pow(base, exp - n), [] - - expanded_base_n = Pow(base, n) - if expanded_base_n.is_Pow: - expanded_base_n = \ - expanded_base_n._eval_expand_multinomial() - for term in Add.make_args(expanded_base_n): - result.append(term*radical) - - return Add(*result) - - n = int(exp) - - if base.is_commutative: - order_terms, other_terms = [], [] - - for order in base.args: - if order.is_Order: - order_terms.append(order) - else: - other_terms.append(order) - - if order_terms: - # (f(x) + O(x^n))^m -> f(x)^m + m*f(x)^{m-1} *O(x^n) - f = Add(*other_terms) - - if n == 2: - return expand_multinomial(f**n, deep=False) + \ - n*f*Add(*order_terms) - else: - g = expand_multinomial(f**(n - 1), deep=False) - return expand_mul(f*g, deep=False) + \ - n*g*Add(*order_terms) - - if base.is_number: - # Efficiently expand expressions of the form (a + b*I)**n - # where 'a' and 'b' are real numbers and 'n' is integer. - a, b = base.as_real_imag() - - if a.is_Rational and b.is_Rational: - if not a.is_Integer: - if not b.is_Integer: - k = Pow(a.q * b.q, n) - a, b = a.p*b.q, a.q*b.p - else: - k = Pow(a.q, n) - a, b = a.p, a.q*b - elif not b.is_Integer: - k = Pow(b.q, n) - a, b = a*b.q, b.p - else: - k = 1 - - a, b, c, d = int(a), int(b), 1, 0 - - while n: - if n & 1: - c, d = a*c - b*d, b*c + a*d - n -= 1 - a, b = a*a - b*b, 2*a*b - n //= 2 - - I = S.ImaginaryUnit - - if k == 1: - return c + I*d - else: - return Integer(c)/k + I*d/k - - p = other_terms - # (x+y)**3 -> x**3 + 3*x**2*y + 3*x*y**2 + y**3 - # in this particular example: - # p = [x,y]; n = 3 - # so now it's easy to get the correct result -- we get the - # coefficients first: - from sympy import multinomial_coefficients - expansion_dict = multinomial_coefficients(len(p), n) - # in our example: {(3, 0): 1, (1, 2): 3, (0, 3): 1, (2, 1): 3} - # and now construct the expression. - - # An elegant way would be to use Poly, but unfortunately it is - # slower than the direct method below, so it is commented out: - #b = {} - #for k in expansion_dict: - # b[k] = Integer(expansion_dict[k]) - #return Poly(b, *p).as_expr() - - from sympy.polys.polyutils import basic_from_dict - result = basic_from_dict(expansion_dict, *p) - return result - else: - if n == 2: - return Add(*[f*g for f in base.args for g in base.args]) - else: - multi = (base**(n - 1))._eval_expand_multinomial() - if multi.is_Add: - return Add(*[f*g for f in base.args - for g in multi.args]) - else: - return Add(*[f*multi for f in base.args]) - elif (exp.is_Rational and exp.p < 0 and base.is_Add and - abs(exp.p) > exp.q): - return 1 / Pow(base, -exp)._eval_expand_multinomial() - elif exp.is_Add and base.is_Number: - # a + b a b - # n --> n n , where n, a, b are Numbers - - coeff, tail = S.One, S.Zero - - for term in exp.args: - if term.is_Number: - coeff *= Pow(base, term) - else: - tail += term - - return coeff * Pow(base, tail) - else: - return result - - def as_real_imag(self, deep=True, **hints): - from sympy.polys.polytools import poly - - if self.exp.is_Integer: - exp = self.exp - re, im = self.base.as_real_imag(deep=deep) - if re.func == C.re or im.func == C.im or not im: - return self, S.Zero - a, b = symbols('a b', cls=Dummy) - if exp >= 0: - if re.is_Number and im.is_Number: - # We can be more efficient in this case - expr = expand_multinomial(self.base**exp) - return expr.as_real_imag() - - expr = poly( - (a + b)**exp) # a = re, b = im; expr = (a + b*I)**exp - else: - mag = re**2 + im**2 - re, im = re/mag, -im/mag - if re.is_Number and im.is_Number: - # We can be more efficient in this case - expr = expand_multinomial((re + im*S.ImaginaryUnit)**-exp) - return expr.as_real_imag() - - expr = poly((a + b)**-exp) - - # Terms with even b powers will be real - r = [i for i in expr.terms() if not i[0][1] % 2] - re_part = Add(*[cc*a**aa*b**bb for (aa, bb), cc in r]) - # Terms with odd b powers will be imaginary - r = [i for i in expr.terms() if i[0][1] % 4 == 1] - im_part1 = Add(*[cc*a**aa*b**bb for (aa, bb), cc in r]) - r = [i for i in expr.terms() if i[0][1] % 4 == 3] - im_part3 = Add(*[cc*a**aa*b**bb for (aa, bb), cc in r]) - - return (re_part.subs({a: re, b: S.ImaginaryUnit*im}), - im_part1.subs({a: re, b: im}) + im_part3.subs({a: re, b: -im})) - - elif self.exp.is_Rational: - # NOTE: This is not totally correct since for x**(p/q) with - # x being imaginary there are actually q roots, but - # only a single one is returned from here. - re, im = self.base.as_real_imag(deep=deep) - r = Pow(Pow(re, 2) + Pow(im, 2), S.Half) - t = C.atan2(im, re) - - rp, tp = Pow(r, self.exp), t*self.exp - - return (rp*C.cos(tp), rp*C.sin(tp)) - else: - - if deep: - hints['complex'] = False - - expanded = self.expand(deep, **hints) - if hints.get('ignore') == expanded: - return None - else: - return (C.re(expanded), C.im(expanded)) - else: - return (C.re(self), C.im(self)) - - def _eval_derivative(self, s): - dbase = self.base.diff(s) - dexp = self.exp.diff(s) - return self * (dexp * C.log(self.base) + dbase * self.exp/self.base) - - def _eval_evalf(self, prec): - base, exp = self.as_base_exp() - base = base._evalf(prec) - if not exp.is_Integer: - exp = exp._evalf(prec) - if exp < 0 and base.is_number and base.is_real is False: - base = base.conjugate() / (base * base.conjugate())._evalf(prec) - exp = -exp - return Pow(base, exp).expand() - return Pow(base, exp) - - def _eval_is_polynomial(self, syms): - if self.exp.has(*syms): - return False - - if self.base.has(*syms): - return self.base._eval_is_polynomial(syms) and \ - self.exp.is_Integer and \ - self.exp >= 0 - else: - return True - - def _eval_is_rational(self): - p = self.func(*self.as_base_exp()) # in case it's unevaluated - if not p.is_Pow: - return p.is_rational - b, e = p.as_base_exp() - if e.is_Rational and b.is_Rational: - # we didn't check that e is not an Integer - # because Rational**Integer autosimplifies - return False - if e.is_integer: - return b.is_rational - - def _eval_is_rational_function(self, syms): - if self.exp.has(*syms): - return False - - if self.base.has(*syms): - return self.base._eval_is_rational_function(syms) and \ - self.exp.is_Integer - else: - return True - - def as_numer_denom(self): - if not self.is_commutative: - return self, S.One - base, exp = self.as_base_exp() - n, d = base.as_numer_denom() - # this should be the same as ExpBase.as_numer_denom wrt - # exponent handling - neg_exp = exp.is_negative - int_exp = exp.is_integer - if not neg_exp and not exp.is_real: - neg_exp = _coeff_isneg(exp) - # the denominator cannot be separated from the numerator if - # its sign is unknown unless the exponent is an integer, e.g. - # sqrt(a/b) != sqrt(a)/sqrt(b) when a=1 and b=-1. But if the - # denominator is negative the numerator and denominator can - # be negated and the denominator (now positive) separated. - if not (d.is_real or int_exp): - n = base - d = S.One - dnonpos = d.is_nonpositive - if dnonpos: - n, d = -n, -d - elif dnonpos is None and not int_exp: - n = base - d = S.One - if neg_exp: - n, d = d, n - exp = -exp - return Pow(n, exp), Pow(d, exp) - - def matches(self, expr, repl_dict={}, old=False): - expr = _sympify(expr) - - # special case, pattern = 1 and expr.exp can match to 0 - if expr is S.One: - d = repl_dict.copy() - d = self.exp.matches(S.Zero, d) - if d is not None: - return d - - b, e = expr.as_base_exp() - - # special case number - sb, se = self.as_base_exp() - if sb.is_Symbol and se.is_Integer and expr: - if e.is_rational: - return sb.matches(b**(e/se), repl_dict) - return sb.matches(expr**(1/se), repl_dict) - - d = repl_dict.copy() - d = self.base.matches(b, d) - if d is None: - return None - - d = self.exp.xreplace(d).matches(e, d) - if d is None: - return Expr.matches(self, expr, repl_dict) - return d - - def _eval_nseries(self, x, n, logx): - # NOTE! This function is an important part of the gruntz algorithm - # for computing limits. It has to return a generalized power - # series with coefficients in C(log, log(x)). In more detail: - # It has to return an expression - # c_0*x**e_0 + c_1*x**e_1 + ... (finitely many terms) - # where e_i are numbers (not necessarily integers) and c_i are - # expressions involving only numbers, the log function, and log(x). - from sympy import powsimp, collect, exp, log, O, ceiling - b, e = self.args - if e.is_Integer: - if e > 0: - # positive integer powers are easy to expand, e.g.: - # sin(x)**4 = (x-x**3/3+...)**4 = ... - return expand_multinomial(Pow(b._eval_nseries(x, n=n, - logx=logx), e), deep=False) - elif e is S.NegativeOne: - # this is also easy to expand using the formula: - # 1/(1 + x) = 1 - x + x**2 - x**3 ... - # so we need to rewrite base to the form "1+x" - - b = b._eval_nseries(x, n=n, logx=logx) - prefactor = b.as_leading_term(x) - # express "rest" as: rest = 1 + k*x**l + ... + O(x**n) - rest = expand_mul((b - prefactor)/prefactor) - if rest == 0: - # if prefactor == w**4 + x**2*w**4 + 2*x*w**4, we need to - # factor the w**4 out using collect: - return 1/collect(prefactor, x) - if rest.is_Order: - return 1/prefactor + rest/prefactor - n2 = rest.getn() - if n2 is not None: - n = n2 - # remove the O - powering this is slow - if logx is not None: - rest = rest.removeO() - - k, l = rest.leadterm(x) - if l.is_Rational and l > 0: - pass - elif l.is_number and l > 0: - l = l.evalf() - else: - raise NotImplementedError() - - terms = [1/prefactor] - for m in range(1, ceiling(n/l)): - new_term = terms[-1]*(-rest) - if new_term.is_Pow: - new_term = new_term._eval_expand_multinomial( - deep=False) - else: - new_term = expand_mul(new_term, deep=False) - terms.append(new_term) - - # Append O(...), we know the order. - if n2 is None or logx is not None: - terms.append(O(x**n)) - return powsimp(Add(*terms), deep=True, combine='exp') - else: - # negative powers are rewritten to the cases above, for - # example: - # sin(x)**(-4) = 1/( sin(x)**4) = ... - # and expand the denominator: - denominator = (b**(-e))._eval_nseries(x, n=n, logx=logx) - if 1/denominator == self: - return self - # now we have a type 1/f(x), that we know how to expand - return (1/denominator)._eval_nseries(x, n=n, logx=logx) - - if e.has(Symbol): - return exp(e*log(b))._eval_nseries(x, n=n, logx=logx) - - # see if the base is as simple as possible - bx = b - while bx.is_Pow and bx.exp.is_Rational: - bx = bx.base - if bx == x: - return self - - # work for b(x)**e where e is not an Integer and does not contain x - # and hopefully has no other symbols - - def e2int(e): - """return the integer value (if possible) of e and a - flag indicating whether it is bounded or not.""" - n = e.limit(x, 0) - unbounded = n.is_unbounded - if not unbounded: - # XXX was int or floor intended? int used to behave like floor - # so int(-Rational(1, 2)) returned -1 rather than int's 0 - try: - n = int(n) - except TypeError: - #well, the n is something more complicated (like 1+log(2)) - try: - n = int(n.evalf()) + 1 # XXX why is 1 being added? - except TypeError: - pass # hope that base allows this to be resolved - n = _sympify(n) - return n, unbounded - - order = O(x**n, x) - ei, unbounded = e2int(e) - b0 = b.limit(x, 0) - if unbounded and (b0 is S.One or b0.has(Symbol)): - # XXX what order - if b0 is S.One: - resid = (b - 1) - if resid.is_positive: - return S.Infinity - elif resid.is_negative: - return S.Zero - raise ValueError('cannot determine sign of %s' % resid) - - return b0**ei - - if (b0 is S.Zero or b0.is_unbounded): - if unbounded is not False: - return b0**e # XXX what order - - if not ei.is_number: # if not, how will we proceed? - raise ValueError( - 'expecting numerical exponent but got %s' % ei) - - nuse = n - ei - bs = b._eval_nseries(x, n=nuse, logx=logx) - terms = bs.removeO() - if terms.is_Add: - bs = terms - lt = terms.as_leading_term(x) - - # bs -> lt + rest -> lt*(1 + (bs/lt - 1)) - return ((Pow(lt, e) * Pow((bs/lt).expand(), e).nseries( - x, n=nuse, logx=logx)).expand() + order) - - rv = bs**e - if terms != bs: - rv += order - return rv - - # either b0 is bounded but neither 1 nor 0 or e is unbounded - # b -> b0 + (b-b0) -> b0 * (1 + (b/b0-1)) - o2 = order*(b0**-e) - z = (b/b0 - 1) - o = O(z, x) - #r = self._compute_oseries3(z, o2, self.taylor_term) - if o is S.Zero or o2 is S.Zero: - unbounded = True - else: - if o.expr.is_number: - e2 = log(o2.expr*x)/log(x) - else: - e2 = log(o2.expr)/log(o.expr) - n, unbounded = e2int(e2) - if unbounded: - # requested accuracy gives infinite series, - # order is probably non-polynomial e.g. O(exp(-1/x), x). - r = 1 + z - else: - l = [] - g = None - for i in range(n + 2): - g = self.taylor_term(i, z, g) - g = g.nseries(x, n=n, logx=logx) - l.append(g) - r = Add(*l) - return r*b0**e + order - - def _eval_as_leading_term(self, x): - if not self.exp.has(x): - return Pow(self.base.as_leading_term(x), self.exp) - return C.exp(self.exp * C.log(self.base)).as_leading_term(x) - - @cacheit - def taylor_term(self, n, x, *previous_terms): # of (1+x)**e - if n < 0: - return S.Zero - x = _sympify(x) - return C.binomial(self.exp, n) * Pow(x, n) - - def _sage_(self): - return self.args[0]._sage_()**self.args[1]._sage_() - -
    [docs] def as_content_primitive(self, radical=False): - """Return the tuple (R, self/R) where R is the positive Rational - extracted from self. - - Examples - ======== - - >>> from sympy import sqrt - >>> sqrt(4 + 4*sqrt(2)).as_content_primitive() - (2, sqrt(1 + sqrt(2))) - >>> sqrt(3 + 3*sqrt(2)).as_content_primitive() - (1, sqrt(3)*sqrt(1 + sqrt(2))) - - >>> from sympy import expand_power_base, powsimp, Mul - >>> from sympy.abc import x, y - - >>> ((2*x + 2)**2).as_content_primitive() - (4, (x + 1)**2) - >>> (4**((1 + y)/2)).as_content_primitive() - (2, 4**(y/2)) - >>> (3**((1 + y)/2)).as_content_primitive() - (1, 3**((y + 1)/2)) - >>> (3**((5 + y)/2)).as_content_primitive() - (9, 3**((y + 1)/2)) - >>> eq = 3**(2 + 2*x) - >>> powsimp(eq) == eq - True - >>> eq.as_content_primitive() - (9, 3**(2*x)) - >>> powsimp(Mul(*_)) - 9**(x + 1) - - >>> eq = (2 + 2*x)**y - >>> s = expand_power_base(eq); s.is_Mul, s - (False, (2*x + 2)**y) - >>> eq.as_content_primitive() - (1, (2*(x + 1))**y) - >>> s = expand_power_base(_[1]); s.is_Mul, s - (True, 2**y*(x + 1)**y) - - See docstring of Expr.as_content_primitive for more examples. - """ - - b, e = self.as_base_exp() - b = _keep_coeff(*b.as_content_primitive(radical=radical)) - ce, pe = e.as_content_primitive(radical=radical) - if b.is_Rational: - #e - #= ce*pe - #= ce*(h + t) - #= ce*h + ce*t - #=> self - #= b**(ce*h)*b**(ce*t) - #= b**(cehp/cehq)*b**(ce*t) - #= b**(iceh+r/cehq)*b**(ce*t) - #= b**(iceh)*b**(r/cehq)*b**(ce*t) - #= b**(iceh)*b**(ce*t + r/cehq) - h, t = pe.as_coeff_Add() - if h.is_Rational: - ceh = ce*h - c = Pow(b, ceh) - r = S.Zero - if not c.is_Rational: - iceh, r = divmod(ceh.p, ceh.q) - c = Pow(b, iceh) - return c, Pow(b, _keep_coeff(ce, t + r/ce/ceh.q)) - e = _keep_coeff(ce, pe) - # b**e = (h*t)**e = h**e*t**e = c*m*t**e - if e.is_Rational and b.is_Mul: - h, t = b.as_content_primitive(radical=radical) # h is positive - c, m = Pow(h, e).as_coeff_Mul() # so c is positive - m, me = m.as_base_exp() - if m is S.One or me == e: # probably always true - # return the following, not return c, m*Pow(t, e) - # which would change Pow into Mul; we let sympy - # decide what to do by using the unevaluated Mul, e.g - # should it stay as sqrt(2 + 2*sqrt(5)) or become - # sqrt(2)*sqrt(1 + sqrt(5)) - return c, Pow(_keep_coeff(m, t), e) - return S.One, Pow(b, e) -
    - def is_constant(self, *wrt, **flags): - if flags.get('simplify', True): - self = self.simplify() - b, e = self.as_base_exp() - bz = b.equals(0) - if bz: # recalculate with assumptions in case it's unevaluated - new = b**e - if new != self: - return new.is_constant() - econ = e.is_constant(*wrt) - bcon = b.is_constant(*wrt) - if bcon: - if econ: - return True - bz = b.equals(0) - if bz is False: - return False - elif bcon is None: - return None - - return e.equals(0) -
    -from .add import Add -from .numbers import Integer -from .mul import Mul, _keep_coeff -from .symbol import Symbol, Dummy, symbols -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/core/relational.html b/dev-py3k/_modules/sympy/core/relational.html deleted file mode 100644 index 7fdcac4d99c..00000000000 --- a/dev-py3k/_modules/sympy/core/relational.html +++ /dev/null @@ -1,708 +0,0 @@ - - - - - - - - - - sympy.core.relational — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.core.relational

    -from .basic import S
    -from .expr import Expr
    -from .evalf import EvalfMixin
    -from .sympify import _sympify
    -
    -from sympy.logic.boolalg import Boolean
    -
    -__all__ = (
    -    'Rel', 'Eq', 'Ne', 'Lt', 'Le', 'Gt', 'Ge',
    -    'Relational', 'Equality', 'Unequality', 'StrictLessThan', 'LessThan',
    -    'StrictGreaterThan', 'GreaterThan',
    -)
    -
    -
    -
    [docs]def Rel(a, b, op): - """ - A handy wrapper around the Relational class. - Rel(a,b, op) - - Examples - ======== - - >>> from sympy import Rel - >>> from sympy.abc import x, y - >>> Rel(y, x+x**2, '==') - y == x**2 + x - - """ - return Relational(a, b, op) - -
    -
    [docs]def Eq(a, b=0): - """ - A handy wrapper around the Relational class. - Eq(a,b) - - Examples - ======== - - >>> from sympy import Eq - >>> from sympy.abc import x, y - >>> Eq(y, x+x**2) - y == x**2 + x - - """ - return Relational(a, b, '==') - -
    -
    [docs]def Ne(a, b): - """ - A handy wrapper around the Relational class. - Ne(a,b) - - Examples - ======== - - >>> from sympy import Ne - >>> from sympy.abc import x, y - >>> Ne(y, x+x**2) - y != x**2 + x - - """ - return Relational(a, b, '!=') - -
    -
    [docs]def Lt(a, b): - """ - A handy wrapper around the Relational class. - Lt(a,b) - - Examples - ======== - - >>> from sympy import Lt - >>> from sympy.abc import x, y - >>> Lt(y, x+x**2) - y < x**2 + x - - """ - return Relational(a, b, '<') - -
    -
    [docs]def Le(a, b): - """ - A handy wrapper around the Relational class. - Le(a,b) - - Examples - ======== - - >>> from sympy import Le - >>> from sympy.abc import x, y - >>> Le(y, x+x**2) - y <= x**2 + x - - """ - return Relational(a, b, '<=') - -
    -
    [docs]def Gt(a, b): - """ - A handy wrapper around the Relational class. - Gt(a,b) - - Examples - ======== - - >>> from sympy import Gt - >>> from sympy.abc import x, y - >>> Gt(y, x + x**2) - y > x**2 + x - - """ - return Relational(a, b, '>') - -
    -
    [docs]def Ge(a, b): - """ - A handy wrapper around the Relational class. - Ge(a,b) - - Examples - ======== - - >>> from sympy import Ge - >>> from sympy.abc import x, y - >>> Ge(y, x + x**2) - y >= x**2 + x - - """ - return Relational(a, b, '>=') - -# Note, see issue 1887. Ideally, we wouldn't want to subclass both Boolean -# and Expr. - -
    -class Relational(Boolean, Expr, EvalfMixin): - - __slots__ = [] - - is_Relational = True - - # ValidRelationOperator - Defined below, because the necessary classes - # have not yet been defined - - def __new__(cls, lhs, rhs, rop=None, **assumptions): - lhs = _sympify(lhs) - rhs = _sympify(rhs) - if cls is not Relational: - rop_cls = cls - else: - try: - rop_cls = Relational.ValidRelationOperator[ rop ] - except KeyError: - msg = "Invalid relational operator symbol: '%r'" - raise ValueError(msg % repr(rop)) - if (lhs.is_number and rhs.is_number and - (rop_cls in (Equality, Unequality) or - lhs.is_real and rhs.is_real)): - diff = lhs - rhs - know = diff.equals(0, failing_expression=True) - if know is True: # exclude failing expression case - Nlhs = S.Zero - elif know is False: - from sympy import sign - Nlhs = sign(diff.n(2)) - else: - Nlhs = None - lhs = know - rhs = S.Zero - if Nlhs is not None: - return rop_cls._eval_relation(Nlhs, S.Zero) - - obj = Expr.__new__(rop_cls, lhs, rhs, **assumptions) - return obj - - @property - def lhs(self): - return self._args[0] - - @property - def rhs(self): - return self._args[1] - - def _eval_evalf(self, prec): - return self.func(*[s._evalf(prec) for s in self.args]) - - def doit(self, **hints): - lhs = self.lhs - rhs = self.rhs - if hints.get('deep', True): - lhs = lhs.doit(**hints) - rhs = rhs.doit(**hints) - return self._eval_relation_doit(lhs, rhs) - - @classmethod - def _eval_relation_doit(cls, lhs, rhs): - return cls._eval_relation(lhs, rhs) - - def _eval_simplify(self, ratio, measure): - return self.__class__(self.lhs.simplify(ratio=ratio), - self.rhs.simplify(ratio=ratio)) - - -
    [docs]class Equality(Relational): - - rel_op = '==' - - __slots__ = [] - - is_Equality = True - - @classmethod - def _eval_relation(cls, lhs, rhs): - return lhs == rhs - - @classmethod - def _eval_relation_doit(cls, lhs, rhs): - return Eq(lhs, rhs) - - def __bool__(self): - return self.lhs.compare(self.rhs) == 0 - -
    -
    [docs]class Unequality(Relational): - - rel_op = '!=' - - __slots__ = [] - - @classmethod - def _eval_relation(cls, lhs, rhs): - return lhs != rhs - - @classmethod - def _eval_relation_doit(cls, lhs, rhs): - return Ne(lhs, rhs) - - def __bool__(self): - return self.lhs.compare(self.rhs) != 0 - -
    -class _Greater(Relational): - """Not intended for general use - - _Greater is only used so that GreaterThan and StrictGreaterThan may subclass - it for the .gts and .lts properties. - """ - - __slots__ = () - - @property - def gts(self): - return self._args[0] - - @property - def lts(self): - return self._args[1] - - -class _Less(Relational): - """Not intended for general use. - - _Less is only used so that LessThan and StrictLessThan may subclass it for - the .gts and .lts properties. - """ - - __slots__ = () - - @property - def gts(self): - return self._args[1] - - @property - def lts(self): - return self._args[0] - - -
    [docs]class GreaterThan(_Greater): - """Class representations of inequalities. - - Extended Summary - ================ - - The ``*Than`` classes represent inequal relationships, where the left-hand - side is generally bigger or smaller than the right-hand side. For example, - the GreaterThan class represents an inequal relationship where the - left-hand side is at least as big as the right side, if not bigger. In - mathematical notation: - - lhs >= rhs - - In total, there are four ``*Than`` classes, to represent the four - inequalities: - - +-----------------+--------+ - |Class Name | Symbol | - +=================+========+ - |GreaterThan | (>=) | - +-----------------+--------+ - |LessThan | (<=) | - +-----------------+--------+ - |StrictGreaterThan| (>) | - +-----------------+--------+ - |StrictLessThan | (<) | - +-----------------+--------+ - - All classes take two arguments, lhs and rhs. - - +----------------------------+-----------------+ - |Signature Example | Math equivalent | - +============================+=================+ - |GreaterThan(lhs, rhs) | lhs >= rhs | - +----------------------------+-----------------+ - |LessThan(lhs, rhs) | lhs <= rhs | - +----------------------------+-----------------+ - |StrictGreaterThan(lhs, rhs) | lhs > rhs | - +----------------------------+-----------------+ - |StrictLessThan(lhs, rhs) | lhs < rhs | - +----------------------------+-----------------+ - - In addition to the normal .lhs and .rhs of Relations, ``*Than`` inequality - objects also have the .lts and .gts properties, which represent the "less - than side" and "greater than side" of the operator. Use of .lts and .gts - in an algorithm rather than .lhs and .rhs as an assumption of inequality - direction will make more explicit the intent of a certain section of code, - and will make it similarly more robust to client code changes: - - >>> from sympy import GreaterThan, StrictGreaterThan - >>> from sympy import LessThan, StrictLessThan - >>> from sympy import And, Ge, Gt, Le, Lt, Rel, S - >>> from sympy.abc import x, y, z - >>> from sympy.core.relational import Relational - - >>> e = GreaterThan(x, 1) - >>> e - x >= 1 - >>> '%s >= %s is the same as %s <= %s' % (e.gts, e.lts, e.lts, e.gts) - 'x >= 1 is the same as 1 <= x' - - Examples - ======== - - One generally does not instantiate these classes directly, but uses various - convenience methods: - - >>> e1 = Ge( x, 2 ) # Ge is a convenience wrapper - >>> print(e1) - x >= 2 - - >>> rels = Ge( x, 2 ), Gt( x, 2 ), Le( x, 2 ), Lt( x, 2 ) - >>> print('%s\\n%s\\n%s\\n%s' % rels) - x >= 2 - x > 2 - x <= 2 - x < 2 - - Another option is to use the Python inequality operators (>=, >, <=, <) - directly. Their main advantage over the Ge, Gt, Le, and Lt counterparts, is - that one can write a more "mathematical looking" statement rather than - littering the math with oddball function calls. However there are certain - (minor) caveats of which to be aware (search for 'gotcha', below). - - >>> e2 = x >= 2 - >>> print(e2) - x >= 2 - >>> print("e1: %s, e2: %s" % (e1, e2)) - e1: x >= 2, e2: x >= 2 - >>> e1 == e2 - True - - However, it is also perfectly valid to instantiate a ``*Than`` class less - succinctly and less conveniently: - - >>> rels = Rel(x, 1, '>='), Relational(x, 1, '>='), GreaterThan(x, 1) - >>> print('%s\\n%s\\n%s' % rels) - x >= 1 - x >= 1 - x >= 1 - - >>> rels = Rel(x, 1, '>'), Relational(x, 1, '>'), StrictGreaterThan(x, 1) - >>> print('%s\\n%s\\n%s' % rels) - x > 1 - x > 1 - x > 1 - - >>> rels = Rel(x, 1, '<='), Relational(x, 1, '<='), LessThan(x, 1) - >>> print("%s\\n%s\\n%s" % rels) - x <= 1 - x <= 1 - x <= 1 - - >>> rels = Rel(x, 1, '<'), Relational(x, 1, '<'), StrictLessThan(x, 1) - >>> print('%s\\n%s\\n%s' % rels) - x < 1 - x < 1 - x < 1 - - Notes - ===== - - There are a couple of "gotchas" when using Python's operators. - - The first enters the mix when comparing against a literal number as the lhs - argument. Due to the order that Python decides to parse a statement, it may - not immediately find two objects comparable. For example, to evaluate the - statement (1 < x), Python will first recognize the number 1 as a native - number, and then that x is *not* a native number. At this point, because a - native Python number does not know how to compare itself with a SymPy object - Python will try the reflective operation, (x > 1). Unfortunately, there is - no way available to SymPy to recognize this has happened, so the statement - (1 < x) will turn silently into (x > 1). - - >>> e1 = x > 1 - >>> e2 = x >= 1 - >>> e3 = x < 1 - >>> e4 = x <= 1 - >>> e5 = 1 > x - >>> e6 = 1 >= x - >>> e7 = 1 < x - >>> e8 = 1 <= x - >>> print("%s %s\\n"*4 % (e1, e2, e3, e4, e5, e6, e7, e8)) - x > 1 x >= 1 - x < 1 x <= 1 - x < 1 x <= 1 - x > 1 x >= 1 - - If the order of the statement is important (for visual output to the - console, perhaps), one can work around this annoyance in a couple ways: (1) - "sympify" the literal before comparison, (2) use one of the wrappers, or (3) - use the less succinct methods described above: - - >>> e1 = S(1) > x - >>> e2 = S(1) >= x - >>> e3 = S(1) < x - >>> e4 = S(1) <= x - >>> e5 = Gt(1, x) - >>> e6 = Ge(1, x) - >>> e7 = Lt(1, x) - >>> e8 = Le(1, x) - >>> print("%s %s\\n"*4 % (e1, e2, e3, e4, e5, e6, e7, e8)) - 1 > x 1 >= x - 1 < x 1 <= x - 1 > x 1 >= x - 1 < x 1 <= x - - The other gotcha is with chained inequalities. Occasionally, one may be - tempted to write statements like: - - >>> e = x < y < z # silent error! Where did ``x`` go? - >>> e #doctest: +SKIP - y < z - - Due to an implementation detail or decision of Python [1]_, there is no way - for SymPy to reliably create that as a chained inequality. To create a - chained inequality, the only method currently available is to make use of - And: - - >>> e = And(x < y, y < z) - >>> type( e ) - And - >>> e - And(x < y, y < z) - - Note that this is different than chaining an equality directly via use of - parenthesis (this is currently an open bug in SymPy [2]_): - - >>> e = (x < y) < z - >>> type( e ) - <class 'sympy.core.relational.StrictLessThan'> - >>> e - (x < y) < z - - Any code that explicitly relies on this latter functionality will not be - robust as this behaviour is completely wrong and will be corrected at some - point. For the time being (circa Jan 2012), use And to create chained - inequalities. - - .. [1] This implementation detail is that Python provides no reliable - method to determine that a chained inequality is being built. Chained - comparison operators are evaluated pairwise, using "and" logic (see - http://docs.python.org/reference/expressions.html#notin). This is done - in an efficient way, so that each object being compared is only - evaluated once and the comparison can short-circuit. For example, ``1 - > 2 > 3`` is evaluated by Python as ``(1 > 2) and (2 > 3)``. The - ``and`` operator coerces each side into a bool, returning the object - itself when it short-circuits. Currently, the bool of the --Than - operators will give True or False arbitrarily. Thus, if we were to - compute ``x > y > z``, with ``x``, ``y``, and ``z`` being Symbols, - Python converts the statement (roughly) into these steps: - - (1) x > y > z - (2) (x > y) and (y > z) - (3) (GreaterThanObject) and (y > z) - (4) (GreaterThanObject.__nonzero__()) and (y > z) - (5) (True) and (y > z) - (6) (y > z) - (7) LessThanObject - - Because of the "and" added at step 2, the statement gets turned into a - weak ternary statement. If the first object evalutes __nonzero__ as - True, then the second object, (y > z) is returned. If the first object - evaluates __nonzero__ as False (step 5), then (x > y) is returned. - - In Python, there is no way to override the ``and`` operator, or to - control how it short circuits, so it is impossible to make something - like ``x > y > z`` work. There is an open PEP to change this, - :pep:`335`, but until that is implemented, this cannot be made to work. - - .. [2] For more information, see these two bug reports: - - "Separate boolean and symbolic relationals" - `Issue 1887 <http://code.google.com/p/sympy/issues/detail?id=1887>`_ - - "It right 0 < x < 1 ?" - `Issue 2960 <http://code.google.com/p/sympy/issues/detail?id=2960>`_ - - """ - - rel_op = '>=' - - __slots__ = () - - @classmethod - def _eval_relation(cls, lhs, rhs): - return lhs >= rhs - - def __bool__(self): - return self.lhs.compare( self.rhs ) >= 0 - -
    -
    [docs]class LessThan(_Less): - __doc__ = GreaterThan.__doc__ - __slots__ = () - - rel_op = '<=' - - @classmethod - def _eval_relation(cls, lhs, rhs): - return lhs <= rhs - - def __bool__(self): - return self.lhs.compare( self.rhs ) <= 0 - -
    -
    [docs]class StrictGreaterThan(_Greater): - __doc__ = GreaterThan.__doc__ - __slots__ = () - - rel_op = '>' - - @classmethod - def _eval_relation(cls, lhs, rhs): - return lhs > rhs - - def __bool__(self): - return self.lhs.compare( self.rhs ) > 0 - -
    -
    [docs]class StrictLessThan(_Less): - __doc__ = GreaterThan.__doc__ - __slots__ = () - - rel_op = '<' - - @classmethod - def _eval_relation(cls, lhs, rhs): - return lhs < rhs - - def __bool__(self): - return self.lhs.compare( self.rhs ) < 0 - -# A class-specific (not object-specific) data item used for a minor speedup. It -# is defined here, rather than directly in the class, because the classes that -# it references have not been defined until now (e.g. StrictLessThan).
    -Relational.ValidRelationOperator = { - None: Equality, - '==': Equality, - 'eq': Equality, - '!=': Unequality, - '<>': Unequality, - 'ne': Unequality, - '>=': GreaterThan, - 'ge': GreaterThan, - '<=': LessThan, - 'le': LessThan, - '>': StrictGreaterThan, - 'gt': StrictGreaterThan, - '<': StrictLessThan, - 'lt': StrictLessThan, -} -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/core/sets.html b/dev-py3k/_modules/sympy/core/sets.html deleted file mode 100644 index dcf5176bc9d..00000000000 --- a/dev-py3k/_modules/sympy/core/sets.html +++ /dev/null @@ -1,1400 +0,0 @@ - - - - - - - - - - sympy.core.sets — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.core.sets

    -from sympy.core.sympify import _sympify, sympify
    -from sympy.core.basic import Basic
    -from sympy.core.singleton import Singleton, S
    -from sympy.core.evalf import EvalfMixin
    -from sympy.core.numbers import Float
    -from sympy.core.compatibility import iterable
    -from sympy.core.decorators import deprecated
    -
    -from sympy.mpmath import mpi, mpf
    -from sympy.assumptions import ask
    -from sympy.logic.boolalg import And, Or
    -
    -from sympy.utilities import default_sort_key
    -
    -
    -
    [docs]class Set(Basic): - """ - The base class for any kind of set. - - This is not meant to be used directly as a container of items. - It does not behave like the builtin set; see FiniteSet for that. - - Real intervals are represented by the Interval class and unions of sets - by the Union class. The empty set is represented by the EmptySet class - and available as a singleton as S.EmptySet. - """ - is_number = False - is_iterable = False - is_interval = False - - is_FiniteSet = False - is_Interval = False - is_ProductSet = False - is_Union = False - is_Intersection = None - is_EmptySet = None - is_UniversalSet = None - -
    [docs] def sort_key(self, order=None): - """ - Give sort_key of infimum (if possible) else sort_key of the set. - """ - try: - infimum = self.inf - if infimum.is_comparable: - return default_sort_key(infimum, order) - except (NotImplementedError, ValueError): - pass - args = tuple([default_sort_key(a, order) for a in self._sorted_args]) - return self.class_key(), (len(args), args), S.One.class_key(), S.One -
    -
    [docs] def union(self, other): - """ - Returns the union of 'self' and 'other'. - - As a shortcut it is possible to use the '+' operator: - - >>> from sympy import Interval, FiniteSet - >>> Interval(0, 1).union(Interval(2, 3)) - [0, 1] U [2, 3] - >>> Interval(0, 1) + Interval(2, 3) - [0, 1] U [2, 3] - >>> Interval(1, 2, True, True) + FiniteSet(2, 3) - (1, 2] U {3} - - Similarly it is possible to use the '-' operator for set differences: - - >>> Interval(0, 2) - Interval(0, 1) - (1, 2] - >>> Interval(1, 3) - FiniteSet(2) - [1, 2) U (2, 3] - - """ - return Union(self, other) -
    -
    [docs] def intersect(self, other): - """ - Returns the intersection of 'self' and 'other'. - - >>> from sympy import Interval - - >>> Interval(1, 3).intersect(Interval(1, 2)) - [1, 2] - - """ - return Intersection(self, other) -
    - def _intersect(self, other): - """ - This function should only be used internally - - self._intersect(other) returns a new, intersected set if self knows how - to intersect itself with other, otherwise it returns None - - When making a new set class you can be assured that other will not - be a Union, FiniteSet, or EmptySet - - Used within the Intersection class - """ - return None - - def _union(self, other): - """ - This function should only be used internally - - self._union(other) returns a new, joined set if self knows how - to join itself with other, otherwise it returns None. - It may also return a python set of SymPy Sets if they are somehow - simpler. If it does this it must be idempotent i.e. the sets returned - must return None with _union'ed with each other - - Used within the Union class - """ - return None - - @property -
    [docs] def complement(self): - """ - The complement of 'self'. - - As a shortcut it is possible to use the '~' or '-' operators: - - >>> from sympy import Interval - - >>> Interval(0, 1).complement - (-oo, 0) U (1, oo) - >>> ~Interval(0, 1) - (-oo, 0) U (1, oo) - >>> -Interval(0, 1) - (-oo, 0) U (1, oo) - - """ - return self._complement -
    - @property - def _complement(self): - raise NotImplementedError("(%s)._complement" % self) - - @property -
    [docs] def inf(self): - """ - The infimum of 'self' - - >>> from sympy import Interval, Union - - >>> Interval(0, 1).inf - 0 - >>> Union(Interval(0, 1), Interval(2, 3)).inf - 0 - - """ - return self._inf -
    - @property - def _inf(self): - raise NotImplementedError("(%s)._inf" % self) - - @property -
    [docs] def sup(self): - """ - The supremum of 'self' - - >>> from sympy import Interval, Union - - >>> Interval(0, 1).sup - 1 - >>> Union(Interval(0, 1), Interval(2, 3)).sup - 3 - - """ - return self._sup -
    - @property - def _sup(self): - raise NotImplementedError("(%s)._sup" % self) - -
    [docs] def contains(self, other): - """ - Returns True if 'other' is contained in 'self' as an element. - - As a shortcut it is possible to use the 'in' operator: - - >>> from sympy import Interval - - >>> Interval(0, 1).contains(0.5) - True - >>> 0.5 in Interval(0, 1) - True - - """ - return self._contains(sympify(other, strict=True)) -
    - def _contains(self, other): - raise NotImplementedError("(%s)._contains(%s)" % (self, other)) - -
    [docs] def subset(self, other): - """ - Returns True if 'other' is a subset of 'self'. - - >>> from sympy import Interval - - >>> Interval(0, 1).contains(0) - True - >>> Interval(0, 1, left_open=True).contains(0) - False - - """ - if isinstance(other, Set): - return self.intersect(other) == other - else: - raise ValueError("Unknown argument '%s'" % other) -
    - @property -
    [docs] def measure(self): - """ - The (Lebesgue) measure of 'self' - - >>> from sympy import Interval, Union - - >>> Interval(0, 1).measure - 1 - >>> Union(Interval(0, 1), Interval(2, 3)).measure - 2 - - """ - return self._measure -
    - @property - def _measure(self): - raise NotImplementedError("(%s)._measure" % self) - - def __add__(self, other): - return self.union(other) - - def __or__(self, other): - return self.union(other) - - def __and__(self, other): - return self.intersect(other) - - def __mul__(self, other): - return ProductSet(self, other) - - def __pow__(self, exp): - if not sympify(exp).is_Integer and exp >= 0: - raise ValueError("%s: Exponent must be a positive Integer" % exp) - return ProductSet([self]*exp) - - def __sub__(self, other): - return self.intersect(other.complement) - - def __neg__(self): - return self.complement - - def __invert__(self): - return self.complement - - def __contains__(self, other): - symb = self.contains(other) - result = ask(symb) - if result is None: - raise TypeError('contains did not evaluate to a bool: %r' % symb) - return result - - @property - def is_real(self): - return None - -
    -
    [docs]class ProductSet(Set): - """ - Represents a Cartesian Product of Sets. - - Returns a Cartesian product given several sets as either an iterable - or individual arguments. - - Can use '*' operator on any sets for convenient shorthand. - - Examples - ======== - - >>> from sympy import Interval, FiniteSet, ProductSet - - >>> I = Interval(0, 5); S = FiniteSet(1, 2, 3) - >>> ProductSet(I, S) - [0, 5] x {1, 2, 3} - - >>> (2, 2) in ProductSet(I, S) - True - - >>> Interval(0, 1) * Interval(0, 1) # The unit square - [0, 1] x [0, 1] - - >>> coin = FiniteSet('H', 'T') - >>> set(coin**2) - set([(H, H), (H, T), (T, H), (T, T)]) - - - Notes - ===== - - Passes most operations down to the argument sets - - Flattens Products of ProductSets - - References - ========== - http://en.wikipedia.org/wiki/Cartesian_product - """ - is_ProductSet = True - - def __new__(cls, *sets, **assumptions): - def flatten(arg): - if isinstance(arg, Set): - if arg.is_ProductSet: - return sum(list(map(flatten, arg.args)), []) - else: - return [arg] - elif iterable(arg): - return sum(list(map(flatten, arg)), []) - raise TypeError("Input must be Sets or iterables of Sets") - sets = flatten(list(sets)) - - if EmptySet() in sets or len(sets) == 0: - return EmptySet() - - return Basic.__new__(cls, *sets, **assumptions) - - def _contains(self, element): - """ - 'in' operator for ProductSets - - >>> from sympy import Interval - - >>> (2, 3) in Interval(0, 5) * Interval(0, 5) - True - - >>> (10, 10) in Interval(0, 5) * Interval(0, 5) - False - - Passes operation on to constituent sets - """ - try: - if len(element) != len(self.args): - return False - except TypeError: # maybe element isn't an iterable - return False - return And(*[set.contains(item) for set, item in zip(self.sets, element)]) - - def _intersect(self, other): - """ - This function should only be used internally - - See Set._intersect for docstring - """ - if not other.is_ProductSet: - return None - if len(other.args) != len(self.args): - return S.EmptySet - return ProductSet(a.intersect(b) - for a, b in zip(self.sets, other.sets)) - - @property - def sets(self): - return self.args - - @property - def _complement(self): - # For each set consider it or it's complement - # We need at least one of the sets to be complemented - # Consider all 2^n combinations. - # We can conveniently represent these options easily using a ProductSet - switch_sets = ProductSet(FiniteSet(s, s.complement) for s in self.sets) - product_sets = (ProductSet(*set) for set in switch_sets) - # Union of all combinations but this one - return Union(p for p in product_sets if p != self) - - @property - def is_real(self): - return all(set.is_real for set in self.sets) - - @property - def is_iterable(self): - return all(set.is_iterable for set in self.sets) - - def __iter__(self): - if self.is_iterable: - from sympy.core.compatibility import product - return product(*self.sets) - else: - raise TypeError("Not all constituent sets are iterable") - - @property - def _measure(self): - measure = 1 - for set in self.sets: - measure *= set.measure - return measure - -
    -
    [docs]class Interval(Set, EvalfMixin): - """ - Represents a real interval as a Set. - - Usage: - Returns an interval with end points "start" and "end". - - For left_open=True (default left_open is False) the interval - will be open on the left. Similarly, for right_open=True the interval - will be open on the right. - - Examples - ======== - - >>> from sympy import Symbol, Interval, sets - - >>> Interval(0, 1) - [0, 1] - >>> Interval(0, 1, False, True) - [0, 1) - - >>> a = Symbol('a', real=True) - >>> Interval(0, a) - [0, a] - - Notes - ===== - - Only real end points are supported - - Interval(a, b) with a > b will return the empty set - - Use the evalf() method to turn an Interval into an mpmath - 'mpi' interval instance - - References - ========== - - <http://en.wikipedia.org/wiki/Interval_(mathematics)> - """ - is_Interval = True - is_real = True - - def __new__(cls, start, end, left_open=False, right_open=False): - - start = _sympify(start) - end = _sympify(end) - - # Only allow real intervals (use symbols with 'is_real=True'). - if not start.is_real or not end.is_real: - raise ValueError("Only real intervals are supported") - - # Make sure that the created interval will be valid. - if end.is_comparable and start.is_comparable: - if end < start: - return S.EmptySet - - if end == start and (left_open or right_open): - return S.EmptySet - if end == start and not (left_open or right_open): - return FiniteSet(end) - - # Make sure infinite interval end points are open. - if start == S.NegativeInfinity: - left_open = True - if end == S.Infinity: - right_open = True - - return Basic.__new__(cls, start, end, left_open, right_open) - - @property -
    [docs] def start(self): - """ - The left end point of 'self'. - - This property takes the same value as the 'inf' property. - - >>> from sympy import Interval - - >>> Interval(0, 1).start - 0 - - """ - return self._args[0] -
    - _inf = left = start - - @property -
    [docs] def end(self): - """ - The right end point of 'self'. - - This property takes the same value as the 'sup' property. - - >>> from sympy import Interval - - >>> Interval(0, 1).end - 1 - - """ - return self._args[1] -
    - _sup = right = end - - @property -
    [docs] def left_open(self): - """ - True if 'self' is left-open. - - >>> from sympy import Interval - - >>> Interval(0, 1, left_open=True).left_open - True - >>> Interval(0, 1, left_open=False).left_open - False - - """ - return self._args[2] -
    - @property -
    [docs] def right_open(self): - """ - True if 'self' is right-open. - - >>> from sympy import Interval - - >>> Interval(0, 1, right_open=True).right_open - True - >>> Interval(0, 1, right_open=False).right_open - False - - """ - return self._args[3] -
    - def _intersect(self, other): - """ - This function should only be used internally - - See Set._intersect for docstring - """ - # We only know how to intersect with other intervals - if not other.is_Interval: - return None - # We can't intersect [0,3] with [x,6] -- we don't know if x>0 or x<0 - if not self._is_comparable(other): - return None - - empty = False - - if self.start <= other.end and other.start <= self.end: - # Get topology right. - if self.start < other.start: - start = other.start - left_open = other.left_open - elif self.start > other.start: - start = self.start - left_open = self.left_open - else: - start = self.start - left_open = self.left_open or other.left_open - - if self.end < other.end: - end = self.end - right_open = self.right_open - elif self.end > other.end: - end = other.end - right_open = other.right_open - else: - end = self.end - right_open = self.right_open or other.right_open - - if end - start == 0 and (left_open or right_open): - empty = True - else: - empty = True - - if empty: - return S.EmptySet - - return Interval(start, end, left_open, right_open) - - def _union(self, other): - """ - This function should only be used internally - - See Set._union for docstring - """ - if other.is_Interval and self._is_comparable(other): - from sympy.functions.elementary.miscellaneous import Min, Max - # Non-overlapping intervals - end = Min(self.end, other.end) - start = Max(self.start, other.start) - if (end < start or - (end == start and (end not in self and end not in other))): - return None - else: - start = Min(self.start, other.start) - end = Max(self.end, other.end) - - left_open = ((self.start != start or self.left_open) and - (other.start != start or other.left_open)) - right_open = ((self.end != end or self.right_open) and - (other.end != end or other.right_open)) - - return Interval(start, end, left_open, right_open) - - # If I have open end points and these endpoints are contained in other - if ((self.left_open and other.contains(self.start) is True) or - (self.right_open and other.contains(self.end) is True)): - # Fill in my end points and return - open_left = self.left_open and self.start not in other - open_right = self.right_open and self.end not in other - new_self = Interval(self.start, self.end, open_left, open_right) - return set((new_self, other)) - - return None - - @property - def _complement(self): - a = Interval(S.NegativeInfinity, self.start, True, not self.left_open) - b = Interval(self.end, S.Infinity, not self.right_open, True) - return Union(a, b) - - def _contains(self, other): - if self.left_open: - expr = other > self.start - else: - expr = other >= self.start - - if self.right_open: - expr = And(expr, other < self.end) - else: - expr = And(expr, other <= self.end) - - return expr - - @property - def _measure(self): - return self.end - self.start - - def to_mpi(self, prec=53): - return mpi(mpf(self.start.evalf(prec)), mpf(self.end.evalf(prec))) - - def _eval_evalf(self, prec): - return Interval(self.left.evalf(), self.right.evalf(), - left_open=self.left_open, right_open=self.right_open) - - def _is_comparable(self, other): - is_comparable = self.start.is_comparable - is_comparable &= self.end.is_comparable - is_comparable &= other.start.is_comparable - is_comparable &= other.end.is_comparable - - return is_comparable - - @property -
    [docs] def is_left_unbounded(self): - """Return ``True`` if the left endpoint is negative infinity. """ - return self.left is S.NegativeInfinity or self.left == Float("-inf") -
    - @property -
    [docs] def is_right_unbounded(self): - """Return ``True`` if the right endpoint is positive infinity. """ - return self.right is S.Infinity or self.right == Float("+inf") -
    -
    [docs] def as_relational(self, symbol): - """Rewrite an interval in terms of inequalities and logic operators. """ - from sympy.core.relational import Lt, Le - - if not self.is_left_unbounded: - if self.left_open: - left = Lt(self.start, symbol) - else: - left = Le(self.start, symbol) - - if not self.is_right_unbounded: - if self.right_open: - right = Lt(symbol, self.right) - else: - right = Le(symbol, self.right) - if self.is_left_unbounded and self.is_right_unbounded: - return True # XXX: Contained(symbol, Floats) - elif self.is_left_unbounded: - return right - elif self.is_right_unbounded: - return left - else: - return And(left, right) - -
    -
    [docs]class Union(Set, EvalfMixin): - """ - Represents a union of sets as a Set. - - Examples - ======== - - >>> from sympy import Union, Interval - - >>> Union(Interval(1, 2), Interval(3, 4)) - [1, 2] U [3, 4] - - The Union constructor will always try to merge overlapping intervals, - if possible. For example: - - >>> Union(Interval(1, 2), Interval(2, 3)) - [1, 3] - - See Also - ======== - Intersection - - References - ========== - <http://en.wikipedia.org/wiki/Union_(set_theory)> - """ - is_Union = True - - def __new__(cls, *args, **kwargs): - evaluate = kwargs.get('evaluate', True) - - # flatten inputs to merge intersections and iterables - args = list(args) - - def flatten(arg): - if isinstance(arg, Set): - if arg.is_Union: - return sum(list(map(flatten, arg.args)), []) - else: - return [arg] - if iterable(arg): # and not isinstance(arg, Set) (implicit) - return sum(list(map(flatten, arg)), []) - raise TypeError("Input must be Sets or iterables of Sets") - args = flatten(args) - - # Union of no sets is EmptySet - if len(args) == 0: - return S.EmptySet - - args = sorted(args, key=default_sort_key) - - # Reduce sets using known rules - if evaluate: - return Union.reduce(args) - - return Basic.__new__(cls, *args) - - @staticmethod -
    [docs] def reduce(args): - """ - Simplify a Union using known rules - - We first start with global rules like - 'Merge all FiniteSets' - - Then we iterate through all pairs and ask the constituent sets if they - can simplify themselves with any other constituent - """ - - # ===== Global Rules ===== - # Merge all finite sets - finite_sets = [x for x in args if x.is_FiniteSet] - if len(finite_sets) > 1: - finite_set = FiniteSet(x for set in finite_sets for x in set) - args = [finite_set] + [x for x in args if not x.is_FiniteSet] - - # ===== Pair-wise Rules ===== - # Here we depend on rules built into the constituent sets - args = set(args) - new_args = True - while(new_args): - for s in args: - new_args = False - for t in args - set((s,)): - new_set = s._union(t) - # This returns None if s does not know how to intersect - # with t. Returns the newly intersected set otherwise - if new_set is not None: - if not isinstance(new_set, set): - new_set = set((new_set, )) - new_args = (args - set((s, t))).union(new_set) - break - if new_args: - args = new_args - break - - if len(args) == 1: - return args.pop() - else: - return Union(args, evaluate=False) -
    - @property - def _inf(self): - # We use Min so that sup is meaningful in combination with symbolic - # interval end points. - from sympy.functions.elementary.miscellaneous import Min - return Min(*[set.inf for set in self.args]) - - @property - def _sup(self): - # We use Max so that sup is meaningful in combination with symbolic - # end points. - from sympy.functions.elementary.miscellaneous import Max - return Max(*[set.sup for set in self.args]) - - @property - def _complement(self): - # De Morgan's formula. - complement = self.args[0].complement - for set in self.args[1:]: - complement = complement.intersect(set.complement) - return complement - - def _contains(self, other): - or_args = [the_set.contains(other) for the_set in self.args] - return Or(*or_args) - - @property - def _measure(self): - # Measure of a union is the sum of the measures of the sets minus - # the sum of their pairwise intersections plus the sum of their - # triple-wise intersections minus ... etc... - - # Sets is a collection of intersections and a set of elementary - # sets which made up those intersections (called "sos" for set of sets) - # An example element might of this list might be: - # ( {A,B,C}, A.intersect(B).intersect(C) ) - - # Start with just elementary sets ( ({A}, A), ({B}, B), ... ) - # Then get and subtract ( ({A,B}, (A int B), ... ) while non-zero - sets = [(FiniteSet(s), s) for s in self.args] - measure = 0 - parity = 1 - while sets: - # Add up the measure of these sets and add or subtract it to total - measure += parity * sum(inter.measure for sos, inter in sets) - - # For each intersection in sets, compute the intersection with every - # other set not already part of the intersection. - sets = ((sos + FiniteSet(newset), newset.intersect(intersection)) - for sos, intersection in sets for newset in self.args - if newset not in sos) - - # Clear out sets with no measure - sets = [(sos, inter) for sos, inter in sets if inter.measure != 0] - - # Clear out duplicates - sos_list = [] - sets_list = [] - for set in sets: - if set[0] in sos_list: - continue - else: - sos_list.append(set[0]) - sets_list.append(set) - sets = sets_list - - # Flip Parity - next time subtract/add if we added/subtracted here - parity *= -1 - return measure - -
    [docs] def as_relational(self, symbol): - """Rewrite a Union in terms of equalities and logic operators. """ - return Or(*[set.as_relational(symbol) for set in self.args]) -
    - @property - def is_iterable(self): - return all(arg.is_iterable for arg in self.args) - - def _eval_evalf(self, prec): - try: - return Union(set.evalf() for set in self.args) - except: - raise TypeError("Not all sets are evalf-able") - - def __iter__(self): - import itertools - if all(set.is_iterable for set in self.args): - return itertools.chain(*(iter(arg) for arg in self.args)) - else: - raise TypeError("Not all constituent sets are iterable") - - @property - def is_real(self): - return all(set.is_real for set in self.args) - -
    -
    [docs]class Intersection(Set): - """ - Represents an intersection of sets as a Set. - - Examples - ======== - - >>> from sympy import Intersection, Interval - - >>> Intersection(Interval(1, 3), Interval(2, 4)) - [2, 3] - - We often use the .intersect method - - >>> Interval(1,3).intersect(Interval(2,4)) - [2, 3] - - See Also - ======== - Union - - References - ========== - <http://en.wikipedia.org/wiki/Intersection_(set_theory)> - """ - is_Intersection = True - - def __new__(cls, *args, **kwargs): - evaluate = kwargs.get('evaluate', True) - - # flatten inputs to merge intersections and iterables - args = list(args) - - def flatten(arg): - if isinstance(arg, Set): - if arg.is_Intersection: - return sum(list(map(flatten, arg.args)), []) - else: - return [arg] - if iterable(arg): # and not isinstance(arg, Set) (implicit) - return sum(list(map(flatten, arg)), []) - raise TypeError("Input must be Sets or iterables of Sets") - args = flatten(args) - - # Intersection of no sets is everything - if len(args) == 0: - return S.UniversalSet - - args = sorted(args, key=default_sort_key) - - # Reduce sets using known rules - if evaluate: - return Intersection.reduce(args) - - return Basic.__new__(cls, *args) - - @property - def is_iterable(self): - return any(arg.is_iterable for arg in self.args) - - @property - def _inf(self): - raise NotImplementedError() - - @property - def _sup(self): - raise NotImplementedError() - - @property - def _complement(self): - raise NotImplementedError() - - def _contains(self, other): - from sympy.logic.boolalg import And - return And(*[set.contains(other) for set in self.args]) - - def __iter__(self): - for s in self.args: - if s.is_iterable: - other_sets = set(self.args) - set((s,)) - other = Intersection(other_sets, evaluate=False) - return (x for x in s if x in other) - - raise ValueError("None of the constituent sets are iterable") - - @staticmethod -
    [docs] def reduce(args): - """ - Simplify an intersection using known rules - - We first start with global rules like - 'if any empty sets return empty set' and 'distribute any unions' - - Then we iterate through all pairs and ask the constituent sets if they - can simplify themselves with any other constituent - """ - - # ===== Global Rules ===== - # If any EmptySets return EmptySet - if any(s.is_EmptySet for s in args): - return S.EmptySet - - # If any FiniteSets see which elements of that finite set occur within - # all other sets in the intersection - for s in args: - if s.is_FiniteSet: - return s.__class__(x for x in s - if all(x in other for other in args)) - - # If any of the sets are unions, return a Union of Intersections - for s in args: - if s.is_Union: - other_sets = set(args) - set((s,)) - other = Intersection(other_sets) - return Union(Intersection(arg, other) for arg in s.args) - - # At this stage we are guaranteed not to have any - # EmptySets, FiniteSets, or Unions in the intersection - - # ===== Pair-wise Rules ===== - # Here we depend on rules built into the constituent sets - args = set(args) - new_args = True - while(new_args): - for s in args: - new_args = False - for t in args - set((s,)): - new_set = s._intersect(t) - # This returns None if s does not know how to intersect - # with t. Returns the newly intersected set otherwise - if new_set is not None: - new_args = (args - set((s, t))).union(set((new_set, ))) - break - if new_args: - args = new_args - break - - if len(args) == 1: - return args.pop() - else: - return Intersection(args, evaluate=False) -
    -
    [docs] def as_relational(self, symbol): - """Rewrite an Intersection in terms of equalities and logic operators""" - return And(*[set.as_relational(symbol) for set in self.args]) - -
    -
    [docs]class EmptySet(Set, metaclass=Singleton): - """ - Represents the empty set. The empty set is available as a singleton - as S.EmptySet. - - Examples - ======== - - >>> from sympy import S, Interval - - >>> S.EmptySet - EmptySet() - - >>> Interval(1, 2).intersect(S.EmptySet) - EmptySet() - - See Also - ======== - UniversalSet - - References - ========== - http://en.wikipedia.org/wiki/Empty_set - """ - is_EmptySet = True - - def _intersect(self, other): - return S.EmptySet - - @property - def _complement(self): - return S.UniversalSet - - @property - def _measure(self): - return 0 - - def _contains(self, other): - return False - - def as_relational(self, symbol): - return False - - def __len__(self): - return 0 - - def _union(self, other): - return other - - def __iter__(self): - return iter([]) - -
    -
    [docs]class UniversalSet(Set, metaclass=Singleton): - """ - Represents the set of all things. - The universal set is available as a singleton as S.UniversalSet - - Examples - ======== - - >>> from sympy import S, Interval - - >>> S.UniversalSet - UniversalSet() - - >>> Interval(1, 2).intersect(S.UniversalSet) - [1, 2] - - See Also - ======== - EmptySet - - References - ========== - http://en.wikipedia.org/wiki/Universal_set - """ - is_UniversalSet = True - - def _intersect(self, other): - return other - - @property - def _complement(self): - return S.EmptySet - - @property - def _measure(self): - return S.Infinity - - def _contains(self, other): - return True - - def as_relational(self, symbol): - return True - - def _union(self, other): - return self - -
    -
    [docs]class FiniteSet(Set, EvalfMixin): - """ - Represents a finite set of discrete numbers - - Examples - ======== - - >>> from sympy import Symbol, FiniteSet, sets - - >>> FiniteSet(1, 2, 3, 4) - {1, 2, 3, 4} - >>> 3 in FiniteSet(1, 2, 3, 4) - True - - References - ========== - http://en.wikipedia.org/wiki/Finite_set - """ - is_FiniteSet = True - is_iterable = True - - def __new__(cls, *args, **kwargs): - evaluate = kwargs.get('evaluate', True) - if evaluate: - if len(args) == 1 and iterable(args[0]): - args = args[0] - - args = list(map(sympify, args)) - - if len(args) == 0: - return EmptySet() - - - args = frozenset(args) # remove duplicates - obj = Basic.__new__(cls, *args) - obj._elements = args - return obj - - def __iter__(self): - return iter(self.args) - - def _intersect(self, other): - """ - This function should only be used internally - - See Set._intersect for docstring - """ - if isinstance(other, self.__class__): - return self.__class__(*(self._elements & other._elements)) - return self.__class__(el for el in self if el in other) - - def _union(self, other): - """ - This function should only be used internally - - See Set._union for docstring - """ - if other.is_FiniteSet: - return FiniteSet(*(self._elements | other._elements)) - - # If other set contains one of my elements, remove it from myself - if any(other.contains(x) is True for x in self): - return set(( - FiniteSet(x for x in self if other.contains(x) is not True), - other)) - - return None - - def _contains(self, other): - """ - Tests whether an element, other, is in the set. - - Relies on Python's set class. This tests for object equality - All inputs are sympified - - >>> from sympy import FiniteSet - - >>> 1 in FiniteSet(1, 2) - True - >>> 5 in FiniteSet(1, 2) - False - - """ - return other in self._elements - - @property - def _complement(self): - """ - The complement of a real finite set is the Union of open Intervals - between the elements of the set. - - >>> from sympy import FiniteSet - >>> FiniteSet(1, 2, 3).complement - (-oo, 1) U (1, 2) U (2, 3) U (3, oo) - - - """ - if not all(elem.is_number for elem in self): - raise ValueError("%s: Complement not defined for symbolic inputs" - % self) - - # as there are only numbers involved, a straight sort is sufficient; - # default_sort_key is not needed - args = sorted(self.args) - - intervals = [] # Build up a list of intervals between the elements - intervals += [Interval(S.NegativeInfinity, args[0], True, True)] - for a, b in zip(args[:-1], args[1:]): - intervals.append(Interval(a, b, True, True)) # open intervals - intervals.append(Interval(args[-1], S.Infinity, True, True)) - return Union(intervals, evaluate=False) - - @property - def _inf(self): - from sympy.functions.elementary.miscellaneous import Min - return Min(*self) - - @property - def _sup(self): - from sympy.functions.elementary.miscellaneous import Max - return Max(*self) - - @property - def measure(self): - return 0 - - def __len__(self): - return len(self.args) - - def __sub__(self, other): - return FiniteSet(el for el in self if el not in other) - -
    [docs] def as_relational(self, symbol): - """Rewrite a FiniteSet in terms of equalities and logic operators. """ - from sympy.core.relational import Eq - return Or(*[Eq(symbol, elem) for elem in self]) -
    - @property - def is_real(self): - return all(el.is_real for el in self) - - def compare(self, other): - return (hash(self) - hash(other)) - - def _eval_evalf(self, prec): - return FiniteSet(elem.evalf(prec) for elem in self) - - def _hashable_content(self): - return (self._elements,) - - @property - def _sorted_args(self): - from sympy.utilities import default_sort_key - return sorted(self.args, key=default_sort_key)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/core/singleton.html b/dev-py3k/_modules/sympy/core/singleton.html deleted file mode 100644 index 961cc49d79b..00000000000 --- a/dev-py3k/_modules/sympy/core/singleton.html +++ /dev/null @@ -1,195 +0,0 @@ - - - - - - - - - - sympy.core.singleton — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.core.singleton

    -"""Singleton mechanism"""
    -
    -from .core import Registry
    -from .assumptions import ManagedProperties
    -from .sympify import sympify
    -
    -
    -class SingletonRegistry(Registry):
    -    """
    -    A map between singleton classes and the corresponding instances.
    -    E.g. S.Exp == C.Exp()
    -    """
    -    __slots__ = []
    -
    -    __call__ = staticmethod(sympify)
    -
    -    def __repr__(self):
    -        return "S"
    -
    -S = SingletonRegistry()
    -
    -
    -class Singleton(ManagedProperties):
    -    """
    -    Metaclass for singleton classes.
    -
    -    A singleton class has only one instance which is returned every time the
    -    class is instantiated. Additionally, this instance can be accessed through
    -    the global registry object S as S.<class_name>.
    -
    -    Examples
    -    ========
    -
    -        >>> from sympy import S, Basic
    -        >>> from sympy.core.singleton import Singleton
    -        >>> class MySingleton(Basic, metaclass=Singleton):
    -        ...     pass
    -        >>> Basic() is Basic()
    -        False
    -        >>> MySingleton() is MySingleton()
    -        True
    -        >>> S.MySingleton is MySingleton()
    -        True
    -
    -    ** Developer notes **
    -        The class is instanciated immediately at the point where it is defined
    -        by calling cls.__new__(cls). This instance is cached and cls.__new__ is
    -        rebound to return it directly.
    -
    -        The original constructor is also cached to allow subclasses to access it
    -        and have their own instance.
    -
    -    """
    -
    -    def __init__(cls, name, bases, dict_):
    -        super(Singleton, cls).__init__(cls, name, bases, dict_)
    -
    -        for ancestor in cls.mro():
    -            if '__new__' in ancestor.__dict__:
    -                break
    -        if isinstance(ancestor, Singleton) and ancestor is not cls:
    -            ctor = ancestor._new_instance
    -        else:
    -            ctor = cls.__new__
    -        cls._new_instance = staticmethod(ctor)
    -
    -        the_instance = ctor(cls)
    -
    -        def __new__(cls):
    -            return the_instance
    -        cls.__new__ = staticmethod(__new__)
    -
    -        setattr(S, name, the_instance)
    -
    -        # Inject pickling support.
    -        def __getnewargs__(self):
    -            return ()
    -        cls.__getnewargs__ = __getnewargs__
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/core/symbol.html b/dev-py3k/_modules/sympy/core/symbol.html deleted file mode 100644 index 9df4a9ac305..00000000000 --- a/dev-py3k/_modules/sympy/core/symbol.html +++ /dev/null @@ -1,594 +0,0 @@ - - - - - - - - - - sympy.core.symbol — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.core.symbol

    -from sympy.core.assumptions import StdFactKB
    -from .basic import Basic
    -from .core import C
    -from .sympify import sympify
    -from .singleton import S
    -from .expr import Expr, AtomicExpr
    -from .cache import cacheit
    -from .function import FunctionClass
    -from sympy.core.logic import fuzzy_bool
    -from sympy.logic.boolalg import Boolean
    -from sympy.utilities.exceptions import SymPyDeprecationWarning
    -
    -import re, string
    -
    -
    -
    [docs]class Symbol(AtomicExpr, Boolean): - """ - Assumptions: - commutative = True - - You can override the default assumptions in the constructor: - - >>> from sympy import symbols - >>> A,B = symbols('A,B', commutative = False) - >>> bool(A*B != B*A) - True - >>> bool(A*B*2 == 2*A*B) == True # multiplication by scalars is commutative - True - - """ - - is_comparable = False - - __slots__ = ['name'] - - is_Symbol = True - - @property - def _diff_wrt(self): - """Allow derivatives wrt Symbols. - - Examples - ======== - - >>> from sympy import Symbol - >>> x = Symbol('x') - >>> x._diff_wrt - True - """ - return True - - def __new__(cls, name, **assumptions): - """Symbols are identified by name and assumptions:: - - >>> from sympy import Symbol - >>> Symbol("x") == Symbol("x") - True - >>> Symbol("x", real=True) == Symbol("x", real=False) - False - - """ - - if 'dummy' in assumptions: - SymPyDeprecationWarning( - feature="Symbol('x', dummy=True)", - useinstead="Dummy() or symbols(..., cls=Dummy)", - issue=3378, deprecated_since_version="0.7.0", - ).warn() - if assumptions.pop('dummy'): - return Dummy(name, **assumptions) - if assumptions.get('zero', False): - return S.Zero - is_commutative = fuzzy_bool(assumptions.get('commutative', True)) - if is_commutative is None: - raise ValueError( - '''Symbol commutativity must be True or False.''') - assumptions['commutative'] = is_commutative - return Symbol.__xnew_cached_(cls, name, **assumptions) - - def __new_stage2__(cls, name, **assumptions): - assert isinstance(name, str), repr(type(name)) - obj = Expr.__new__(cls) - obj.name = name - obj._assumptions = StdFactKB(assumptions) - return obj - - __xnew__ = staticmethod( - __new_stage2__) # never cached (e.g. dummy) - __xnew_cached_ = staticmethod( - cacheit(__new_stage2__)) # symbols are always cached - - def __getnewargs__(self): - return (self.name,) - - def __getstate__(self): - return {'_assumptions': self._assumptions} - - def _hashable_content(self): - return (self.name,) + tuple(sorted(self.assumptions0.items())) - - @property - def assumptions0(self): - return dict((key, value) for key, value - in self._assumptions.items() if value is not None) - - @cacheit - def sort_key(self, order=None): - return self.class_key(), (1, (str(self),)), S.One.sort_key(), S.One - - def as_dummy(self): - return Dummy(self.name, **self.assumptions0) - - def __call__(self, *args): - from .function import Function - return Function(self.name)(*args) - - def as_real_imag(self, deep=True, **hints): - if hints.get('ignore') == self: - return None - else: - return (C.re(self), C.im(self)) - - def _sage_(self): - import sage.all as sage - return sage.var(self.name) - - def is_constant(self, *wrt, **flags): - if not wrt: - return False - return not self in wrt - - @property - def is_number(self): - return False - - @property - def free_symbols(self): - return set([self]) - -
    -
    [docs]class Dummy(Symbol): - """Dummy symbols are each unique, identified by an internal count index: - - >>> from sympy import Dummy - >>> bool(Dummy("x") == Dummy("x")) == True - False - - If a name is not supplied then a string value of the count index will be - used. This is useful when a temporary variable is needed and the name - of the variable used in the expression is not important. - - >>> Dummy._count = 0 # /!\ this should generally not be changed; it is being - >>> Dummy() # used here to make sure that the doctest passes. - _0 - - """ - - _count = 0 - - __slots__ = ['dummy_index'] - - is_Dummy = True - - def __new__(cls, name=None, **assumptions): - if name is None: - name = str(Dummy._count) - - is_commutative = fuzzy_bool(assumptions.get('commutative', True)) - if is_commutative is None: - raise ValueError( - '''Dummy's commutativity must be True or False.''') - assumptions['commutative'] = is_commutative - obj = Symbol.__xnew__(cls, name, **assumptions) - - Dummy._count += 1 - obj.dummy_index = Dummy._count - return obj - - def __getstate__(self): - return {'_assumptions': self._assumptions, 'dummy_index': self.dummy_index} - - def _hashable_content(self): - return Symbol._hashable_content(self) + (self.dummy_index,) - -
    -
    [docs]class Wild(Symbol): - """ - A Wild symbol matches anything. - - Examples - ======== - - >>> from sympy import Wild, WildFunction, cos, pi - >>> from sympy.abc import x - >>> a = Wild('a') - >>> b = Wild('b') - >>> b.match(a) - {a_: b_} - >>> x.match(a) - {a_: x} - >>> pi.match(a) - {a_: pi} - >>> (x**2).match(a) - {a_: x**2} - >>> cos(x).match(a) - {a_: cos(x)} - >>> A = WildFunction('A') - >>> A.match(a) - {a_: A_} - """ - - __slots__ = ['exclude', 'properties'] - is_Wild = True - - def __new__(cls, name, exclude=(), properties=(), **assumptions): - exclude = tuple([sympify(x) for x in exclude]) - properties = tuple(properties) - is_commutative = fuzzy_bool(assumptions.get('commutative', True)) - if is_commutative is None: - raise ValueError( - '''Wild's commutativity must be True or False.''') - assumptions['commutative'] = is_commutative - return Wild.__xnew__(cls, name, exclude, properties, **assumptions) - - def __getnewargs__(self): - return (self.name, self.exclude, self.properties) - - @staticmethod - @cacheit - def __xnew__(cls, name, exclude, properties, **assumptions): - obj = Symbol.__xnew__(cls, name, **assumptions) - obj.exclude = exclude - obj.properties = properties - return obj - - def _hashable_content(self): - return super(Wild, self)._hashable_content() + (self.exclude, self.properties) - - # TODO add check against another Wild - def matches(self, expr, repl_dict={}, old=False): - if any(expr.has(x) for x in self.exclude): - return None - if any(not f(expr) for f in self.properties): - return None - repl_dict = repl_dict.copy() - repl_dict[self] = expr - return repl_dict - - def __call__(self, *args, **kwargs): - raise TypeError("'%s' object is not callable" % type(self).__name__) -
    -_re_var_range = re.compile(r"^(.*?)(\d*):(\d+)$") -_re_var_scope = re.compile(r"^(.*?|)(.):(.)(.*?|)$") -_re_var_split = re.compile(r"\s*,\s*|\s+") - - -
    [docs]def symbols(names, **args): - """ - Transform strings into instances of :class:`Symbol` class. - - :func:`symbols` function returns a sequence of symbols with names taken - from ``names`` argument, which can be a comma or whitespace delimited - string, or a sequence of strings:: - - >>> from sympy import symbols, Function - - >>> x, y, z = symbols('x,y,z') - >>> a, b, c = symbols('a b c') - - The type of output is dependent on the properties of input arguments:: - - >>> symbols('x') - x - >>> symbols('x,') - (x,) - >>> symbols('x,y') - (x, y) - >>> symbols(('a', 'b', 'c')) - (a, b, c) - >>> symbols(['a', 'b', 'c']) - [a, b, c] - >>> symbols(set(['a', 'b', 'c'])) - set([a, b, c]) - - If an iterable container is needed for a single symbol, set the ``seq`` - argument to ``True`` or terminate the symbol name with a comma:: - - >>> symbols('x', seq=True) - (x,) - - To reduce typing, range syntax is supported to create indexed symbols:: - - >>> symbols('x:10') - (x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) - - >>> symbols('x5:10') - (x5, x6, x7, x8, x9) - - >>> symbols('x5:10,y:5') - (x5, x6, x7, x8, x9, y0, y1, y2, y3, y4) - - >>> symbols(('x5:10', 'y:5')) - ((x5, x6, x7, x8, x9), (y0, y1, y2, y3, y4)) - - To reduce typing even more, lexicographic range syntax is supported:: - - >>> symbols('x:z') - (x, y, z) - - >>> symbols('a:d,x:z') - (a, b, c, d, x, y, z) - - >>> symbols(('a:d', 'x:z')) - ((a, b, c, d), (x, y, z)) - - All newly created symbols have assumptions set accordingly to ``args``:: - - >>> a = symbols('a', integer=True) - >>> a.is_integer - True - - >>> x, y, z = symbols('x,y,z', real=True) - >>> x.is_real and y.is_real and z.is_real - True - - Despite its name, :func:`symbols` can create symbol--like objects of - other type, for example instances of Function or Wild classes. To - achieve this, set ``cls`` keyword argument to the desired type:: - - >>> symbols('f,g,h', cls=Function) - (f, g, h) - - >>> type(_[0]) - <class 'sympy.core.function.UndefinedFunction'> - - """ - result = [] - if 'each_char' in args: - if args['each_char']: - value = "Tip: ' '.join(s) will transform a string s = 'xyz' to 'x y z'." - else: - value = "" - SymPyDeprecationWarning( - feature="each_char in the options to symbols() and var()", - useinstead="spaces or commas between symbol names", - issue=1919, deprecated_since_version="0.7.0", value=value - ).warn() - - if isinstance(names, str): - names = names.strip() - as_seq = names.endswith(',') - if as_seq: - names = names[:-1].rstrip() - if not names: - raise ValueError('no symbols given') - - names = _re_var_split.split(names) - if args.pop('each_char', False) and not as_seq and len(names) == 1: - return symbols(tuple(names[0]), **args) - - cls = args.pop('cls', Symbol) - seq = args.pop('seq', as_seq) - - for name in names: - if not name: - raise ValueError('missing symbol') - - if ':' not in name: - symbol = cls(name, **args) - result.append(symbol) - continue - - match = _re_var_range.match(name) - - if match is not None: - name, start, end = match.groups() - - if not start: - start = 0 - else: - start = int(start) - - for i in range(start, int(end)): - symbol = cls("%s%i" % (name, i), **args) - result.append(symbol) - - seq = True - continue - - match = _re_var_scope.match(name) - - if match is not None: - name, start, end, suffix = match.groups() - letters = list(string.ascii_lowercase + string.ascii_uppercase - + string.digits) - start = letters.index(start) - end = letters.index(end) - - for subname in range(start, end + 1): - symbol = cls(name + letters[subname] + suffix, **args) - result.append(symbol) - - seq = True - continue - - raise ValueError( - "'%s' is not a valid symbol range specification" % name) - - if not seq and len(result) <= 1: - if not result: - raise ValueError('missing symbol') # should never happen - return result[0] - - return tuple(result) - else: - for name in names: - result.append(symbols(name, **args)) - - return type(names)(result) - -
    -
    [docs]def var(names, **args): - """ - Create symbols and inject them into the global namespace. - - This calls :func:`symbols` with the same arguments and puts the results - into the *global* namespace. It's recommended not to use :func:`var` in - library code, where :func:`symbols` has to be used:: - - >>> from sympy import var - - >>> var('x') - x - >>> x - x - - >>> var('a,ab,abc') - (a, ab, abc) - >>> abc - abc - - >>> var('x,y', real=True) - (x, y) - >>> x.is_real and y.is_real - True - - See :func:`symbol` documentation for more details on what kinds of - arguments can be passed to :func:`var`. - - """ - def traverse(symbols, frame): - """Recursively inject symbols to the global namespace. """ - for symbol in symbols: - if isinstance(symbol, Basic): - frame.f_globals[symbol.name] = symbol - elif isinstance(symbol, FunctionClass): - frame.f_globals[symbol.__name__] = symbol - else: - traverse(symbol, frame) - - from inspect import currentframe - frame = currentframe().f_back - - try: - syms = symbols(names, **args) - - if syms is not None: - if isinstance(syms, Basic): - frame.f_globals[syms.name] = syms - elif isinstance(syms, FunctionClass): - frame.f_globals[syms.__name__] = syms - else: - traverse(syms, frame) - finally: - del frame # break cyclic dependencies as stated in inspect docs - - return syms
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/core/sympify.html b/dev-py3k/_modules/sympy/core/sympify.html deleted file mode 100644 index 4573d966392..00000000000 --- a/dev-py3k/_modules/sympy/core/sympify.html +++ /dev/null @@ -1,395 +0,0 @@ - - - - - - - - - - sympy.core.sympify — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.core.sympify

    -"""sympify -- convert objects SymPy internal format"""
    -
    -from inspect import getmro
    -
    -from .core import all_classes as sympy_classes
    -from sympy.core.compatibility import iterable
    -
    -
    -class SympifyError(ValueError):
    -    def __init__(self, expr, base_exc=None):
    -        self.expr = expr
    -        self.base_exc = base_exc
    -
    -    def __str__(self):
    -        if self.base_exc is None:
    -            return "SympifyError: %r" % (self.expr,)
    -
    -        return ("Sympify of expression '%s' failed, because of exception being "
    -            "raised:\n%s: %s" % (self.expr, self.base_exc.__class__.__name__,
    -            str(self.base_exc)))
    -
    -converter = {}  # See sympify docstring.
    -
    -
    -
    [docs]def sympify(a, locals=None, convert_xor=True, strict=False, rational=False): - """ - Converts an arbitrary expression to a type that can be used inside sympy. - - For example, it will convert python ints into instance of sympy.Rational, - floats into instances of sympy.Float, etc. It is also able to coerce symbolic - expressions which inherit from Basic. This can be useful in cooperation - with SAGE. - - It currently accepts as arguments: - - any object defined in sympy (except matrices [TODO]) - - standard numeric python types: int, long, float, Decimal - - strings (like "0.09" or "2e-19") - - booleans, including ``None`` (will leave them unchanged) - - lists, sets or tuples containing any of the above - - If the argument is already a type that sympy understands, it will do - nothing but return that value. This can be used at the beginning of a - function to ensure you are working with the correct type. - - >>> from sympy import sympify - - >>> sympify(2).is_integer - True - >>> sympify(2).is_real - True - - >>> sympify(2.0).is_real - True - >>> sympify("2.0").is_real - True - >>> sympify("2e-45").is_real - True - - If the expression could not be converted, a SympifyError is raised. - - >>> sympify("x***2") - Traceback (most recent call last): - ... - SympifyError: SympifyError: "could not parse u'x***2'" - - Locals - ------ - - The sympification happens with access to everything that is loaded - by ``from sympy import *``; anything used in a string that is not - defined by that import will be converted to a symbol. In the following, - the ``bitcout`` function is treated as a symbol and the ``O`` is - interpreted as the Order object (used with series) and it raises - an error when used improperly: - - >>> s = 'bitcount(42)' - >>> sympify(s) - bitcount(42) - >>> sympify("O(x)") - O(x) - >>> sympify("O + 1") - Traceback (most recent call last): - ... - TypeError: unbound method... - - In order to have ``bitcount`` be recognized it can be imported into a - namespace dictionary and passed as locals: - - >>> ns = {} - >>> exec('from sympy.core.evalf import bitcount', ns) - >>> sympify(s, locals=ns) - 6 - - In order to have the ``O`` interpreted as a Symbol, identify it as such - in the namespace dictionary. This can be done in a variety of ways; all - three of the following are possibilities: - - >>> from sympy import Symbol - >>> ns["O"] = Symbol("O") # method 1 - >>> exec('from sympy.abc import O', ns) # method 2 - >>> ns.update(dict(O=Symbol("O"))) # method 3 - >>> sympify("O + 1", locals=ns) - O + 1 - - If you want *all* single-letter and Greek-letter variables to be symbols - then you can use the clashing-symbols dictionaries that have been defined - there as private variables: _clash1 (single-letter variables), _clash2 - (the multi-letter Greek names) or _clash (both single and multi-letter - names that are defined in abc). - - >>> from sympy.abc import _clash1 - >>> _clash1 - {'C': C, 'E': E, 'I': I, 'N': N, 'O': O, 'Q': Q, 'S': S} - >>> sympify('C & Q', _clash1) - And(C, Q) - - Strict - ------ - - If the option ``strict`` is set to ``True``, only the types for which an - explicit conversion has been defined are converted. In the other - cases, a SympifyError is raised. - - >>> sympify(True) - True - >>> sympify(True, strict=True) - Traceback (most recent call last): - ... - SympifyError: SympifyError: True - - Extending - --------- - - To extend ``sympify`` to convert custom objects (not derived from ``Basic``), - just define a ``_sympy_`` method to your class. You can do that even to - classes that you do not own by subclassing or adding the method at runtime. - - >>> from sympy import Matrix - >>> class MyList1(object): - ... def __iter__(self): - ... yield 1 - ... yield 2 - ... raise StopIteration - ... def __getitem__(self, i): return list(self)[i] - ... def _sympy_(self): return Matrix(self) - >>> sympify(MyList1()) - [1] - [2] - - If you do not have control over the class definition you could also use the - ``converter`` global dictionary. The key is the class and the value is a - function that takes a single argument and returns the desired SymPy - object, e.g. ``converter[MyList] = lambda x: Matrix(x)``. - - >>> class MyList2(object): # XXX Do not do this if you control the class! - ... def __iter__(self): # Use _sympy_! - ... yield 1 - ... yield 2 - ... raise StopIteration - ... def __getitem__(self, i): return list(self)[i] - >>> from sympy.core.sympify import converter - >>> converter[MyList2] = lambda x: Matrix(x) - >>> sympify(MyList2()) - [1] - [2] - - """ - try: - cls = a.__class__ - except AttributeError: # a is probably an old-style class object - cls = type(a) - if cls in sympy_classes: - return a - if cls in (bool, type(None)): - if strict: - raise SympifyError(a) - else: - return a - - try: - return converter[cls](a) - except KeyError: - for superclass in getmro(cls): - try: - return converter[superclass](a) - except KeyError: - continue - - try: - return a._sympy_() - except AttributeError: - pass - - if not isinstance(a, str): - for coerce in (float, int): - try: - return sympify(coerce(a)) - except (TypeError, ValueError, AttributeError, SympifyError): - continue - - if strict: - raise SympifyError(a) - - if iterable(a): - try: - return type(a)([sympify(x, locals=locals, convert_xor=convert_xor, - rational=rational) for x in a]) - except TypeError: - # Not all iterables are rebuildable with their type. - pass - if isinstance(a, dict): - try: - return type(a)([sympify(x, locals=locals, convert_xor=convert_xor, - rational=rational) for x in a.items()]) - except TypeError: - # Not all iterables are rebuildable with their type. - pass - - # At this point we were given an arbitrary expression - # which does not inherit from Basic and doesn't implement - # _sympy_ (which is a canonical and robust way to convert - # anything to SymPy expression). - # - # As a last chance, we try to take "a"'s normal form via unicode() - # and try to parse it. If it fails, then we have no luck and - # return an exception - try: - a = str(a) - except Exception as exc: - raise SympifyError(a, exc) - - from sympy.parsing.sympy_parser import (parse_expr, TokenError, - standard_transformations) - from sympy.parsing.sympy_parser import convert_xor as t_convert_xor - from sympy.parsing.sympy_parser import rationalize as t_rationalize - - transformations = standard_transformations - - if rational: - transformations += (t_rationalize,) - if convert_xor: - transformations += (t_convert_xor,) - - try: - a = a.replace('\n', '') - expr = parse_expr(a, locals or {}, transformations) - except (TokenError, SyntaxError): - raise SympifyError('could not parse %r' % a) - - return expr - -
    -def _sympify(a): - """Short version of sympify for internal usage for __add__ and __eq__ - methods where it is ok to allow some things (like Python integers - and floats) in the expression. This excludes things (like strings) - that are unwise to allow into such an expression. - - >>> from sympy import Integer - >>> Integer(1) == 1 - True - - >>> Integer(1) == '1' - False - - >>> from sympy import Symbol - >>> from sympy.abc import x - >>> x + 1 - x + 1 - - >>> x + '1' - Traceback (most recent call last): - ... - TypeError: unsupported operand type(s) for +: 'Symbol' and 'str' - - see: sympify - """ - return sympify(a, strict=True) -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/diffgeom.html b/dev-py3k/_modules/sympy/diffgeom.html deleted file mode 100644 index f6c41cecde9..00000000000 --- a/dev-py3k/_modules/sympy/diffgeom.html +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - sympy.diffgeom — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.diffgeom

    -from .diffgeom import (
    -    BaseCovarDerivativeOp, BaseScalarField, BaseVectorField, Commutator,
    -    contravariant_order, CoordSystem, CovarDerivativeOp, covariant_order,
    -    Differential, intcurve_diffequ, intcurve_series, LieDerivative,
    -    Manifold, metric_to_Christoffel_1st, metric_to_Christoffel_2nd,
    -    metric_to_Ricci_components, metric_to_Riemann_components, Patch,
    -    Point, TensorProduct, twoform_to_matrix, vectors_in_basis,
    -    WedgeProduct,
    -)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/functions/combinatorial/factorials.html b/dev-py3k/_modules/sympy/functions/combinatorial/factorials.html deleted file mode 100644 index 1f8e6d8d49c..00000000000 --- a/dev-py3k/_modules/sympy/functions/combinatorial/factorials.html +++ /dev/null @@ -1,667 +0,0 @@ - - - - - - - - - - sympy.functions.combinatorial.factorials — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.functions.combinatorial.factorials

    -from sympy.core import S, C, sympify
    -from sympy.core.function import Function, ArgumentIndexError
    -from sympy.ntheory import sieve
    -from math import sqrt as _sqrt
    -
    -from sympy.core.compatibility import reduce, as_int
    -from sympy.core.cache import cacheit
    -from functools import reduce
    -
    -
    -
    -class CombinatorialFunction(Function):
    -    """Base class for combinatorial functions. """
    -
    -###############################################################################
    -######################## FACTORIAL and MULTI-FACTORIAL ########################
    -###############################################################################
    -
    -
    -
    [docs]class factorial(CombinatorialFunction): - """Implementation of factorial function over nonnegative integers. - For the sake of convenience and simplicity of procedures using - this function it is defined for negative integers and returns - zero in this case. - - The factorial is very important in combinatorics where it gives - the number of ways in which 'n' objects can be permuted. It also - arises in calculus, probability, number theory etc. - - There is strict relation of factorial with gamma function. In - fact n! = gamma(n+1) for nonnegative integers. Rewrite of this - kind is very useful in case of combinatorial simplification. - - Computation of the factorial is done using two algorithms. For - small arguments naive product is evaluated. However for bigger - input algorithm Prime-Swing is used. It is the fastest algorithm - known and computes n! via prime factorization of special class - of numbers, called here the 'Swing Numbers'. - - Examples - ======== - - >>> from sympy import Symbol, factorial - >>> n = Symbol('n', integer=True) - - >>> factorial(-2) - 0 - - >>> factorial(0) - 1 - - >>> factorial(7) - 5040 - - >>> factorial(n) - n! - - >>> factorial(2*n) - (2*n)! - - See Also - ======== - - factorial2, RisingFactorial, FallingFactorial - """ - - nargs = 1 - - def fdiff(self, argindex=1): - if argindex == 1: - return C.gamma(self.args[0] + 1)*C.polygamma(0, self.args[0] + 1) - else: - raise ArgumentIndexError(self, argindex) - - _small_swing = [ - 1, 1, 1, 3, 3, 15, 5, 35, 35, 315, 63, 693, 231, 3003, 429, 6435, 6435, 109395, - 12155, 230945, 46189, 969969, 88179, 2028117, 676039, 16900975, 1300075, - 35102025, 5014575, 145422675, 9694845, 300540195, 300540195 - ] - - @classmethod - def _swing(cls, n): - if n < 33: - return cls._small_swing[n] - else: - N, primes = int(_sqrt(n)), [] - - for prime in sieve.primerange(3, N + 1): - p, q = 1, n - - while True: - q //= prime - - if q > 0: - if q & 1 == 1: - p *= prime - else: - break - - if p > 1: - primes.append(p) - - for prime in sieve.primerange(N + 1, n//3 + 1): - if (n // prime) & 1 == 1: - primes.append(prime) - - L_product = R_product = 1 - - for prime in sieve.primerange(n//2 + 1, n + 1): - L_product *= prime - - for prime in primes: - R_product *= prime - - return L_product*R_product - - @classmethod - def _recursive(cls, n): - if n < 2: - return 1 - else: - return (cls._recursive(n//2)**2)*cls._swing(n) - - @classmethod - def eval(cls, n): - n = sympify(n) - - if n.is_Number: - if n is S.Zero: - return S.One - elif n.is_Integer: - if n.is_negative: - return S.Zero - else: - n, result = n.p, 1 - - if n < 20: - for i in range(2, n + 1): - result *= i - else: - N, bits = n, 0 - - while N != 0: - if N & 1 == 1: - bits += 1 - - N = N >> 1 - - result = cls._recursive(n)*2**(n - bits) - - return C.Integer(result) - - if n.is_negative: - return S.Zero - - def _eval_rewrite_as_gamma(self, n): - return C.gamma(n + 1) - - def _eval_is_integer(self): - return self.args[0].is_integer - -
    -
    [docs]class MultiFactorial(CombinatorialFunction): - pass - -
    -class subfactorial(CombinatorialFunction): - """The subfactorial counts the derangements of n items and is - defined for non-negative integers as:: - - , - | 1 for n = 0 - !n = { 0 for n = 1 - | (n - 1)*(!(n - 1) + !(n - 2)) for n > 1 - ` - - It can also be written as int(round(n!/exp(1))) but the recursive - definition with caching is implemented for this function. - - References - ========== - * http://en.wikipedia.org/wiki/Subfactorial - - Examples - ======== - - >>> from sympy import subfactorial - >>> from sympy.abc import n - >>> subfactorial(n + 1) - !(n + 1) - >>> subfactorial(5) - 44 - - See Also - ======== - factorial, sympy.utilities.iterables.generate_derangements - """ - nargs = 1 - - @classmethod - @cacheit - def _eval(self, n): - if not n: - return 1 - elif n == 1: - return 0 - return (n - 1)*(self._eval(n - 1) + self._eval(n - 2)) - - @classmethod - def eval(cls, arg): - try: - arg = as_int(arg) - if arg < 0: - raise ValueError - return C.Integer(cls._eval(arg)) - except ValueError: - if sympify(arg).is_Number: - raise ValueError("argument must be a nonnegative integer") - - def _sympystr(self, p): - if self.args[0].is_Atom: - return "!%s" % p.doprint(self.args[0]) - else: - return "!(%s)" % p.doprint(self.args[0]) - - -
    [docs]class factorial2(CombinatorialFunction): - """The double factorial n!!, not to be confused with (n!)! - - The double factorial is defined for integers >= -1 as:: - - , - | n*(n - 2)*(n - 4)* ... * 1 for n odd - n!! = { n*(n - 2)*(n - 4)* ... * 2 for n even - | 1 for n = 0, -1 - ` - - Examples - ======== - - >>> from sympy import factorial2, var - >>> var('n') - n - >>> factorial2(n + 1) - (n + 1)!! - >>> factorial2(5) - 15 - >>> factorial2(-1) - 1 - - See Also - ======== - - factorial, RisingFactorial, FallingFactorial - """ - nargs = 1 - - @classmethod - def eval(cls, arg): - if arg.is_Number: - if arg == S.Zero or arg == S.NegativeOne: - return S.One - return factorial2(arg - 2)*arg - - def _sympystr(self, p): - if self.args[0].is_Atom: - return "%s!!" % p.doprint(self.args[0]) - else: - return "(%s)!!" % p.doprint(self.args[0]) - -############################################################################### -######################## RISING and FALLING FACTORIALS ######################## -############################################################################### - -
    -
    [docs]class RisingFactorial(CombinatorialFunction): - """Rising factorial (also called Pochhammer symbol) is a double valued - function arising in concrete mathematics, hypergeometric functions - and series expansions. It is defined by: - - rf(x, k) = x * (x+1) * ... * (x + k-1) - - where 'x' can be arbitrary expression and 'k' is an integer. For - more information check "Concrete mathematics" by Graham, pp. 66 - or visit http://mathworld.wolfram.com/RisingFactorial.html page. - - Examples - ======== - - >>> from sympy import rf - >>> from sympy.abc import x - - >>> rf(x, 0) - 1 - - >>> rf(1, 5) - 120 - - >>> rf(x, 5) == x*(1 + x)*(2 + x)*(3 + x)*(4 + x) - True - - See Also - ======== - - factorial, factorial2, FallingFactorial - """ - - nargs = 2 - - @classmethod - def eval(cls, x, k): - x = sympify(x) - k = sympify(k) - - if x is S.NaN: - return S.NaN - elif x is S.One: - return factorial(k) - elif k.is_Integer: - if k is S.NaN: - return S.NaN - elif k is S.Zero: - return S.One - else: - if k.is_positive: - if x is S.Infinity: - return S.Infinity - elif x is S.NegativeInfinity: - if k.is_odd: - return S.NegativeInfinity - else: - return S.Infinity - else: - return reduce(lambda r, i: r*(x + i), range(0, int(k)), 1) - else: - if x is S.Infinity: - return S.Infinity - elif x is S.NegativeInfinity: - return S.Infinity - else: - return 1/reduce(lambda r, i: r*(x - i), range(1, abs(int(k)) + 1), 1) - - def _eval_rewrite_as_gamma(self, x, k): - return C.gamma(x + k) / C.gamma(x) - -
    -
    [docs]class FallingFactorial(CombinatorialFunction): - """Falling factorial (related to rising factorial) is a double valued - function arising in concrete mathematics, hypergeometric functions - and series expansions. It is defined by - - ff(x, k) = x * (x-1) * ... * (x - k+1) - - where 'x' can be arbitrary expression and 'k' is an integer. For - more information check "Concrete mathematics" by Graham, pp. 66 - or visit http://mathworld.wolfram.com/FallingFactorial.html page. - - >>> from sympy import ff - >>> from sympy.abc import x - - >>> ff(x, 0) - 1 - - >>> ff(5, 5) - 120 - - >>> ff(x, 5) == x*(x-1)*(x-2)*(x-3)*(x-4) - True - - See Also - ======== - - factorial, factorial2, RisingFactorial - """ - - nargs = 2 - - @classmethod - def eval(cls, x, k): - x = sympify(x) - k = sympify(k) - - if x is S.NaN: - return S.NaN - elif k.is_Integer: - if k is S.NaN: - return S.NaN - elif k is S.Zero: - return S.One - else: - if k.is_positive: - if x is S.Infinity: - return S.Infinity - elif x is S.NegativeInfinity: - if k.is_odd: - return S.NegativeInfinity - else: - return S.Infinity - else: - return reduce(lambda r, i: r*(x - i), range(0, int(k)), 1) - else: - if x is S.Infinity: - return S.Infinity - elif x is S.NegativeInfinity: - return S.Infinity - else: - return 1/reduce(lambda r, i: r*(x + i), range(1, abs(int(k)) + 1), 1) - - def _eval_rewrite_as_gamma(self, x, k): - return (-1)**k * C.gamma(-x + k) / C.gamma(-x) -
    -rf = RisingFactorial -ff = FallingFactorial - -############################################################################### -########################### BINOMIAL COEFFICIENTS ############################# -############################################################################### - - -
    [docs]class binomial(CombinatorialFunction): - """Implementation of the binomial coefficient. It can be defined - in two ways depending on its desired interpretation: - - C(n,k) = n!/(k!(n-k)!) or C(n, k) = ff(n, k)/k! - - First, in a strict combinatorial sense it defines the - number of ways we can choose 'k' elements from a set of - 'n' elements. In this case both arguments are nonnegative - integers and binomial is computed using an efficient - algorithm based on prime factorization. - - The other definition is generalization for arbitrary 'n', - however 'k' must also be nonnegative. This case is very - useful when evaluating summations. - - For the sake of convenience for negative 'k' this function - will return zero no matter what valued is the other argument. - - Examples - ======== - - >>> from sympy import Symbol, Rational, binomial - >>> n = Symbol('n', integer=True) - - >>> binomial(15, 8) - 6435 - - >>> binomial(n, -1) - 0 - - >>> [ binomial(0, i) for i in range(1)] - [1] - >>> [ binomial(1, i) for i in range(2)] - [1, 1] - >>> [ binomial(2, i) for i in range(3)] - [1, 2, 1] - >>> [ binomial(3, i) for i in range(4)] - [1, 3, 3, 1] - >>> [ binomial(4, i) for i in range(5)] - [1, 4, 6, 4, 1] - - >>> binomial(Rational(5,4), 3) - -5/128 - - >>> binomial(n, 3) - n*(n - 2)*(n - 1)/6 - - """ - - nargs = 2 - - def fdiff(self, argindex=1): - if argindex == 1: - # http://functions.wolfram.com/GammaBetaErf/Binomial/20/01/01/ - n, k = self.args - return binomial(n, k)*(C.polygamma(0, n + 1) - C.polygamma(0, n - k + 1)) - elif argindex == 2: - # http://functions.wolfram.com/GammaBetaErf/Binomial/20/01/02/ - n, k = self.args - return binomial(n, k)*(C.polygamma(0, n - k + 1) - C.polygamma(0, k + 1)) - else: - raise ArgumentIndexError(self, argindex) - - @classmethod - def eval(cls, n, k): - n, k = list(map(sympify, (n, k))) - - if k.is_Number: - if k.is_Integer: - if k < 0: - return S.Zero - elif k == 0 or n == k: - return S.One - elif n.is_Integer and n >= 0: - n, k = int(n), int(k) - - if k > n: - return S.Zero - elif k > n // 2: - k = n - k - - M, result = int(_sqrt(n)), 1 - - for prime in sieve.primerange(2, n + 1): - if prime > n - k: - result *= prime - elif prime > n // 2: - continue - elif prime > M: - if n % prime < k % prime: - result *= prime - else: - N, K = n, k - exp = a = 0 - - while N > 0: - a = int((N % prime) < (K % prime + a)) - N, K = N // prime, K // prime - exp = a + exp - - if exp > 0: - result *= prime**exp - - return C.Integer(result) - else: - result = n - k + 1 - - for i in range(2, k + 1): - result *= n - k + i - result /= i - - return result - elif k.is_negative: - return S.Zero - elif (n - k).simplify().is_negative: - return S.Zero - else: - d = n - k - - if d.is_Integer: - return cls.eval(n, d) - - def _eval_rewrite_as_factorial(self, n, k): - return C.factorial(n)/(C.factorial(k)*C.factorial(n - k)) - - def _eval_rewrite_as_gamma(self, n, k): - return C.gamma(n + 1)/(C.gamma(k + 1)*C.gamma(n - k + 1)) - - def _eval_is_integer(self): - return self.args[0].is_integer and self.args[1].is_integer
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/functions/combinatorial/numbers.html b/dev-py3k/_modules/sympy/functions/combinatorial/numbers.html deleted file mode 100644 index 359bf183df9..00000000000 --- a/dev-py3k/_modules/sympy/functions/combinatorial/numbers.html +++ /dev/null @@ -1,786 +0,0 @@ - - - - - - - - - - sympy.functions.combinatorial.numbers — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.functions.combinatorial.numbers

    -"""
    -This module implements some special functions that commonly appear in
    -combinatorial contexts (e.g. in power series); in particular,
    -sequences of rational numbers such as Bernoulli and Fibonacci numbers.
    -
    -Factorials, binomial coefficients and related functions are located in
    -the separate 'factorials' module.
    -"""
    -
    -from sympy import Function, S, Symbol, Rational, oo, Integer, C, Add, expand_mul
    -
    -from sympy.mpmath import bernfrac
    -from sympy.mpmath.libmp import ifib as _ifib
    -
    -
    -def _product(a, b):
    -    p = 1
    -    for k in range(a, b + 1):
    -        p *= k
    -    return p
    -
    -from sympy.utilities.memoization import recurrence_memo
    -
    -
    -# Dummy symbol used for computing polynomial sequences
    -_sym = Symbol('x')
    -_symbols = Function('x')
    -
    -
    -#----------------------------------------------------------------------------#
    -#                                                                            #
    -#                           Fibonacci numbers                                #
    -#                                                                            #
    -#----------------------------------------------------------------------------#
    -
    -
    [docs]class fibonacci(Function): - """ - Fibonacci numbers / Fibonacci polynomials - - The Fibonacci numbers are the integer sequence defined by the - initial terms F_0 = 0, F_1 = 1 and the two-term recurrence - relation F_n = F_{n-1} + F_{n-2}. - - The Fibonacci polynomials are defined by F_1(x) = 1, - F_2(x) = x, and F_n(x) = x*F_{n-1}(x) + F_{n-2}(x) for n > 2. - For all positive integers n, F_n(1) = F_n. - - * fibonacci(n) gives the nth Fibonacci number, F_n - * fibonacci(n, x) gives the nth Fibonacci polynomial in x, F_n(x) - - Examples - ======== - - >>> from sympy import fibonacci, Symbol - - >>> [fibonacci(x) for x in range(11)] - [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55] - >>> fibonacci(5, Symbol('t')) - t**4 + 3*t**2 + 1 - - References - ========== - - * http://en.wikipedia.org/wiki/Fibonacci_number - * http://mathworld.wolfram.com/FibonacciNumber.html - - See Also - ======== - - lucas - """ - - @staticmethod - def _fib(n): - return _ifib(n) - - @staticmethod - @recurrence_memo([None, S.One, _sym]) - def _fibpoly(n, prev): - return (prev[-2] + _sym*prev[-1]).expand() - - @classmethod - def eval(cls, n, sym=None): - if n.is_Integer: - n = int(n) - if n < 0: - return S.NegativeOne**(n + 1) * fibonacci(-n) - if sym is None: - return Integer(cls._fib(n)) - else: - if n < 1: - raise ValueError("Fibonacci polynomials are defined " - "only for positive integer indices.") - return cls._fibpoly(n).subs(_sym, sym) - -
    -
    [docs]class lucas(Function): - """ - Lucas numbers - - Lucas numbers satisfy a recurrence relation similar to that of - the Fibonacci sequence, in which each term is the sum of the - preceding two. They are generated by choosing the initial - values L_0 = 2 and L_1 = 1. - - * lucas(n) gives the nth Lucas number - - Examples - ======== - - >>> from sympy import lucas - - >>> [lucas(x) for x in range(11)] - [2, 1, 3, 4, 7, 11, 18, 29, 47, 76, 123] - - References - ========== - - * http://en.wikipedia.org/wiki/Lucas_number - - See Also - ======== - - fibonacci - """ - - @classmethod - def eval(cls, n): - if n.is_Integer: - return fibonacci(n + 1) + fibonacci(n - 1) - - -#----------------------------------------------------------------------------# -# # -# Bernoulli numbers # -# # -#----------------------------------------------------------------------------# -
    -
    [docs]class bernoulli(Function): - r""" - Bernoulli numbers / Bernoulli polynomials - - The Bernoulli numbers are a sequence of rational numbers - defined by B_0 = 1 and the recursive relation (n > 0):: - - n - ___ - \ / n + 1 \ - 0 = ) | | * B . - /___ \ k / k - k = 0 - - They are also commonly defined by their exponential generating - function, which is x/(exp(x) - 1). For odd indices > 1, the - Bernoulli numbers are zero. - - The Bernoulli polynomials satisfy the analogous formula:: - - n - ___ - \ / n \ n-k - B (x) = ) | | * B * x . - n /___ \ k / k - k = 0 - - Bernoulli numbers and Bernoulli polynomials are related as - B_n(0) = B_n. - - We compute Bernoulli numbers using Ramanujan's formula:: - - / n + 3 \ - B = (A(n) - S(n)) / | | - n \ n / - - where A(n) = (n+3)/3 when n = 0 or 2 (mod 6), A(n) = -(n+3)/6 - when n = 4 (mod 6), and:: - - [n/6] - ___ - \ / n + 3 \ - S(n) = ) | | * B - /___ \ n - 6*k / n-6*k - k = 1 - - This formula is similar to the sum given in the definition, but - cuts 2/3 of the terms. For Bernoulli polynomials, we use the - formula in the definition. - - * bernoulli(n) gives the nth Bernoulli number, B_n - * bernoulli(n, x) gives the nth Bernoulli polynomial in x, B_n(x) - - Examples - ======== - - >>> from sympy import bernoulli - - >>> [bernoulli(n) for n in range(11)] - [1, -1/2, 1/6, 0, -1/30, 0, 1/42, 0, -1/30, 0, 5/66] - >>> bernoulli(1000001) - 0 - - References - ========== - - * http://en.wikipedia.org/wiki/Bernoulli_number - * http://en.wikipedia.org/wiki/Bernoulli_polynomial - - See Also - ======== - - euler, bell - """ - - # Calculates B_n for positive even n - @staticmethod - def _calc_bernoulli(n): - s = 0 - a = int(C.binomial(n + 3, n - 6)) - for j in range(1, n//6 + 1): - s += a * bernoulli(n - 6*j) - # Avoid computing each binomial coefficient from scratch - a *= _product(n - 6 - 6*j + 1, n - 6*j) - a //= _product(6*j + 4, 6*j + 9) - if n % 6 == 4: - s = -Rational(n + 3, 6) - s - else: - s = Rational(n + 3, 3) - s - return s / C.binomial(n + 3, n) - - # We implement a specialized memoization scheme to handle each - # case modulo 6 separately - _cache = {0: S.One, 2: Rational(1, 6), 4: Rational(-1, 30)} - _highest = {0: 0, 2: 2, 4: 4} - - @classmethod - def eval(cls, n, sym=None): - if n.is_Number: - if n.is_Integer and n.is_nonnegative: - if n is S.Zero: - return S.One - elif n is S.One: - if sym is None: - return -S.Half - else: - return sym - S.Half - # Bernoulli numbers - elif sym is None: - if n.is_odd: - return S.Zero - n = int(n) - # Use mpmath for enormous Bernoulli numbers - if n > 500: - p, q = bernfrac(n) - return Rational(int(p), int(q)) - case = n % 6 - highest_cached = cls._highest[case] - if n <= highest_cached: - return cls._cache[n] - # To avoid excessive recursion when, say, bernoulli(1000) is - # requested, calculate and cache the entire sequence ... B_988, - # B_994, B_1000 in increasing order - for i in range(highest_cached + 6, n + 6, 6): - b = cls._calc_bernoulli(i) - cls._cache[i] = b - cls._highest[case] = i - return b - # Bernoulli polynomials - else: - n, result = int(n), [] - for k in range(n + 1): - result.append(C.binomial(n, k)*cls(k)*sym**(n - k)) - return Add(*result) - else: - raise ValueError("Bernoulli numbers are defined only" - " for nonnegative integer indices.") - - -#----------------------------------------------------------------------------# -# # -# Bell numbers # -# # -#----------------------------------------------------------------------------# -
    -
    [docs]class bell(Function): - r""" - Bell numbers / Bell polynomials - - The Bell numbers satisfy `B_0 = 1` and - - .. math:: B_n = \sum_{k=0}^{n-1} \binom{n-1}{k} B_k. - - They are also given by: - - .. math:: B_n = \frac{1}{e} \sum_{k=0}^{\infty} \frac{k^n}{k!}. - - The Bell polynomials are given by `B_0(x) = 1` and - - .. math:: B_n(x) = x \sum_{k=1}^{n-1} \binom{n-1}{k-1} B_{k-1}(x). - - The second kind of Bell polynomials (are sometimes called "partial" Bell - polynomials or incomplete Bell polynomials) are defined as - - .. math:: B_{n,k}(x_1, x_2,\dotsc x_{n-k+1}) = - \sum_{j_1+j_2+j_2+\dotsb=k \atop j_1+2j_2+3j_2+\dotsb=n} - \frac{n!}{j_1!j_2!\dotsb j_{n-k+1}!} - \left(\frac{x_1}{1!} \right)^{j_1} - \left(\frac{x_2}{2!} \right)^{j_2} \dotsb - \left(\frac{x_{n-k+1}}{(n-k+1)!} \right) ^{j_{n-k+1}}. - - * bell(n) gives the `n^{th}` Bell number, `B_n`. - * bell(n, x) gives the `n^{th}` Bell polynomial, `B_n(x)`. - * bell(n, k, (x1, x2, ...)) gives Bell polynomials of the second kind, - `B_{n,k}(x_1, x_2, \dotsc, x_{n-k+1})`. - - Notes - ===== - - Not to be confused with Bernoulli numbers and Bernoulli polynomials, - which use the same notation. - - Examples - ======== - - >>> from sympy import bell, Symbol, symbols - - >>> [bell(n) for n in range(11)] - [1, 1, 2, 5, 15, 52, 203, 877, 4140, 21147, 115975] - >>> bell(30) - 846749014511809332450147 - >>> bell(4, Symbol('t')) - t**4 + 6*t**3 + 7*t**2 + t - >>> bell(6, 2, symbols('x:6')[1:]) - 6*x1*x5 + 15*x2*x4 + 10*x3**2 - - References - ========== - - * http://en.wikipedia.org/wiki/Bell_number - * http://mathworld.wolfram.com/BellNumber.html - * http://mathworld.wolfram.com/BellPolynomial.html - - See Also - ======== - - euler, bernoulli - """ - - @staticmethod - @recurrence_memo([1, 1]) - def _bell(n, prev): - s = 1 - a = 1 - for k in range(1, n): - a = a * (n - k) // k - s += a * prev[k] - return s - - @staticmethod - @recurrence_memo([S.One, _sym]) - def _bell_poly(n, prev): - s = 1 - a = 1 - for k in range(2, n + 1): - a = a * (n - k + 1) // (k - 1) - s += a * prev[k - 1] - return expand_mul(_sym * s) - - @staticmethod - def _bell_incomplete_poly(n, k, symbols): - r""" - The second kind of Bell polynomials (incomplete Bell polynomials). - - Calculated by recurrence formula: - - .. math:: B_{n,k}(x_1, x_2, \dotsc, x_{n-k+1}) = - \sum_{m=1}^{n-k+1} - \x_m \binom{n-1}{m-1} B_{n-m,k-1}(x_1, x_2, \dotsc, x_{n-m-k}) - - where - B_{0,0} = 1; - B_{n,0} = 0; for n>=1 - B_{0,k} = 0; for k>=1 - - """ - if (n == 0) and (k == 0): - return S.One - elif (n == 0) or (k == 0): - return S.Zero - s = S.Zero - a = S.One - for m in range(1, n - k + 2): - s += a*bell._bell_incomplete_poly(n - m, k - 1, symbols)*symbols[m - 1] - a = a*(n - m)/m - return expand_mul(s) - - @classmethod - def eval(cls, n, k_sym=None, symbols=None): - if n.is_Integer and n.is_nonnegative: - if k_sym is None: - return Integer(cls._bell(int(n))) - elif symbols is None: - return cls._bell_poly(int(n)).subs(_sym, k_sym) - else: - r = cls._bell_incomplete_poly(int(n), int(k_sym), symbols) - return r - -#----------------------------------------------------------------------------# -# # -# Harmonic numbers # -# # -#----------------------------------------------------------------------------# - -
    -
    [docs]class harmonic(Function): - r""" - Harmonic numbers - - The nth harmonic number is given by 1 + 1/2 + 1/3 + ... + 1/n. - - More generally:: - - n - ___ - \ -m - H = ) k . - n,m /___ - k = 1 - - As n -> oo, H_{n,m} -> zeta(m) (the Riemann zeta function) - - * harmonic(n) gives the nth harmonic number, H_n - - * harmonic(n, m) gives the nth generalized harmonic number - of order m, H_{n,m}, where harmonic(n) == harmonic(n, 1) - - Examples - ======== - - >>> from sympy import harmonic, oo - - >>> [harmonic(n) for n in range(6)] - [0, 1, 3/2, 11/6, 25/12, 137/60] - >>> [harmonic(n, 2) for n in range(6)] - [0, 1, 5/4, 49/36, 205/144, 5269/3600] - >>> harmonic(oo, 2) - pi**2/6 - - References - ========== - - * http://en.wikipedia.org/wiki/Harmonic_number - - """ - - # Generate one memoized Harmonic number-generating function for each - # order and store it in a dictionary - _functions = {} - - nargs = (1, 2) - - @classmethod - def eval(cls, n, m=None): - if m is None: - m = S.One - if n == oo: - return C.zeta(m) - if n.is_Integer and n.is_nonnegative and m.is_Integer: - if n == 0: - return S.Zero - if not m in cls._functions: - @recurrence_memo([0]) - def f(n, prev): - return prev[-1] + S.One / n**m - cls._functions[m] = f - return cls._functions[m](int(n)) - -#----------------------------------------------------------------------------# -# # -# Euler numbers # -# # -#----------------------------------------------------------------------------# - -
    -
    [docs]class euler(Function): - r""" - Euler numbers - - The euler numbers are given by:: - - 2*n+1 k - ___ ___ j 2*n+1 - \ \ / k \ (-1) * (k-2*j) - E = I ) ) | | -------------------- - 2n /___ /___ \ j / k k - k = 1 j = 0 2 * I * k - - E = 0 - 2n+1 - - * euler(n) gives the n-th Euler number, E_n - - Examples - ======== - - >>> from sympy import Symbol, euler - >>> [euler(n) for n in range(10)] - [1, 0, -1, 0, 5, 0, -61, 0, 1385, 0] - >>> n = Symbol("n") - >>> euler(n+2*n) - euler(3*n) - - References - ========== - - * http://en.wikipedia.org/wiki/Euler_numbers - * http://mathworld.wolfram.com/EulerNumber.html - * http://en.wikipedia.org/wiki/Alternating_permutation - * http://mathworld.wolfram.com/AlternatingPermutation.html - - See Also - ======== - - bernoulli, bell - """ - - nargs = 1 - - @classmethod - def eval(cls, m, evaluate=True): - if not evaluate: - return - if m.is_odd: - return S.Zero - if m.is_Integer and m.is_nonnegative: - from sympy.mpmath import mp - m = m._to_mpmath(mp.prec) - res = mp.eulernum(m, exact=True) - return Integer(res) - - def _eval_rewrite_as_Sum(self, arg): - if arg.is_even: - k = C.Dummy("k", integer=True) - j = C.Dummy("j", integer=True) - n = self.args[0] / 2 - Em = (S.ImaginaryUnit * C.Sum( C.Sum( C.binomial(k, j) * ((-1)**j * (k - 2*j)**(2*n + 1)) / - (2**k*S.ImaginaryUnit**k * k), (j, 0, k)), (k, 1, 2*n + 1))) - - return Em - - def _eval_evalf(self, prec): - m = self.args[0] - - if m.is_Integer and m.is_nonnegative: - from sympy.mpmath import mp - from sympy import Expr - m = m._to_mpmath(prec) - oprec = mp.prec - mp.prec = prec - res = mp.eulernum(m) - mp.prec = oprec - return Expr._from_mpmath(res, prec) - -#----------------------------------------------------------------------------# -# # -# Catalan numbers # -# # -#----------------------------------------------------------------------------# - -
    -
    [docs]class catalan(Function): - r""" - Catalan numbers - - The n-th catalan number is given by:: - - 1 / 2*n \ - C = ----- | | - n n + 1 \ n / - - * catalan(n) gives the n-th Catalan number, C_n - - Examples - ======== - - >>> from sympy import (Symbol, binomial, gamma, hyper, polygamma, - ... catalan, diff, combsimp, Rational, I) - - >>> [ catalan(i) for i in range(1,10) ] - [1, 2, 5, 14, 42, 132, 429, 1430, 4862] - - >>> n = Symbol("n", integer=True) - - >>> catalan(n) - catalan(n) - - Catalan numbers can be transformed into several other, identical - expressions involving other mathematical functions - - >>> catalan(n).rewrite(binomial) - binomial(2*n, n)/(n + 1) - - >>> catalan(n).rewrite(gamma) - 4**n*gamma(n + 1/2)/(sqrt(pi)*gamma(n + 2)) - - >>> catalan(n).rewrite(hyper) - hyper((-n + 1, -n), (2,), 1) - - For some non-integer values of n we can get closed form - expressions by rewriting in terms of gamma functions: - - >>> catalan(Rational(1,2)).rewrite(gamma) - 8/(3*pi) - - We can differentiate the Catalan numbers C(n) interpreted as a - continuous real funtion in n: - - >>> diff(catalan(n), n) - (polygamma(0, n + 1/2) - polygamma(0, n + 2) + 2*log(2))*catalan(n) - - As a more advanced example consider the following ratio - between consecutive numbers: - - >>> combsimp((catalan(n + 1)/catalan(n)).rewrite(binomial)) - 2*(2*n + 1)/(n + 2) - - The Catalan numbers can be generalized to complex numbers: - - >>> catalan(I).rewrite(gamma) - 4**I*gamma(1/2 + I)/(sqrt(pi)*gamma(2 + I)) - - and evaluated with arbitrary precision: - - >>> catalan(I).evalf(20) - 0.39764993382373624267 - 0.020884341620842555705*I - - References - ========== - - * http://en.wikipedia.org/wiki/Catalan_number - * http://mathworld.wolfram.com/CatalanNumber.html - * http://geometer.org/mathcircles/catalan.pdf - - See Also - ======== - - sympy.functions.combinatorial.factorials.binomial - """ - - @classmethod - def eval(cls, n, evaluate=True): - if n.is_Integer and n.is_nonnegative: - return 4**n*C.gamma(n + S.Half)/(C.gamma(S.Half)*C.gamma(n + 2)) - - def fdiff(self, argindex=1): - n = self.args[0] - return catalan(n)*(C.polygamma(0, n + Rational(1, 2)) - C.polygamma(0, n + 2) + C.log(4)) - - def _eval_rewrite_as_binomial(self, n): - return C.binomial(2*n, n)/(n + 1) - - def _eval_rewrite_as_gamma(self, n): - # The gamma function allows to generalize Catalan numbers to complex n - return 4**n*C.gamma(n + S.Half)/(C.gamma(S.Half)*C.gamma(n + 2)) - - def _eval_rewrite_as_hyper(self, n): - return C.hyper([1 - n, -n], [2], 1) - - def _eval_evalf(self, prec): - return self.rewrite(C.gamma).evalf(prec)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/functions/elementary/complexes.html b/dev-py3k/_modules/sympy/functions/elementary/complexes.html deleted file mode 100644 index 7c22a8743c5..00000000000 --- a/dev-py3k/_modules/sympy/functions/elementary/complexes.html +++ /dev/null @@ -1,957 +0,0 @@ - - - - - - - - - - sympy.functions.elementary.complexes — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.functions.elementary.complexes

    -from sympy.core import S, C
    -from sympy.core.function import Function, Derivative, ArgumentIndexError
    -from sympy.functions.elementary.miscellaneous import sqrt
    -from sympy.functions.elementary.piecewise import Piecewise
    -from sympy.core import Add, Mul
    -from sympy.core.relational import Eq
    -
    -###############################################################################
    -######################### REAL and IMAGINARY PARTS ############################
    -###############################################################################
    -
    -
    -
    [docs]class re(Function): - """Returns real part of expression. This function performs only - elementary analysis and so it will fail to decompose properly - more complicated expressions. If completely simplified result - is needed then use Basic.as_real_imag() or perform complex - expansion on instance of this function. - - >>> from sympy import re, im, I, E - >>> from sympy.abc import x, y - - >>> re(2*E) - 2*E - - >>> re(2*I + 17) - 17 - - >>> re(2*I) - 0 - - >>> re(im(x) + x*I + 2) - 2 - - See Also - ======== - - im - """ - nargs = 1 - - is_real = True - unbranched = True # implicitely works on the projection to C - - @classmethod - def eval(cls, arg): - if arg is S.NaN: - return S.NaN - elif arg.is_real: - return arg - elif arg.is_imaginary: - return S.Zero - elif arg.is_Function and arg.func == conjugate: - return re(arg.args[0]) - else: - - included, reverted, excluded = [], [], [] - args = Add.make_args(arg) - for term in args: - coeff = term.as_coefficient(S.ImaginaryUnit) - - if coeff is not None: - if not coeff.is_real: - reverted.append(coeff) - elif not term.has(S.ImaginaryUnit) and term.is_real: - excluded.append(term) - else: - # Try to do some advanced expansion. If - # impossible, don't try to do re(arg) again - # (because this is what we are trying to do now). - real_imag = term.as_real_imag(ignore=arg) - if real_imag: - excluded.append(real_imag[0]) - else: - included.append(term) - - if len(args) != len(included): - a, b, c = [Add(*xs) for xs in [included, reverted, excluded]] - - return cls(a) - im(b) + c - -
    [docs] def as_real_imag(self, deep=True, **hints): - """ - Returns the real number with a zero complex part. - """ - return (self, S.Zero) -
    - def _eval_derivative(self, x): - if x.is_real or self.args[0].is_real: - return re(Derivative(self.args[0], x, **{'evaluate': True})) - if x.is_imaginary or self.args[0].is_imaginary: - return -S.ImaginaryUnit \ - * im(Derivative(self.args[0], x, **{'evaluate': True})) - -
    -class im(Function): - """ - Returns imaginary part of expression. This function performs only - elementary analysis and so it will fail to decompose properly more - complicated expressions. If completely simplified result is needed then - use Basic.as_real_imag() or perform complex expansion on instance of - this function. - - Examples - ======== - - >>> from sympy import re, im, E, I - >>> from sympy.abc import x, y - - >>> im(2*E) - 0 - - >>> re(2*I + 17) - 17 - - >>> im(x*I) - re(x) - - >>> im(re(x) + y) - im(y) - - See Also - ======== - - re - """ - - nargs = 1 - - is_real = True - unbranched = True # implicitely works on the projection to C - - @classmethod - def eval(cls, arg): - if arg is S.NaN: - return S.NaN - elif arg.is_real: - return S.Zero - elif arg.is_imaginary: - return -S.ImaginaryUnit * arg - elif arg.is_Function and arg.func == conjugate: - return -im(arg.args[0]) - else: - included, reverted, excluded = [], [], [] - args = Add.make_args(arg) - for term in args: - coeff = term.as_coefficient(S.ImaginaryUnit) - - if coeff is not None: - if not coeff.is_real: - reverted.append(coeff) - else: - excluded.append(coeff) - elif term.has(S.ImaginaryUnit) or not term.is_real: - # Try to do some advanced expansion. If - # impossible, don't try to do im(arg) again - # (because this is what we are trying to do now). - real_imag = term.as_real_imag(ignore=arg) - if real_imag: - excluded.append(real_imag[1]) - else: - included.append(term) - - if len(args) != len(included): - a, b, c = [Add(*xs) for xs in [included, reverted, excluded]] - - return cls(a) + re(b) + c - - def as_real_imag(self, deep=True, **hints): - """ - Return the imaginary part with a zero real part. - - Examples - ======== - - >>> from sympy.functions import im - >>> from sympy import I - >>> im(2 + 3*I).as_real_imag() - (3, 0) - """ - return (self, S.Zero) - - def _eval_derivative(self, x): - if x.is_real or self.args[0].is_real: - return im(Derivative(self.args[0], x, **{'evaluate': True})) - if x.is_imaginary or self.args[0].is_imaginary: - return -S.ImaginaryUnit \ - * re(Derivative(self.args[0], x, **{'evaluate': True})) - - -############################################################################### -############### SIGN, ABSOLUTE VALUE, ARGUMENT and CONJUGATION ################ -############################################################################### - -
    [docs]class sign(Function): - """ - Returns the sign of an expression, that is: - - * 1 if expression is positive - * 0 if expression is equal to zero - * -1 if expression is negative - - Examples - ======== - - >>> from sympy.functions import sign - >>> sign(-1) - -1 - >>> sign(0) - 0 - - See Also - ======== - - Abs, conjugate - """ - - nargs = 1 - - is_bounded = True - is_complex = True - - def doit(self): - if self.args[0].is_nonzero: - return self.args[0] / Abs(self.args[0]) - return self - - @classmethod - def eval(cls, arg): - if arg is S.NaN: - return S.NaN - if arg is S.Zero: - return S.Zero - if arg.is_positive: - return S.One - if arg.is_negative: - return S.NegativeOne - if arg.is_Function: - if arg.func is sign: - return arg - if arg.is_imaginary: - arg2 = -S.ImaginaryUnit * arg - if arg2.is_positive: - return S.ImaginaryUnit - if arg2.is_negative: - return -S.ImaginaryUnit - if arg.is_Mul: - c, args = arg.as_coeff_mul() - unk = [] - is_imag = c.is_imaginary - is_neg = c.is_negative - for ai in args: - ai2 = -S.ImaginaryUnit * ai - if ai.is_negative: - is_neg = not is_neg - elif ai.is_imaginary and ai2.is_positive: - is_imag = not is_imag - elif ai.is_negative is None or \ - (ai.is_imaginary is None or ai2.is_positive is None): - unk.append(ai) - if c is S.One and len(unk) == len(args): - return None - return (S.NegativeOne if is_neg else S.One) \ - * (S.ImaginaryUnit if is_imag else S.One) \ - * cls(arg._new_rawargs(*unk)) - - def _eval_Abs(self): - if self.args[0].is_nonzero: - return S.One - - def _eval_conjugate(self): - return sign(conjugate(self.args[0])) - - def _eval_derivative(self, x): - if self.args[0].is_real: - from sympy.functions.special.delta_functions import DiracDelta - return 2 * Derivative(self.args[0], x, **{'evaluate': True}) \ - * DiracDelta(self.args[0]) - elif self.args[0].is_imaginary: - from sympy.functions.special.delta_functions import DiracDelta - return 2 * Derivative(self.args[0], x, **{'evaluate': True}) \ - * DiracDelta(-S.ImaginaryUnit * self.args[0]) - - def _eval_is_imaginary(self): - return self.args[0].is_imaginary - - def _eval_is_integer(self): - return self.args[0].is_real - - def _eval_is_zero(self): - return self.args[0].is_zero - - def _eval_power(self, other): - if ( - self.args[0].is_real and - self.args[0].is_nonzero and - other.is_integer and - other.is_even - ): - return S.One - - def _sage_(self): - import sage.all as sage - return sage.sgn(self.args[0]._sage_()) - -
    -
    [docs]class Abs(Function): - """ - Return the absolute value of the argument. - - This is an extension of the built-in function abs() to accept symbolic - values. If you pass a SymPy expression to the built-in abs(), it will - pass it automatically to Abs(). - - Examples - ======== - - >>> from sympy import Abs, Symbol, S - >>> Abs(-1) - 1 - >>> x = Symbol('x', real=True) - >>> Abs(-x) - Abs(x) - >>> Abs(x**2) - x**2 - >>> abs(-x) # The Python built-in - Abs(x) - - Note that the Python built-in will return either an Expr or int depending on - the argument:: - - >>> type(abs(-1)) - <... 'int'> - >>> type(abs(S.NegativeOne)) - <class 'sympy.core.numbers.One'> - - Abs will always return a sympy object. - - See Also - ======== - - sign, conjugate - """ - - nargs = 1 - - is_real = True - is_negative = False - unbranched = True - -
    [docs] def fdiff(self, argindex=1): - """ - Get the first derivative of the argument to Abs(). - - Examples - ======== - - >>> from sympy.abc import x - >>> from sympy.functions import Abs - >>> Abs(-x).fdiff() - sign(x) - """ - if argindex == 1: - return sign(self.args[0]) - else: - raise ArgumentIndexError(self, argindex) -
    - @classmethod - def eval(cls, arg): - if hasattr(arg, '_eval_Abs'): - obj = arg._eval_Abs() - if obj is not None: - return obj - if arg.is_Mul: - known = [] - unk = [] - for t in arg.args: - tnew = cls(t) - if tnew.func is cls: - unk.append(tnew.args[0]) - else: - known.append(tnew) - known = Mul(*known) - unk = cls(Mul(*unk), evaluate=False) if unk else S.One - return known*unk - if arg is S.NaN: - return S.NaN - if arg.is_nonnegative: - return arg - if arg.is_nonpositive: - return -arg - if arg.is_imaginary: - arg2 = -S.ImaginaryUnit * arg - if arg2.is_nonnegative: - return arg2 - if arg.is_real is False and arg.is_imaginary is False: - from sympy import expand_mul - return sqrt( expand_mul(arg * arg.conjugate()) ) - if arg.is_Pow: - base, exponent = arg.as_base_exp() - if exponent.is_even and base.is_real: - return arg - - def _eval_is_nonzero(self): - return self._args[0].is_nonzero - - def _eval_is_positive(self): - return self.is_nonzero - - def _eval_power(self, other): - if self.args[0].is_real and other.is_integer: - if other.is_even: - return self.args[0]**other - elif other is not S.NegativeOne and other.is_Integer: - e = other - sign(other) - return self.args[0]**e*self - return - - def _eval_nseries(self, x, n, logx): - direction = self.args[0].leadterm(x)[0] - s = self.args[0]._eval_nseries(x, n=n, logx=logx) - when = Eq(direction, 0) - return Piecewise( - ((s.subs(direction, 0)), when), - (sign(direction)*s, True), - ) - - def _sage_(self): - import sage.all as sage - return sage.abs_symbolic(self.args[0]._sage_()) - - def _eval_derivative(self, x): - if self.args[0].is_real or self.args[0].is_imaginary: - return Derivative(self.args[0], x, **{'evaluate': True}) \ - * sign(conjugate(self.args[0])) - return (re(self.args[0]) * Derivative(re(self.args[0]), x, - **{'evaluate': True}) + im(self.args[0]) * Derivative(im(self.args[0]), - x, **{'evaluate': True})) / Abs(self.args[0]) - - def _eval_rewrite_as_Heaviside(self, arg): - # Note this only holds for real arg (since Heaviside is not defined - # for complex arguments). - if arg.is_real: - return arg*(C.Heaviside(arg) - C.Heaviside(-arg)) - else: - return self - -
    -
    [docs]class arg(Function): - """Returns the argument (in radians) of a complex number""" - - nargs = 1 - - is_real = True - is_bounded = True - - @classmethod - def eval(cls, arg): - x, y = re(arg), im(arg) - arg = C.atan2(y, x) - if arg.is_number: - return arg - - def _eval_derivative(self, t): - x, y = re(self.args[0]), im(self.args[0]) - return (x * Derivative(y, t, **{'evaluate': True}) - y * - Derivative(x, t, **{'evaluate': True})) / (x**2 + y**2) - -
    -
    [docs]class conjugate(Function): - """ - Changes the sign of the imaginary part of a complex number. - - Examples - ======== - - >>> from sympy import conjugate, I - - >>> conjugate(1 + I) - 1 - I - - See Also - ======== - - sign, Abs - """ - - nargs = 1 - - @classmethod - def eval(cls, arg): - obj = arg._eval_conjugate() - if obj is not None: - return obj - - def _eval_Abs(self): - return Abs(self.args[0], **{'evaluate': True}) - - def _eval_adjoint(self): - return transpose(self.args[0]) - - def _eval_conjugate(self): - return self.args[0] - - def _eval_derivative(self, x): - if x.is_real: - return conjugate(Derivative(self.args[0], x, **{'evaluate': True})) - elif x.is_imaginary: - return -conjugate(Derivative(self.args[0], x, **{'evaluate': True})) - - def _eval_transpose(self): - return adjoint(self.args[0]) - -
    -class transpose(Function): - """ - Linear map transposition. - """ - - nargs = 1 - - @classmethod - def eval(cls, arg): - obj = arg._eval_transpose() - if obj is not None: - return obj - - def _eval_adjoint(self): - return conjugate(self.args[0]) - - def _eval_conjugate(self): - return adjoint(self.args[0]) - - def _eval_transpose(self): - return self.args[0] - - -class adjoint(Function): - """ - Conjugate transpose or Hermite conjugation. - """ - - nargs = 1 - - @classmethod - def eval(cls, arg): - obj = arg._eval_adjoint() - if obj is not None: - return obj - obj = arg._eval_transpose() - if obj is not None: - return conjugate(obj) - - def _eval_adjoint(self): - return self.args[0] - - def _eval_conjugate(self): - return transpose(self.args[0]) - - def _eval_transpose(self): - return conjugate(self.args[0]) - - def _latex(self, printer, exp=None, *args): - arg = printer._print(self.args[0]) - tex = r'%s^{\dag}' % arg - if exp: - tex = r'\left(%s\right)^{%s}' % (tex, printer._print(exp)) - return tex - - def _pretty(self, printer, *args): - from sympy.printing.pretty.stringpict import prettyForm - pform = printer._print(self.args[0], *args) - if printer._use_unicode: - pform = pform**prettyForm('\u2020') - else: - pform = pform**prettyForm('+') - return pform - -############################################################################### -############### HANDLING OF POLAR NUMBERS ##################################### -############################################################################### - - -class polar_lift(Function): - """ - Lift argument to the riemann surface of the logarithm, using the - standard branch. - - >>> from sympy import Symbol, polar_lift, I - >>> p = Symbol('p', polar=True) - >>> x = Symbol('x') - >>> polar_lift(4) - 4*exp_polar(0) - >>> polar_lift(-4) - 4*exp_polar(I*pi) - >>> polar_lift(-I) - exp_polar(-I*pi/2) - >>> polar_lift(I + 2) - polar_lift(2 + I) - - >>> polar_lift(4*x) - 4*polar_lift(x) - >>> polar_lift(4*p) - 4*p - - See Also - ======== - - sympy.functions.elementary.exponential.exp_polar - periodic_argument - """ - - nargs = 1 - - is_polar = True - is_comparable = False # Cannot be evalf'd. - - @classmethod - def eval(cls, arg): - from sympy import exp_polar, pi, I, arg as argument - if arg.is_number: - ar = argument(arg) - #if not ar.has(argument) and not ar.has(atan): - if ar in (0, pi/2, -pi/2, pi): - return exp_polar(I*ar)*abs(arg) - - if arg.is_Mul: - args = arg.args - else: - args = [arg] - included = [] - excluded = [] - positive = [] - for arg in args: - if arg.is_polar: - included += [arg] - elif arg.is_positive: - positive += [arg] - else: - excluded += [arg] - if len(excluded) < len(args): - if excluded: - return Mul(*(included + positive))*polar_lift(Mul(*excluded)) - elif included: - return Mul(*(included + positive)) - else: - return Mul(*positive)*exp_polar(0) - - def _eval_evalf(self, prec): - """ Careful! any evalf of polar numbers is flaky """ - return self.args[0]._eval_evalf(prec) - - -class periodic_argument(Function): - """ - Represent the argument on a quotient of the riemann surface of the - logarithm. That is, given a period P, always return a value in - (-P/2, P/2], by using exp(P*I) == 1. - - >>> from sympy import exp, exp_polar, periodic_argument, unbranched_argument - >>> from sympy import I, pi - >>> unbranched_argument(exp(5*I*pi)) - pi - >>> unbranched_argument(exp_polar(5*I*pi)) - 5*pi - >>> periodic_argument(exp_polar(5*I*pi), 2*pi) - pi - >>> periodic_argument(exp_polar(5*I*pi), 3*pi) - -pi - >>> periodic_argument(exp_polar(5*I*pi), pi) - 0 - - See Also - ======== - - sympy.functions.elementary.exponential.exp_polar - polar_lift : Lift argument to the riemann surface of the logarithm - principal_branch - """ - - nargs = 2 - - @classmethod - def _getunbranched(cls, ar): - from sympy import exp_polar, log, polar_lift - if ar.is_Mul: - args = ar.args - else: - args = [ar] - unbranched = 0 - for a in args: - if not a.is_polar: - unbranched += arg(a) - elif a.func is exp_polar: - unbranched += a.exp.as_real_imag()[1] - elif a.is_Pow: - re, im = a.exp.as_real_imag() - unbranched += re*unbranched_argument( - a.base) + im*log(abs(a.base)) - elif a.func is polar_lift: - unbranched += arg(a.args[0]) - else: - return None - return unbranched - - @classmethod - def eval(cls, ar, period): - # Our strategy is to evaluate the argument on the riemann surface of the - # logarithm, and then reduce. - # NOTE evidently this means it is a rather bad idea to use this with - # period != 2*pi and non-polar numbers. - from sympy import ceiling, oo, atan2, atan, polar_lift, pi, Mul - if not period.is_positive: - return None - if period == oo and isinstance(ar, principal_branch): - return periodic_argument(*ar.args) - if ar.func is polar_lift and period >= 2*pi: - return periodic_argument(ar.args[0], period) - if ar.is_Mul: - newargs = [x for x in ar.args if not x.is_positive] - if len(newargs) != len(ar.args): - return periodic_argument(Mul(*newargs), period) - unbranched = cls._getunbranched(ar) - if unbranched is None: - return None - if unbranched.has(periodic_argument, atan2, arg, atan): - return None - if period == oo: - return unbranched - if period != oo: - n = ceiling(unbranched/period - S(1)/2)*period - if not n.has(ceiling): - return unbranched - n - - def _eval_evalf(self, prec): - from sympy import ceiling, oo - z, period = self.args - if period == oo: - unbranched = periodic_argument._getunbranched(z) - if unbranched is None: - return self - return unbranched._eval_evalf(prec) - ub = periodic_argument(z, oo)._eval_evalf(prec) - return (ub - ceiling(ub/period - S(1)/2)*period)._eval_evalf(prec) - - -def unbranched_argument(arg): - from sympy import oo - return periodic_argument(arg, oo) - - -class principal_branch(Function): - """ - Represent a polar number reduced to its principal branch on a quotient - of the riemann surface of the logarithm. - - This is a function of two arguments. The first argument is a polar - number `z`, and the second one a positive real number of infinity, `p`. - The result is "z mod exp_polar(I*p)". - - >>> from sympy import exp_polar, principal_branch, oo, I, pi - >>> from sympy.abc import z - >>> principal_branch(z, oo) - z - >>> principal_branch(exp_polar(2*pi*I)*3, 2*pi) - 3*exp_polar(0) - >>> principal_branch(exp_polar(2*pi*I)*3*z, 2*pi) - 3*principal_branch(z, 2*pi) - - See Also - ======== - - sympy.functions.elementary.exponential.exp_polar - polar_lift : Lift argument to the riemann surface of the logarithm - periodic_argument - """ - - nargs = 2 - is_polar = True - is_comparable = False # cannot always be evalf'd - - @classmethod - def eval(self, x, period): - from sympy import oo, exp_polar, I, Mul, polar_lift, Symbol - if isinstance(x, polar_lift): - return principal_branch(x.args[0], period) - if period == oo: - return x - ub = periodic_argument(x, oo) - barg = periodic_argument(x, period) - if ub != barg and not ub.has(periodic_argument) \ - and not barg.has(periodic_argument): - pl = polar_lift(x) - - def mr(expr): - if not isinstance(expr, Symbol): - return polar_lift(expr) - return expr - pl = pl.replace(polar_lift, mr) - if not pl.has(polar_lift): - res = exp_polar(I*(barg - ub))*pl - if not res.is_polar and not res.has(exp_polar): - res *= exp_polar(0) - return res - - if not x.free_symbols: - c, m = x, () - else: - c, m = x.as_coeff_mul(*x.free_symbols) - others = [] - for y in m: - if y.is_positive: - c *= y - else: - others += [y] - m = tuple(others) - arg = periodic_argument(c, period) - if arg.has(periodic_argument): - return None - if arg.is_number and (unbranched_argument(c) != arg or - (arg == 0 and m != () and c != 1)): - if arg == 0: - return abs(c)*principal_branch(Mul(*m), period) - return principal_branch(exp_polar(I*arg)*Mul(*m), period)*abs(c) - if arg.is_number and ((abs(arg) < period/2) is True or arg == period/2) \ - and m == (): - return exp_polar(arg*I)*abs(c) - - def _eval_evalf(self, prec): - from sympy import exp, pi, I - z, period = self.args - p = periodic_argument(z, period)._eval_evalf(prec) - if abs(p) > pi or p == -pi: - return self # Cannot evalf for this argument. - return (abs(z)*exp(I*p))._eval_evalf(prec) - -# /cyclic/ -from sympy.core import basic as _ -_.abs_ = Abs -del _ -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/functions/elementary/exponential.html b/dev-py3k/_modules/sympy/functions/elementary/exponential.html deleted file mode 100644 index 46f55603eb8..00000000000 --- a/dev-py3k/_modules/sympy/functions/elementary/exponential.html +++ /dev/null @@ -1,866 +0,0 @@ - - - - - - - - - - sympy.functions.elementary.exponential — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.functions.elementary.exponential

    -from sympy.core import C, sympify
    -from sympy.core.add import Add
    -from sympy.core.function import Lambda, Function, ArgumentIndexError
    -from sympy.core.cache import cacheit
    -from sympy.core.singleton import S
    -from sympy.core.symbol import Wild, Dummy
    -from sympy.core.mul import Mul
    -
    -from sympy.functions.elementary.miscellaneous import sqrt
    -from sympy.ntheory import multiplicity, perfect_power
    -
    -# NOTE IMPORTANT
    -# The series expansion code in this file is an important part of the gruntz
    -# algorithm for determining limits. _eval_nseries has to return a generalized
    -# power series with coefficients in C(log(x), log).
    -# In more detail, the result of _eval_nseries(self, x, n) must be
    -#   c_0*x**e_0 + ... (finitely many terms)
    -# where e_i are numbers (not necessarily integers) and c_i involve only
    -# numbers, the function log, and log(x). [This also means it must not contain
    -# log(x(1+p)), this *has* to be expanded to log(x)+log(1+p) if x.is_positive and
    -# p.is_positive.]
    -
    -
    -class ExpBase(Function):
    -
    -    nargs = 1
    -    unbranched = True
    -
    -    def inverse(self, argindex=1):
    -        """
    -        Returns the inverse function of ``exp(x)``.
    -        """
    -        return log
    -
    -    def as_numer_denom(self):
    -        """
    -        Returns this with a positive exponent as a 2-tuple (a fraction).
    -
    -        Examples
    -        ========
    -
    -        >>> from sympy.functions import exp
    -        >>> from sympy.abc import x
    -        >>> exp(-x).as_numer_denom()
    -        (1, exp(x))
    -        >>> exp(x).as_numer_denom()
    -        (exp(x), 1)
    -        """
    -        # this should be the same as Pow.as_numer_denom wrt
    -        # exponent handling
    -        exp = self.exp
    -        neg_exp = exp.is_negative
    -        if not neg_exp and not exp.is_real:
    -            neg_exp = _coeff_isneg(exp)
    -        if neg_exp:
    -            return S.One, self.func(-exp)
    -        return self, S.One
    -
    -    @property
    -    def exp(self):
    -        """
    -        Returns the exponent of the function.
    -        """
    -        return self.args[0]
    -
    -    def as_base_exp(self):
    -        """
    -        Returns the 2-tuple (base, exponent).
    -        """
    -        return self.func(1), Mul(*self.args)
    -
    -    def _eval_conjugate(self):
    -        return self.func(self.args[0].conjugate())
    -
    -    def _eval_is_bounded(self):
    -        arg = self.args[0]
    -        if arg.is_unbounded:
    -            if arg.is_negative:
    -                return True
    -            if arg.is_positive:
    -                return False
    -        if arg.is_bounded:
    -            return True
    -
    -    def _eval_is_rational(self):
    -        s = self.func(*self.args)
    -        if s.func == self.func:
    -            if s.args[0].is_rational:
    -                return False
    -        else:
    -            return s.is_rational
    -
    -    def _eval_is_zero(self):
    -        return (self.args[0] is S.NegativeInfinity)
    -
    -    def _eval_power(b, e):
    -        """exp(arg)**e -> exp(arg*e) if assumptions allow it.
    -        """
    -        f = b.func
    -        be = b.exp
    -        rv = f(be*e)
    -        if e.is_integer:
    -            return rv
    -        if be.is_real:
    -            return rv
    -        # "is True" needed below; exp.is_polar returns <property object ...>
    -        if f.is_polar is True:
    -            return rv
    -        if e.is_polar:
    -            return rv
    -        if be.is_polar:
    -            return rv
    -        besmall = abs(be) <= S.Pi
    -        if besmall:
    -            return rv
    -        elif besmall is False and e.is_Rational and e.q == 2:
    -            return -rv
    -
    -    def _eval_expand_power_exp(self, **hints):
    -        arg = self.args[0]
    -        if arg.is_Add and arg.is_commutative:
    -            expr = 1
    -            for x in arg.args:
    -                expr *= self.func(x)
    -            return expr
    -        return self.func(arg)
    -
    -
    -class exp_polar(ExpBase):
    -    r"""
    -    Represent a 'polar number' (see g-function Sphinx documentation).
    -
    -    ``exp_polar`` represents the function
    -    `Exp: \mathbb{C} \rightarrow \mathcal{S}`, sending the complex number
    -    `z = a + bi` to the polar number `r = exp(a), \theta = b`. It is one of
    -    the main functions to construct polar numbers.
    -
    -    >>> from sympy import exp_polar, pi, I, exp
    -
    -    The main difference is that polar numbers don't "wrap around" at `2 \pi`:
    -
    -    >>> exp(2*pi*I)
    -    1
    -    >>> exp_polar(2*pi*I)
    -    exp_polar(2*I*pi)
    -
    -    apart from that they behave mostly like classical complex numbers:
    -
    -    >>> exp_polar(2)*exp_polar(3)
    -    exp_polar(5)
    -
    -    See also
    -    ========
    -
    -    sympy.simplify.simplify.powsimp
    -    sympy.functions.elementary.complexes.polar_lift
    -    sympy.functions.elementary.complexes.periodic_argument
    -    sympy.functions.elementary.complexes.principal_branch
    -    """
    -
    -    is_polar = True
    -    is_comparable = False  # cannot be evalf'd
    -
    -    def _eval_Abs(self):
    -        from sympy import expand_mul
    -        return sqrt( expand_mul(self * self.conjugate()) )
    -
    -    def _eval_evalf(self, prec):
    -        """ Careful! any evalf of polar numbers is flaky """
    -        from sympy import im, pi, re
    -        i = im(self.args[0])
    -        if i <= -pi or i > pi:
    -            return self  # cannot evalf for this argument
    -        res = exp(self.args[0])._eval_evalf(prec)
    -        if i > 0 and im(res) < 0:
    -            # i ~ pi, but exp(I*i) evaluated to argument slightly bigger than pi
    -            return re(res)
    -        return res
    -
    -    def _eval_is_real(self):
    -        if self.args[0].is_real:
    -            return True
    -
    -    def as_base_exp(self):
    -        # XXX exp_polar(0) is special!
    -        if self.args[0] == 0:
    -            return self, S(1)
    -        return ExpBase.as_base_exp(self)
    -
    -
    -
    [docs]class exp(ExpBase): - """ - The exponential function, :math:`e^x`. - - See Also - ======== - - log - """ - -
    [docs] def fdiff(self, argindex=1): - """ - Returns the first derivative of this function. - """ - if argindex == 1: - return self - else: - raise ArgumentIndexError(self, argindex) -
    - @classmethod - def eval(cls, arg): - if arg.is_Number: - if arg is S.NaN: - return S.NaN - elif arg is S.Zero: - return S.One - elif arg is S.One: - return S.Exp1 - elif arg is S.Infinity: - return S.Infinity - elif arg is S.NegativeInfinity: - return S.Zero - elif arg.func is log: - return arg.args[0] - elif arg.is_Mul: - Ioo = S.ImaginaryUnit*S.Infinity - if arg in [Ioo, -Ioo]: - return S.NaN - - coeff = arg.coeff(S.Pi*S.ImaginaryUnit) - if coeff: - if (2*coeff).is_integer: - if coeff.is_even: - return S.One - elif coeff.is_odd: - return S.NegativeOne - elif (coeff + S.Half).is_even: - return -S.ImaginaryUnit - elif (coeff + S.Half).is_odd: - return S.ImaginaryUnit - - # Warning: code in risch.py will be very sensitive to changes - # in this (see DifferentialExtension). - - # look for a single log factor - - coeff, terms = arg.as_coeff_Mul() - - # but it can't be multiplied by oo - if coeff in [S.NegativeInfinity, S.Infinity]: - return None - - coeffs, log_term = [coeff], None - for term in Mul.make_args(terms): - if term.func is log: - if log_term is None: - log_term = term.args[0] - else: - return None - elif term.is_comparable: - coeffs.append(term) - else: - return None - - return log_term**Mul(*coeffs) if log_term else None - - elif arg.is_Add: - out = [] - add = [] - for a in arg.args: - if a is S.One: - add.append(a) - continue - newa = cls(a) - if newa.func is cls: - add.append(a) - else: - out.append(newa) - if out: - return Mul(*out)*cls(Add(*add), evaluate=False) - - @property -
    [docs] def base(self): - """ - Returns the base of the exponential function. - """ - return S.Exp1 -
    - @staticmethod - @cacheit -
    [docs] def taylor_term(n, x, *previous_terms): - """ - Calculates the next term in the Taylor series expansion. - """ - if n < 0: - return S.Zero - if n == 0: - return S.One - x = sympify(x) - if previous_terms: - p = previous_terms[-1] - if p is not None: - return p * x / n - return x**n/C.factorial()(n) -
    -
    [docs] def as_real_imag(self, deep=True, **hints): - """ - Returns this function as a 2-tuple representing a complex number. - - Examples - ======== - - >>> from sympy import I - >>> from sympy.abc import x - >>> from sympy.functions import exp - >>> exp(x).as_real_imag() - (exp(re(x))*cos(im(x)), exp(re(x))*sin(im(x))) - >>> exp(1).as_real_imag() - (E, 0) - >>> exp(I).as_real_imag() - (cos(1), sin(1)) - >>> exp(1+I).as_real_imag() - (E*cos(1), E*sin(1)) - - See Also - ======== - - sympy.functions.elementary.complexes.re - sympy.functions.elementary.complexes.im - """ - re, im = self.args[0].as_real_imag() - if deep: - re = re.expand(deep, **hints) - im = im.expand(deep, **hints) - cos, sin = C.cos(im), C.sin(im) - return (exp(re)*cos, exp(re)*sin) -
    - def _eval_subs(self, old, new): - arg = self.args[0] - o = old - if old.is_Pow: # handle (exp(3*log(x))).subs(x**2, z) -> z**(3/2) - o = exp(o.exp*log(o.base)) - if o.func is exp: - # exp(a*expr) .subs( exp(b*expr), y ) -> y ** (a/b) - a, expr_terms = self.args[0].as_independent( - C.Symbol, as_Add=False) - b, expr_terms_ = o.args[0].as_independent( - C.Symbol, as_Add=False) - - if expr_terms == expr_terms_: - return new**(a/b) - - if arg.is_Add: # exp(2*x+a).subs(exp(3*x),y) -> y**(2/3) * exp(a) - # exp(exp(x) + exp(x**2)).subs(exp(exp(x)), w) -> w * exp(exp(x**2)) - oarg = o.args[0] - new_l = [] - o_al = [] - coeff2, terms2 = oarg.as_coeff_mul() - for a in arg.args: - a = a._subs(o, new) - coeff1, terms1 = a.as_coeff_mul() - if terms1 == terms2: - new_l.append(new**(coeff1/coeff2)) - else: - o_al.append(a._subs(o, new)) - if new_l: - new_l.append(self.func(Add(*o_al))) - r = Mul(*new_l) - return r - if o is S.Exp1: - # treat this however Pow is being treated - u = C.Dummy('u', positive=True) - return (u**self.args[0]).xreplace({u: new}) - - return Function._eval_subs(self, o, new) - - def _eval_is_real(self): - if self.args[0].is_real: - return True - elif self.args[0].is_imaginary: - arg2 = -S(2) * S.ImaginaryUnit * self.args[0] / S.Pi - return arg2.is_even - - def _eval_is_positive(self): - if self.args[0].is_real: - return not self.args[0] is S.NegativeInfinity - elif self.args[0].is_imaginary: - arg2 = -S.ImaginaryUnit * self.args[0] / S.Pi - return arg2.is_even - - def _eval_lseries(self, x): - s = self.args[0] - yield exp(s.subs(x, 0)) - from sympy import integrate - t = Dummy("t") - f = s.subs(x, t) - for term in (exp(f)*f.diff(t)).lseries(t): - yield integrate(term, (t, 0, x)) - - def _eval_nseries(self, x, n, logx): - # NOTE Please see the comment at the beginning of this file, labelled - # IMPORTANT. - from sympy import limit, oo, powsimp - arg = self.args[0] - arg_series = arg._eval_nseries(x, n=n, logx=logx) - if arg_series.is_Order: - return 1 + arg_series - arg0 = limit(arg_series.removeO(), x, 0) - if arg0 in [-oo, oo]: - return self - t = Dummy("t") - exp_series = exp(t)._taylor(t, n) - o = exp_series.getO() - exp_series = exp_series.removeO() - r = exp(arg0)*exp_series.subs(t, arg_series - arg0) - r += C.Order(o.expr.subs(t, (arg_series - arg0)), x) - r = r.expand() - return powsimp(r, deep=True, combine='exp') - - def _taylor(self, x, n): - l = [] - g = None - for i in range(n): - g = self.taylor_term(i, self.args[0], g) - g = g.nseries(x, n=n) - l.append(g) - return Add(*l) + C.Order(x**n, x) - - def _eval_as_leading_term(self, x): - arg = self.args[0] - if arg.is_Add: - return Mul(*[exp(f).as_leading_term(x) for f in arg.args]) - arg = self.args[0].as_leading_term(x) - if C.Order(1, x).contains(arg): - return S.One - return exp(arg) - - def _eval_rewrite_as_sin(self, arg): - I = S.ImaginaryUnit - return C.sin(I*arg + S.Pi/2) - I*C.sin(I*arg) - - def _eval_rewrite_as_cos(self, arg): - I = S.ImaginaryUnit - return C.cos(I*arg) + I*C.cos(I*arg + S.Pi/2) - - def _sage_(self): - import sage.all as sage - return sage.exp(self.args[0]._sage_()) - -
    -
    [docs]class log(Function): - """ - The logarithmic function :math:`ln(x)` or :math:`log(x)`. - - See Also - ======== - - exp - """ - - nargs = (1, 2) - -
    [docs] def fdiff(self, argindex=1): - """ - Returns the first derivative of the function. - """ - if argindex == 1: - return 1/self.args[0] - s = C.Dummy('x') - return Lambda(s**(-1), s) - else: - raise ArgumentIndexError(self, argindex) -
    -
    [docs] def inverse(self, argindex=1): - """ - Returns the inverse function, log(x) (or ln(x)). - """ - return exp -
    - @classmethod - def eval(cls, arg, base=None): - from sympy import unpolarify - arg = sympify(arg) - - if base is not None: - base = sympify(base) - if base == 1: - if arg == 1: - return S.NaN - else: - return S.ComplexInfinity - try: - if not (base.is_positive and arg.is_positive): - raise ValueError - n = multiplicity(base, arg) - return n + log(arg // base ** n) / log(base) - except ValueError: - pass - if base is not S.Exp1: - return cls(arg)/cls(base) - else: - return cls(arg) - - if arg.is_Number: - if arg is S.Zero: - return S.ComplexInfinity - elif arg is S.One: - return S.Zero - elif arg is S.Infinity: - return S.Infinity - elif arg is S.NegativeInfinity: - return S.Infinity - elif arg is S.NaN: - return S.NaN - elif arg.is_negative: - return S.Pi * S.ImaginaryUnit + cls(-arg) - elif arg.is_Rational: - if arg.q != 1: - return cls(arg.p) - cls(arg.q) - # remove perfect powers automatically - p = perfect_power(int(arg)) - if p is not False: - return p[1]*cls(p[0]) - elif arg is S.ComplexInfinity: - return S.ComplexInfinity - elif arg is S.Exp1: - return S.One - elif arg.func is exp and arg.args[0].is_real: - return arg.args[0] - elif arg.func is exp_polar: - return unpolarify(arg.exp) - #don't autoexpand Pow or Mul (see the issue 252): - elif not arg.is_Add: - coeff = arg.as_coefficient(S.ImaginaryUnit) - - if coeff is not None: - if coeff is S.Infinity: - return S.Infinity - elif coeff is S.NegativeInfinity: - return S.Infinity - elif coeff.is_Rational: - if coeff.is_nonnegative: - return S.Pi * S.ImaginaryUnit * S.Half + cls(coeff) - else: - return -S.Pi * S.ImaginaryUnit * S.Half + cls(-coeff) - -
    [docs] def as_base_exp(self): - """ - Returns this function in the form (base, exponent). - """ - return self, S.One -
    - @staticmethod - @cacheit -
    [docs] def taylor_term(n, x, *previous_terms): # of log(1+x) - """ - Returns the next term in the Taylor series expansion of log(1+x). - """ - from sympy import powsimp - if n < 0: - return S.Zero - x = sympify(x) - if n == 0: - return x - if previous_terms: - p = previous_terms[-1] - if p is not None: - return powsimp((-n) * p * x / (n + 1), deep=True, combine='exp') - return (1 - 2*(n % 2)) * x**(n + 1)/(n + 1) -
    - def _eval_expand_log(self, deep=True, **hints): - from sympy import unpolarify - force = hints.get('force', False) - arg = self.args[0] - if arg.is_Mul: - expr = [] - nonpos = [] - for x in arg.args: - if force or x.is_positive or x.is_polar: - a = self.func(x) - if isinstance(a, log): - expr.append(self.func(x)._eval_expand_log(**hints)) - else: - expr.append(a) - else: - nonpos.append(x) - return Add(*expr) + log(Mul(*nonpos)) - elif arg.is_Pow: - if force or (arg.exp.is_real and arg.base.is_positive) or \ - arg.base.is_polar: - b = arg.base - e = arg.exp - a = self.func(b) - if isinstance(a, log): - return unpolarify(e) * a._eval_expand_log(**hints) - else: - return unpolarify(e) * a - - return self.func(arg) - -
    [docs] def as_real_imag(self, deep=True, **hints): - """ - Returns this function as a complex coordinate. - - Examples - ======== - - >>> from sympy import I - >>> from sympy.abc import x - >>> from sympy.functions import log - >>> log(x).as_real_imag() - (log(Abs(x)), arg(x)) - >>> log(I).as_real_imag() - (0, pi/2) - >>> log(1+I).as_real_imag() - (log(sqrt(2)), pi/4) - >>> log(I*x).as_real_imag() - (log(Abs(x)), arg(I*x)) - - """ - if deep: - abs = C.Abs(self.args[0].expand(deep, **hints)) - arg = C.arg(self.args[0].expand(deep, **hints)) - else: - abs = C.Abs(self.args[0]) - arg = C.arg(self.args[0]) - if hints.get('log', False): # Expand the log - hints['complex'] = False - return (log(abs).expand(deep, **hints), arg) - else: - return (log(abs), arg) -
    - def _eval_is_rational(self): - s = self.func(*self.args) - if s.func == self.func: - if s.args[0].is_rational: - return False - else: - return s.is_rational - - def _eval_is_real(self): - return self.args[0].is_positive - - def _eval_is_bounded(self): - arg = self.args[0] - if arg.is_infinitesimal: - return False - return arg.is_bounded - - def _eval_is_positive(self): - arg = self.args[0] - if arg.is_positive: - if arg.is_unbounded: - return True - if arg.is_infinitesimal: - return False - if arg.is_Number: - return arg > 1 - - def _eval_is_zero(self): - # XXX This is not quite useless. Try evaluating log(0.5).is_negative - # without it. There's probably a nicer way though. - if self.args[0] is S.One: - return True - elif self.args[0].is_number: - return self.args[0].expand() is S.One - elif self.args[0].is_negative: - return False - - def _eval_nseries(self, x, n, logx): - # NOTE Please see the comment at the beginning of this file, labelled - # IMPORTANT. - from sympy import cancel - if not logx: - logx = log(x) - if self.args[0] == x: - return logx - arg = self.args[0] - k, l = Wild("k"), Wild("l") - r = arg.match(k*x**l) - if r is not None: - #k = r.get(r, S.One) - #l = r.get(l, S.Zero) - k, l = r[k], r[l] - if l != 0 and not l.has(x) and not k.has(x): - r = log(k) + l*logx # XXX true regardless of assumptions? - return r - - # TODO new and probably slow - s = self.args[0].nseries(x, n=n, logx=logx) - while s.is_Order: - n += 1 - s = self.args[0].nseries(x, n=n, logx=logx) - a, b = s.leadterm(x) - p = cancel(s/(a*x**b) - 1) - g = None - l = [] - for i in range(n + 2): - g = log.taylor_term(i, p, g) - g = g.nseries(x, n=n, logx=logx) - l.append(g) - return log(a) + b*logx + Add(*l) + C.Order(p**n, x) - - def _eval_as_leading_term(self, x): - arg = self.args[0].as_leading_term(x) - if arg is S.One: - return (self.args[0] - 1).as_leading_term(x) - return self.func(arg) - - def _sage_(self): - import sage.all as sage - return sage.log(self.args[0]._sage_()) - -
    -
    [docs]class LambertW(Function): - """Lambert W function, defined as the inverse function of - x*exp(x). This function represents the principal branch - of this inverse function, which like the natural logarithm - is multivalued. - - For more information, see: - http://en.wikipedia.org/wiki/Lambert_W_function - """ - nargs = 1 - - @classmethod - def eval(cls, x): - if x == S.Zero: - return S.Zero - if x == S.Exp1: - return S.One - if x == -1/S.Exp1: - return S.NegativeOne - if x == -log(2)/2: - return -log(2) - if x == S.Infinity: - return S.Infinity - -
    [docs] def fdiff(self, argindex=1): - """ - Return the first derivative of this function. - """ - if argindex == 1: - x = self.args[0] - return LambertW(x)/(x*(1 + LambertW(x))) - else: - raise ArgumentIndexError(self, argindex) -
    -from sympy.core.function import _coeff_isneg -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/functions/elementary/hyperbolic.html b/dev-py3k/_modules/sympy/functions/elementary/hyperbolic.html deleted file mode 100644 index 70dc1fcec70..00000000000 --- a/dev-py3k/_modules/sympy/functions/elementary/hyperbolic.html +++ /dev/null @@ -1,992 +0,0 @@ - - - - - - - - - - sympy.functions.elementary.hyperbolic — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.functions.elementary.hyperbolic

    -from sympy.core import S, C, sympify, cacheit
    -from sympy.core.function import Function, ArgumentIndexError, _coeff_isneg
    -
    -from sympy.functions.elementary.miscellaneous import sqrt
    -
    -###############################################################################
    -########################### HYPERBOLIC FUNCTIONS ##############################
    -###############################################################################
    -
    -
    -
    [docs]class HyperbolicFunction(Function): - """Base class for hyperbolic functions. """ - - unbranched = True - -
    -
    [docs]class sinh(HyperbolicFunction): - """ - The hyperbolic sine function, :math:`\\frac{exp(x) - exp(-x)}{2}`. - - * sinh(x) -> Returns the hyperbolic sine of x - - See Also - ======== - - cosh, tanh, asinh - """ - nargs = 1 - -
    [docs] def fdiff(self, argindex=1): - """ - Returns the first derivative of this function. - """ - if argindex == 1: - return cosh(self.args[0]) - else: - raise ArgumentIndexError(self, argindex) -
    -
    [docs] def inverse(self, argindex=1): - """ - Returns the inverse of this function. - """ - return asinh -
    - @classmethod - def eval(cls, arg): - arg = sympify(arg) - - if arg.is_Number: - if arg is S.NaN: - return S.NaN - elif arg is S.Infinity: - return S.Infinity - elif arg is S.NegativeInfinity: - return S.NegativeInfinity - elif arg is S.Zero: - return S.Zero - elif arg.is_negative: - return -cls(-arg) - else: - if arg is S.ComplexInfinity: - return S.NaN - - i_coeff = arg.as_coefficient(S.ImaginaryUnit) - - if i_coeff is not None: - return S.ImaginaryUnit * C.sin(i_coeff) - else: - if _coeff_isneg(arg): - return -cls(-arg) - - if arg.func == asinh: - return arg.args[0] - - if arg.func == acosh: - x = arg.args[0] - return sqrt(x - 1) * sqrt(x + 1) - - if arg.func == atanh: - x = arg.args[0] - return x/sqrt(1 - x**2) - - if arg.func == acoth: - x = arg.args[0] - return 1/(sqrt(x - 1) * sqrt(x + 1)) - - @staticmethod - @cacheit -
    [docs] def taylor_term(n, x, *previous_terms): - """ - Returns the next term in the Taylor series expansion. - """ - if n < 0 or n % 2 == 0: - return S.Zero - else: - x = sympify(x) - - if len(previous_terms) > 2: - p = previous_terms[-2] - return p * x**2 / (n*(n - 1)) - else: - return x**(n) / C.factorial(n) -
    - def _eval_conjugate(self): - return self.func(self.args[0].conjugate()) - -
    [docs] def as_real_imag(self, deep=True, **hints): - """ - Returns this function as a complex coordinate. - """ - if self.args[0].is_real: - if deep: - hints['complex'] = False - return (self.expand(deep, **hints), S.Zero) - else: - return (self, S.Zero) - if deep: - re, im = self.args[0].expand(deep, **hints).as_real_imag() - else: - re, im = self.args[0].as_real_imag() - return (sinh(re)*C.cos(im), cosh(re)*C.sin(im)) -
    - def _eval_rewrite_as_exp(self, arg): - return (C.exp(arg) - C.exp(-arg)) / 2 - - def _eval_rewrite_as_cosh(self, arg): - return -S.ImaginaryUnit*cosh(arg + S.Pi*S.ImaginaryUnit/2) - - def _eval_rewrite_as_tanh(self, arg): - tanh_half = tanh(S.Half*arg) - return 2*tanh_half/(1 - tanh_half**2) - - def _eval_rewrite_as_coth(self, arg): - coth_half = coth(S.Half*arg) - return 2*coth_half/(coth_half**2 - 1) - - def _eval_as_leading_term(self, x): - arg = self.args[0].as_leading_term(x) - - if x in arg.free_symbols and C.Order(1, x).contains(arg): - return S.One - else: - return self.func(arg) - - def _eval_is_real(self): - return self.args[0].is_real - - def _eval_is_bounded(self): - arg = self.args[0] - if arg.is_imaginary: - return True - - def _sage_(self): - import sage.all as sage - return sage.sinh(self.args[0]._sage_()) - -
    -
    [docs]class cosh(HyperbolicFunction): - """ - The hyperbolic cosine function, :math:`\\frac{exp(x) + exp(-x)}{2}`. - - * cosh(x) -> Returns the hyperbolic cosine of x - - See Also - ======== - - sinh, tanh, acosh - """ - nargs = 1 - - def fdiff(self, argindex=1): - if argindex == 1: - return sinh(self.args[0]) - else: - raise ArgumentIndexError(self, argindex) - -
    [docs] def inverse(self, argindex=1): - """ - Returns the inverse of this function. - """ - return acosh -
    - @classmethod - def eval(cls, arg): - arg = sympify(arg) - - if arg.is_Number: - if arg is S.NaN: - return S.NaN - elif arg is S.Infinity: - return S.Infinity - elif arg is S.NegativeInfinity: - return S.Infinity - elif arg is S.Zero: - return S.One - elif arg.is_negative: - return cls(-arg) - else: - if arg is S.ComplexInfinity: - return S.NaN - - i_coeff = arg.as_coefficient(S.ImaginaryUnit) - - if i_coeff is not None: - return C.cos(i_coeff) - else: - if _coeff_isneg(arg): - return cls(-arg) - - if arg.func == asinh: - return sqrt(1 + arg.args[0]**2) - - if arg.func == acosh: - return arg.args[0] - - if arg.func == atanh: - return 1/sqrt(1 - arg.args[0]**2) - - if arg.func == acoth: - x = arg.args[0] - return x/(sqrt(x - 1) * sqrt(x + 1)) - - @staticmethod - @cacheit - def taylor_term(n, x, *previous_terms): - if n < 0 or n % 2 == 1: - return S.Zero - else: - x = sympify(x) - - if len(previous_terms) > 2: - p = previous_terms[-2] - return p * x**2 / (n*(n - 1)) - else: - return x**(n)/C.factorial(n) - - def _eval_conjugate(self): - return self.func(self.args[0].conjugate()) - - def as_real_imag(self, deep=True, **hints): - if self.args[0].is_real: - if deep: - hints['complex'] = False - return (self.expand(deep, **hints), S.Zero) - else: - return (self, S.Zero) - if deep: - re, im = self.args[0].expand(deep, **hints).as_real_imag() - else: - re, im = self.args[0].as_real_imag() - - return (cosh(re)*C.cos(im), sinh(re)*C.sin(im)) - - def _eval_rewrite_as_exp(self, arg): - return (C.exp(arg) + C.exp(-arg)) / 2 - - def _eval_rewrite_as_sinh(self, arg): - return -S.ImaginaryUnit*sinh(arg + S.Pi*S.ImaginaryUnit/2) - - def _eval_rewrite_as_tanh(self, arg): - tanh_half = tanh(S.Half*arg)**2 - return (1 + tanh_half)/(1 - tanh_half) - - def _eval_rewrite_as_coth(self, arg): - coth_half = coth(S.Half*arg)**2 - return (coth_half + 1)/(coth_half - 1) - - def _eval_as_leading_term(self, x): - arg = self.args[0].as_leading_term(x) - - if x in arg.free_symbols and C.Order(1, x).contains(arg): - return S.One - else: - return self.func(arg) - - def _eval_is_real(self): - return self.args[0].is_real - - def _eval_is_bounded(self): - arg = self.args[0] - if arg.is_imaginary: - return True - - def _sage_(self): - import sage.all as sage - return sage.cosh(self.args[0]._sage_()) - -
    -
    [docs]class tanh(HyperbolicFunction): - """ - The hyperbolic tangent function, :math:`\\frac{sinh(x)}{cosh(x)}`. - - * tanh(x) -> Returns the hyperbolic tangent of x - - See Also - ======== - - sinh, cosh, atanh - """ - nargs = 1 - - def fdiff(self, argindex=1): - if argindex == 1: - return S.One - tanh(self.args[0])**2 - else: - raise ArgumentIndexError(self, argindex) - -
    [docs] def inverse(self, argindex=1): - """ - Returns the inverse of this function. - """ - return atanh -
    - @classmethod - def eval(cls, arg): - arg = sympify(arg) - - if arg.is_Number: - if arg is S.NaN: - return S.NaN - elif arg is S.Infinity: - return S.One - elif arg is S.NegativeInfinity: - return S.NegativeOne - elif arg is S.Zero: - return S.Zero - elif arg.is_negative: - return -cls(-arg) - else: - if arg is S.ComplexInfinity: - return S.NaN - - i_coeff = arg.as_coefficient(S.ImaginaryUnit) - - if i_coeff is not None: - if _coeff_isneg(i_coeff): - return -S.ImaginaryUnit * C.tan(-i_coeff) - return S.ImaginaryUnit * C.tan(i_coeff) - else: - if _coeff_isneg(arg): - return -cls(-arg) - - if arg.func == asinh: - x = arg.args[0] - return x/sqrt(1 + x**2) - - if arg.func == acosh: - x = arg.args[0] - return sqrt(x - 1) * sqrt(x + 1) / x - - if arg.func == atanh: - return arg.args[0] - - if arg.func == acoth: - return 1/arg.args[0] - - @staticmethod - @cacheit - def taylor_term(n, x, *previous_terms): - if n < 0 or n % 2 == 0: - return S.Zero - else: - x = sympify(x) - - a = 2**(n + 1) - - B = C.bernoulli(n + 1) - F = C.factorial(n + 1) - - return a*(a - 1) * B/F * x**n - - def _eval_conjugate(self): - return self.func(self.args[0].conjugate()) - - def as_real_imag(self, deep=True, **hints): - if self.args[0].is_real: - if deep: - hints['complex'] = False - return (self.expand(deep, **hints), S.Zero) - else: - return (self, S.Zero) - if deep: - re, im = self.args[0].expand(deep, **hints).as_real_imag() - else: - re, im = self.args[0].as_real_imag() - denom = sinh(re)**2 + C.cos(im)**2 - return (sinh(re)*cosh(re)/denom, C.sin(im)*C.cos(im)/denom) - - def _eval_rewrite_as_exp(self, arg): - neg_exp, pos_exp = C.exp(-arg), C.exp(arg) - return (pos_exp - neg_exp)/(pos_exp + neg_exp) - - def _eval_rewrite_as_sinh(self, arg): - return S.ImaginaryUnit*sinh(arg)/sinh(S.Pi*S.ImaginaryUnit/2 - arg) - - def _eval_rewrite_as_cosh(self, arg): - return S.ImaginaryUnit*cosh(S.Pi*S.ImaginaryUnit/2 - arg)/cosh(arg) - - def _eval_rewrite_as_coth(self, arg): - return 1/coth(arg) - - def _eval_as_leading_term(self, x): - arg = self.args[0].as_leading_term(x) - - if x in arg.free_symbols and C.Order(1, x).contains(arg): - return S.One - else: - return self.func(arg) - - def _eval_is_real(self): - return self.args[0].is_real - - def _eval_is_bounded(self): - arg = self.args[0] - if arg.is_real: - return True - - def _sage_(self): - import sage.all as sage - return sage.tanh(self.args[0]._sage_()) - -
    -
    [docs]class coth(HyperbolicFunction): - """ - The hyperbolic tangent function, :math:`\\frac{cosh(x)}{sinh(x)}`. - - * coth(x) -> Returns the hyperbolic cotangent of x - """ - nargs = 1 - - def fdiff(self, argindex=1): - if argindex == 1: - return -1/sinh(self.args[0])**2 - else: - raise ArgumentIndexError(self, argindex) - -
    [docs] def inverse(self, argindex=1): - """ - Returns the inverse of this function. - """ - return acoth -
    - @classmethod - def eval(cls, arg): - arg = sympify(arg) - - if arg.is_Number: - if arg is S.NaN: - return S.NaN - elif arg is S.Infinity: - return S.One - elif arg is S.NegativeInfinity: - return S.NegativeOne - elif arg is S.Zero: - return S.Zero - elif arg.is_negative: - return -cls(-arg) - else: - if arg is S.ComplexInfinity: - return S.NaN - - i_coeff = arg.as_coefficient(S.ImaginaryUnit) - - if i_coeff is not None: - if _coeff_isneg(i_coeff): - return S.ImaginaryUnit * C.cot(-i_coeff) - return -S.ImaginaryUnit * C.cot(i_coeff) - else: - if _coeff_isneg(arg): - return -cls(-arg) - - if arg.func == asinh: - x = arg.args[0] - return sqrt(1 + x**2)/x - - if arg.func == acosh: - x = arg.args[0] - return x/(sqrt(x - 1) * sqrt(x + 1)) - - if arg.func == atanh: - return 1/arg.args[0] - - if arg.func == acoth: - return arg.args[0] - - @staticmethod - @cacheit - def taylor_term(n, x, *previous_terms): - if n == 0: - return 1 / sympify(x) - elif n < 0 or n % 2 == 0: - return S.Zero - else: - x = sympify(x) - - B = C.bernoulli(n + 1) - F = C.factorial(n + 1) - - return 2**(n + 1) * B/F * x**n - - def _eval_conjugate(self): - return self.func(self.args[0].conjugate()) - - def as_real_imag(self, deep=True, **hints): - if self.args[0].is_real: - if deep: - hints['complex'] = False - return (self.expand(deep, **hints), S.Zero) - else: - return (self, S.Zero) - if deep: - re, im = self.args[0].expand(deep, **hints).as_real_imag() - else: - re, im = self.args[0].as_real_imag() - denom = sinh(re)**2 + C.sin(im)**2 - return (sinh(re)*cosh(re)/denom, -C.sin(im)*C.cos(im)/denom) - - def _eval_rewrite_as_exp(self, arg): - neg_exp, pos_exp = C.exp(-arg), C.exp(arg) - return (pos_exp + neg_exp)/(pos_exp - neg_exp) - - def _eval_rewrite_as_sinh(self, arg): - return -S.ImaginaryUnit*sinh(S.Pi*S.ImaginaryUnit/2 - arg)/sinh(arg) - - def _eval_rewrite_as_cosh(self, arg): - return -S.ImaginaryUnit*cosh(arg)/cosh(S.Pi*S.ImaginaryUnit/2 - arg) - - def _eval_rewrite_as_tanh(self, arg): - return 1/tanh(arg) - - def _eval_as_leading_term(self, x): - arg = self.args[0].as_leading_term(x) - - if x in arg.free_symbols and C.Order(1, x).contains(arg): - return S.One - else: - return self.func(arg) - - def _sage_(self): - import sage.all as sage - return sage.coth(self.args[0]._sage_()) - - -############################################################################### -############################# HYPERBOLIC INVERSES ############################# -############################################################################### -
    -
    [docs]class asinh(Function): - """ - The inverse hyperbolic sine function. - - * asinh(x) -> Returns the inverse hyperbolic sine of x - - See Also - ======== - - acosh, atanh, sinh - """ - nargs = 1 - - def fdiff(self, argindex=1): - if argindex == 1: - return 1/sqrt(self.args[0]**2 + 1) - else: - raise ArgumentIndexError(self, argindex) - - @classmethod - def eval(cls, arg): - arg = sympify(arg) - - if arg.is_Number: - if arg is S.NaN: - return S.NaN - elif arg is S.Infinity: - return S.Infinity - elif arg is S.NegativeInfinity: - return S.NegativeInfinity - elif arg is S.Zero: - return S.Zero - elif arg is S.One: - return C.log(sqrt(2) + 1) - elif arg is S.NegativeOne: - return C.log(sqrt(2) - 1) - elif arg.is_negative: - return -cls(-arg) - else: - if arg is S.ComplexInfinity: - return S.ComplexInfinity - - i_coeff = arg.as_coefficient(S.ImaginaryUnit) - - if i_coeff is not None: - return S.ImaginaryUnit * C.asin(i_coeff) - else: - if _coeff_isneg(arg): - return -cls(-arg) - - @staticmethod - @cacheit - def taylor_term(n, x, *previous_terms): - if n < 0 or n % 2 == 0: - return S.Zero - else: - x = sympify(x) - if len(previous_terms) >= 2 and n > 2: - p = previous_terms[-2] - return -p * (n - 2)**2/(n*(n - 1)) * x**2 - else: - k = (n - 1) // 2 - R = C.RisingFactorial(S.Half, k) - F = C.factorial(k) - return (-1)**k * R / F * x**n / n - - def _eval_as_leading_term(self, x): - arg = self.args[0].as_leading_term(x) - - if x in arg.free_symbols and C.Order(1, x).contains(arg): - return arg - else: - return self.func(arg) - - def _sage_(self): - import sage.all as sage - return sage.asinh(self.args[0]._sage_()) - -
    -
    [docs]class acosh(Function): - """ - The inverse hyperbolic cosine function. - - * acosh(x) -> Returns the inverse hyperbolic cosine of x - - See Also - ======== - - asinh, atanh, cosh - """ - nargs = 1 - - def fdiff(self, argindex=1): - if argindex == 1: - return 1/sqrt(self.args[0]**2 - 1) - else: - raise ArgumentIndexError(self, argindex) - - @classmethod - def eval(cls, arg): - arg = sympify(arg) - - if arg.is_Number: - if arg is S.NaN: - return S.NaN - elif arg is S.Infinity: - return S.Infinity - elif arg is S.NegativeInfinity: - return S.Infinity - elif arg is S.Zero: - return S.Pi*S.ImaginaryUnit / 2 - elif arg is S.One: - return S.Zero - elif arg is S.NegativeOne: - return S.Pi*S.ImaginaryUnit - - if arg.is_number: - cst_table = { - S.ImaginaryUnit: C.log(S.ImaginaryUnit*(1 + sqrt(2))), - -S.ImaginaryUnit: C.log(-S.ImaginaryUnit*(1 + sqrt(2))), - S.Half: S.Pi/3, - -S.Half: 2*S.Pi/3, - sqrt(2)/2: S.Pi/4, - -sqrt(2)/2: 3*S.Pi/4, - 1/sqrt(2): S.Pi/4, - -1/sqrt(2): 3*S.Pi/4, - sqrt(3)/2: S.Pi/6, - -sqrt(3)/2: 5*S.Pi/6, - (sqrt(3) - 1)/sqrt(2**3): 5*S.Pi/12, - -(sqrt(3) - 1)/sqrt(2**3): 7*S.Pi/12, - sqrt(2 + sqrt(2))/2: S.Pi/8, - -sqrt(2 + sqrt(2))/2: 7*S.Pi/8, - sqrt(2 - sqrt(2))/2: 3*S.Pi/8, - -sqrt(2 - sqrt(2))/2: 5*S.Pi/8, - (1 + sqrt(3))/(2*sqrt(2)): S.Pi/12, - -(1 + sqrt(3))/(2*sqrt(2)): 11*S.Pi/12, - (sqrt(5) + 1)/4: S.Pi/5, - -(sqrt(5) + 1)/4: 4*S.Pi/5 - } - - if arg in cst_table: - if arg.is_real: - return cst_table[arg]*S.ImaginaryUnit - return cst_table[arg] - - if arg is S.ComplexInfinity: - return S.Infinity - - i_coeff = arg.as_coefficient(S.ImaginaryUnit) - - if i_coeff is not None: - if _coeff_isneg(i_coeff): - return S.ImaginaryUnit * C.acos(i_coeff) - return S.ImaginaryUnit * C.acos(-i_coeff) - else: - if _coeff_isneg(arg): - return -cls(-arg) - - @staticmethod - @cacheit - def taylor_term(n, x, *previous_terms): - if n == 0: - return S.Pi*S.ImaginaryUnit / 2 - elif n < 0 or n % 2 == 0: - return S.Zero - else: - x = sympify(x) - if len(previous_terms) >= 2 and n > 2: - p = previous_terms[-2] - return p * (n - 2)**2/(n*(n - 1)) * x**2 - else: - k = (n - 1) // 2 - R = C.RisingFactorial(S.Half, k) - F = C.factorial(k) - return -R / F * S.ImaginaryUnit * x**n / n - - def _eval_as_leading_term(self, x): - arg = self.args[0].as_leading_term(x) - - if x in arg.free_symbols and C.Order(1, x).contains(arg): - return arg - else: - return self.func(arg) - - def _sage_(self): - import sage.all as sage - return sage.acosh(self.args[0]._sage_()) - -
    -
    [docs]class atanh(Function): - """ - The inverse hyperbolic tangent function. - - * atanh(x) -> Returns the inverse hyperbolic tangent of x - - See Also - ======== - - asinh, acosh, tanh - """ - nargs = 1 - - def fdiff(self, argindex=1): - if argindex == 1: - return 1/(1 - self.args[0]**2) - else: - raise ArgumentIndexError(self, argindex) - - @classmethod - def eval(cls, arg): - arg = sympify(arg) - - if arg.is_Number: - if arg is S.NaN: - return S.NaN - elif arg is S.Zero: - return S.Zero - elif arg is S.One: - return S.Infinity - elif arg is S.NegativeOne: - return S.NegativeInfinity - elif arg is S.Infinity: - return -S.ImaginaryUnit * C.atan(arg) - elif arg is S.NegativeInfinity: - return S.ImaginaryUnit * C.atan(-arg) - elif arg.is_negative: - return -cls(-arg) - else: - if arg is S.ComplexInfinity: - return S.NaN - - i_coeff = arg.as_coefficient(S.ImaginaryUnit) - - if i_coeff is not None: - return S.ImaginaryUnit * C.atan(i_coeff) - else: - if _coeff_isneg(arg): - return -cls(-arg) - - @staticmethod - @cacheit - def taylor_term(n, x, *previous_terms): - if n < 0 or n % 2 == 0: - return S.Zero - else: - x = sympify(x) - return x**n / n - - def _eval_as_leading_term(self, x): - arg = self.args[0].as_leading_term(x) - - if x in arg.free_symbols and C.Order(1, x).contains(arg): - return arg - else: - return self.func(arg) - - def _sage_(self): - import sage.all as sage - return sage.atanh(self.args[0]._sage_()) - -
    -
    [docs]class acoth(Function): - """ - The inverse hyperbolic cotangent function. - - * acoth(x) -> Returns the inverse hyperbolic cotangent of x - """ - nargs = 1 - - def fdiff(self, argindex=1): - if argindex == 1: - return 1/(1 - self.args[0]**2) - else: - raise ArgumentIndexError(self, argindex) - - @classmethod - def eval(cls, arg): - arg = sympify(arg) - - if arg.is_Number: - if arg is S.NaN: - return S.NaN - elif arg is S.Infinity: - return S.Zero - elif arg is S.NegativeInfinity: - return S.Zero - elif arg is S.Zero: - return S.Pi*S.ImaginaryUnit / 2 - elif arg is S.One: - return S.Infinity - elif arg is S.NegativeOne: - return S.NegativeInfinity - elif arg.is_negative: - return -cls(-arg) - else: - if arg is S.ComplexInfinity: - return 0 - - i_coeff = arg.as_coefficient(S.ImaginaryUnit) - - if i_coeff is not None: - return -S.ImaginaryUnit * C.acot(i_coeff) - else: - if _coeff_isneg(arg): - return -cls(-arg) - - @staticmethod - @cacheit - def taylor_term(n, x, *previous_terms): - if n == 0: - return S.Pi*S.ImaginaryUnit / 2 - elif n < 0 or n % 2 == 0: - return S.Zero - else: - x = sympify(x) - return x**n / n - - def _eval_as_leading_term(self, x): - arg = self.args[0].as_leading_term(x) - - if x in arg.free_symbols and C.Order(1, x).contains(arg): - return arg - else: - return self.func(arg) - - def _sage_(self): - import sage.all as sage - return sage.acoth(self.args[0]._sage_())
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/functions/elementary/integers.html b/dev-py3k/_modules/sympy/functions/elementary/integers.html deleted file mode 100644 index 13fdace23b0..00000000000 --- a/dev-py3k/_modules/sympy/functions/elementary/integers.html +++ /dev/null @@ -1,298 +0,0 @@ - - - - - - - - - - sympy.functions.elementary.integers — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.functions.elementary.integers

    -from sympy.core.basic import C
    -from sympy.core.singleton import S
    -from sympy.core.function import Function
    -from sympy.core import Add
    -from sympy.core.evalf import get_integer_part, PrecisionExhausted
    -
    -###############################################################################
    -######################### FLOOR and CEILING FUNCTIONS #########################
    -###############################################################################
    -
    -
    -
    [docs]class RoundFunction(Function): - """The base class for rounding functions.""" - - nargs = 1 - - @classmethod - def eval(cls, arg): - if arg.is_integer: - return arg - if arg.is_imaginary: - return cls(C.im(arg))*S.ImaginaryUnit - - v = cls._eval_number(arg) - if v is not None: - return v - - # Integral, numerical, symbolic part - ipart = npart = spart = S.Zero - - # Extract integral (or complex integral) terms - terms = Add.make_args(arg) - - for t in terms: - if t.is_integer or (t.is_imaginary and C.im(t).is_integer): - ipart += t - elif t.has(C.Symbol): - spart += t - else: - npart += t - - if not (npart or spart): - return ipart - - # Evaluate npart numerically if independent of spart - if npart and ( - not spart or - npart.is_real and spart.is_imaginary or - npart.is_imaginary and spart.is_real): - try: - re, im = get_integer_part( - npart, cls._dir, {}, return_ints=True) - ipart += C.Integer(re) + C.Integer(im)*S.ImaginaryUnit - npart = S.Zero - except (PrecisionExhausted, NotImplementedError): - pass - - spart = npart + spart - if not spart: - return ipart - elif spart.is_imaginary: - return ipart + cls(C.im(spart), evaluate=False)*S.ImaginaryUnit - else: - return ipart + cls(spart, evaluate=False) - - def _eval_is_bounded(self): - return self.args[0].is_bounded - - def _eval_is_real(self): - return self.args[0].is_real - - def _eval_is_integer(self): - return self.args[0].is_real - -
    -
    [docs]class floor(RoundFunction): - """ - Floor is a univariate function which returns the largest integer - value not greater than its argument. However this implementation - generalizes floor to complex numbers. - - More information can be found in "Concrete mathematics" by Graham, - pp. 87 or visit http://mathworld.wolfram.com/FloorFunction.html. - - >>> from sympy import floor, E, I, Float, Rational - >>> floor(17) - 17 - >>> floor(Rational(23, 10)) - 2 - >>> floor(2*E) - 5 - >>> floor(-Float(0.567)) - -1 - >>> floor(-I/2) - -I - - See Also - ======== - - ceiling - """ - _dir = -1 - - @classmethod - def _eval_number(cls, arg): - if arg.is_Number: - if arg.is_Rational: - return C.Integer(arg.p // arg.q) - elif arg.is_Float: - return C.Integer(int(arg.floor())) - else: - return arg - if arg.is_NumberSymbol: - return arg.approximation_interval(C.Integer)[0] - - def _eval_nseries(self, x, n, logx): - r = self.subs(x, 0) - args = self.args[0] - args0 = args.subs(x, 0) - if args0 == r: - direction = (args - args0).leadterm(x)[0] - if direction.is_positive: - return r - else: - return r - 1 - else: - return r - -
    -
    [docs]class ceiling(RoundFunction): - """ - Ceiling is a univariate function which returns the smallest integer - value not less than its argument. Ceiling function is generalized - in this implementation to complex numbers. - - More information can be found in "Concrete mathematics" by Graham, - pp. 87 or visit http://mathworld.wolfram.com/CeilingFunction.html. - - >>> from sympy import ceiling, E, I, Float, Rational - >>> ceiling(17) - 17 - >>> ceiling(Rational(23, 10)) - 3 - >>> ceiling(2*E) - 6 - >>> ceiling(-Float(0.567)) - 0 - >>> ceiling(I/2) - I - - See Also - ======== - - floor - """ - _dir = 1 - - @classmethod - def _eval_number(cls, arg): - if arg.is_Number: - if arg.is_Rational: - return -C.Integer(-arg.p // arg.q) - elif arg.is_Float: - return C.Integer(int(arg.ceiling())) - else: - return arg - if arg.is_NumberSymbol: - return arg.approximation_interval(C.Integer)[1] - - def _eval_nseries(self, x, n, logx): - r = self.subs(x, 0) - args = self.args[0] - args0 = args.subs(x, 0) - if args0 == r: - direction = (args - args0).leadterm(x)[0] - if direction.is_positive: - return r + 1 - else: - return r - else: - return r
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/functions/elementary/miscellaneous.html b/dev-py3k/_modules/sympy/functions/elementary/miscellaneous.html deleted file mode 100644 index b1b11181adb..00000000000 --- a/dev-py3k/_modules/sympy/functions/elementary/miscellaneous.html +++ /dev/null @@ -1,624 +0,0 @@ - - - - - - - - - - sympy.functions.elementary.miscellaneous — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.functions.elementary.miscellaneous

    -from sympy.core import S, C, sympify
    -from sympy.core.basic import Basic
    -from sympy.core.containers import Tuple
    -from sympy.core.numbers import Rational
    -from sympy.core.operations import LatticeOp, ShortCircuit
    -from sympy.core.function import Application, Lambda
    -from sympy.core.expr import Expr
    -from sympy.core.singleton import Singleton
    -from sympy.core.rules import Transform
    -from sympy.core.compatibility import as_int
    -
    -
    -
    [docs]class IdentityFunction(Lambda, metaclass=Singleton): - """ - The identity function - - Examples - ======== - - >>> from sympy import Id, Symbol - >>> x = Symbol('x') - >>> Id(x) - x - - """ - __slots__ = [] - nargs = 1 - - def __new__(cls): - x = C.Dummy('x') - #construct "by hand" to avoid infinite loop - return Expr.__new__(cls, Tuple(x), x)
    -Id = S.IdentityFunction - -############################################################################### -############################# ROOT and SQUARE ROOT FUNCTION ################### -############################################################################### - - -
    [docs]def sqrt(arg): - """The square root function - - sqrt(x) -> Returns the principal square root of x. - - Examples - ======== - - >>> from sympy import sqrt, Symbol - >>> x = Symbol('x') - - >>> sqrt(x) - sqrt(x) - - >>> sqrt(x)**2 - x - - Note that sqrt(x**2) does not simplify to x. - - >>> sqrt(x**2) - sqrt(x**2) - - This is because the two are not equal to each other in general. - For example, consider x == -1: - - >>> sqrt(x**2).subs(x, -1) - 1 - >>> x.subs(x, -1) - -1 - - This is because sqrt computes the principal square root, so the square may - put the argument in a different branch. This identity does hold if x is - positive: - - >>> y = Symbol('y', positive=True) - >>> sqrt(y**2) - y - - You can force this simplification by using the powdenest() function with - the force option set to True: - - >>> from sympy import powdenest - >>> sqrt(x**2) - sqrt(x**2) - >>> powdenest(sqrt(x**2), force=True) - x - - To get both branches of the square root you can use the RootOf function: - - >>> from sympy import RootOf - - >>> [ RootOf(x**2-3,i) for i in (0,1) ] - [-sqrt(3), sqrt(3)] - - See Also - ======== - - sympy.polys.rootoftools.RootOf, root - - References - ========== - - * http://en.wikipedia.org/wiki/Square_root - * http://en.wikipedia.org/wiki/Principal_value - - """ - # arg = sympify(arg) is handled by Pow - return C.Pow(arg, S.Half) - -
    -
    [docs]def root(arg, n): - """The n-th root function (a shortcut for arg**(1/n)) - - root(x, n) -> Returns the principal n-th root of x. - - - Examples - ======== - - >>> from sympy import root, Rational - >>> from sympy.abc import x, n - - >>> root(x, 2) - sqrt(x) - - >>> root(x, 3) - x**(1/3) - - >>> root(x, n) - x**(1/n) - - >>> root(x, -Rational(2, 3)) - x**(-3/2) - - - To get all n n-th roots you can use the RootOf function. - The following examples show the roots of unity for n - equal 2, 3 and 4: - - >>> from sympy import RootOf, I - - >>> [ RootOf(x**2-1,i) for i in (0,1) ] - [-1, 1] - - >>> [ RootOf(x**3-1,i) for i in (0,1,2) ] - [1, -1/2 - sqrt(3)*I/2, -1/2 + sqrt(3)*I/2] - - >>> [ RootOf(x**4-1,i) for i in (0,1,2,3) ] - [-1, 1, -I, I] - - SymPy, like other symbolic algebra systems, returns the - complex root of negative numbers. This is the principal - root and differs from the text-book result that one might - be expecting. For example, the cube root of -8 does not - come back as -2: - - >>> root(-8, 3) - 2*(-1)**(1/3) - - The real_root function can be used to either make such a result - real or simply return the real root in the first place: - - >>> from sympy import real_root - >>> real_root(_) - -2 - >>> real_root(-32, 5) - -2 - - See Also - ======== - - sympy.polys.rootoftools.RootOf - sympy.core.power.integer_nthroot - sqrt, real_root - - References - ========== - - * http://en.wikipedia.org/wiki/Square_root - * http://en.wikipedia.org/wiki/real_root - * http://en.wikipedia.org/wiki/Root_of_unity - * http://en.wikipedia.org/wiki/Principal_value - * http://mathworld.wolfram.com/CubeRoot.html - - """ - n = sympify(n) - return C.Pow(arg, 1/n) - -
    -def real_root(arg, n=None): - """Return the real nth-root of arg if possible. If n is omitted then - all instances of -1**(1/odd) will be changed to -1. - - Examples - ======== - - >>> from sympy import root, real_root, Rational - >>> from sympy.abc import x, n - - >>> real_root(-8, 3) - -2 - >>> root(-8, 3) - 2*(-1)**(1/3) - >>> real_root(_) - -2 - - See Also - ======== - - sympy.polys.rootoftools.RootOf - sympy.core.power.integer_nthroot - root, sqrt - """ - if n is not None: - n = as_int(n) - rv = C.Pow(arg, Rational(1, n)) - if n % 2 == 0: - return rv - else: - rv = sympify(arg) - n1pow = Transform(lambda x: S.NegativeOne, - lambda x: - x.is_Pow and - x.base is S.NegativeOne and - x.exp.is_Rational and - x.exp.p == 1 and x.exp.q % 2) - return rv.xreplace(n1pow) - -############################################################################### -############################# MINIMUM and MAXIMUM ############################# -############################################################################### - - -class MinMaxBase(Expr, LatticeOp): - def __new__(cls, *args, **assumptions): - if not args: - raise ValueError("The Max/Min functions must have arguments.") - - args = (sympify(arg) for arg in args) - - # first standard filter, for cls.zero and cls.identity - # also reshape Max(a, Max(b, c)) to Max(a, b, c) - try: - _args = frozenset(cls._new_args_filter(args)) - except ShortCircuit: - return cls.zero - - # second filter - # variant I: remove ones which can be removed - # args = cls._collapse_arguments(set(_args), **assumptions) - - # variant II: find local zeros - args = cls._find_localzeros(set(_args), **assumptions) - - _args = frozenset(args) - - if not _args: - return cls.identity - elif len(_args) == 1: - return set(_args).pop() - else: - # base creation - obj = Expr.__new__(cls, _args, **assumptions) - obj._argset = _args - return obj - - @classmethod - def _new_args_filter(cls, arg_sequence): - """ - Generator filtering args. - - first standard filter, for cls.zero and cls.identity. - Also reshape Max(a, Max(b, c)) to Max(a, b, c), - and check arguments for comparability - """ - for arg in arg_sequence: - - # pre-filter, checking comparability of arguments - if (arg.is_real is False) or (arg is S.ComplexInfinity): - raise ValueError("The argument '%s' is not comparable." % arg) - - if arg == cls.zero: - raise ShortCircuit(arg) - elif arg == cls.identity: - continue - elif arg.func == cls: - for x in arg.iter_basic_args(): - yield x - else: - yield arg - - @classmethod - def _find_localzeros(cls, values, **options): - """ - Sequentially allocate values to localzeros. - - When a value is identified as being more extreme than another member it - replaces that member; if this is never true, then the value is simply - appended to the localzeros. - """ - localzeros = set() - for v in values: - is_newzero = True - localzeros_ = list(localzeros) - for z in localzeros_: - if id(v) == id(z): - is_newzero = False - elif cls._is_connected(v, z): - is_newzero = False - if cls._is_asneeded(v, z): - localzeros.remove(z) - localzeros.update([v]) - if is_newzero: - localzeros.update([v]) - return localzeros - - @classmethod - def _is_connected(cls, x, y): - """ - Check if x and y are connected somehow. - """ - if (x == y) or isinstance(x > y, bool) or isinstance(x < y, bool): - return True - if x.is_Number and y.is_Number: - return True - return False - - @classmethod - def _is_asneeded(cls, x, y): - """ - Check if x and y satisfy relation condition. - - The relation condition for Max function is x > y, - for Min function is x < y. They are defined in children Max and Min - classes through the method _rel(cls, x, y) - """ - if (x == y): - return False - if x.is_Number and y.is_Number: - if cls._rel(x, y): - return True - xy = cls._rel(x, y) - if isinstance(xy, bool): - if xy: - return True - return False - yx = cls._rel_inversed(x, y) - if isinstance(yx, bool): - if yx: - return False # never occurs? - return True - return False - - -
    [docs]class Max(MinMaxBase, Application): - """ - Return, if possible, the maximum value of the list. - - When number of arguments is equal one, then - return this argument. - - When number of arguments is equal two, then - return, if possible, the value from (a, b) that is >= the other. - - In common case, when the length of list greater than 2, the task - is more complicated. Return only the arguments, which are greater - than others, if it is possible to determine directional relation. - - If is not possible to determine such a relation, return a partially - evaluated result. - - Assumptions are used to make the decision too. - - Also, only comparable arguments are permitted. - - Examples - ======== - - >>> from sympy import Max, Symbol, oo - >>> from sympy.abc import x, y - >>> p = Symbol('p', positive=True) - >>> n = Symbol('n', negative=True) - - >>> Max(x, -2) #doctest: +SKIP - Max(x, -2) - - >>> Max(x, -2).subs(x, 3) - 3 - - >>> Max(p, -2) - p - - >>> Max(x, y) #doctest: +SKIP - Max(x, y) - - >>> Max(x, y) == Max(y, x) - True - - >>> Max(x, Max(y, z)) #doctest: +SKIP - Max(x, y, z) - - >>> Max(n, 8, p, 7, -oo) #doctest: +SKIP - Max(8, p) - - >>> Max (1, x, oo) - oo - - Algorithm - - The task can be considered as searching of supremums in the - directed complete partial orders [1]_. - - The source values are sequentially allocated by the isolated subsets - in which supremums are searched and result as Max arguments. - - If the resulted supremum is single, then it is returned. - - The isolated subsets are the sets of values which are only the comparable - with each other in the current set. E.g. natural numbers are comparable with - each other, but not comparable with the `x` symbol. Another example: the - symbol `x` with negative assumption is comparable with a natural number. - - Also there are "least" elements, which are comparable with all others, - and have a zero property (maximum or minimum for all elements). E.g. `oo`. - In case of it the allocation operation is terminated and only this value is - returned. - - Assumption: - - if A > B > C then A > C - - if A==B then B can be removed - - References - ========== - - .. [1] http://en.wikipedia.org/wiki/Directed_complete_partial_order - .. [2] http://en.wikipedia.org/wiki/Lattice_%28order%29 - - See Also - ======== - - Min : find minimum values - """ - zero = S.Infinity - identity = S.NegativeInfinity - - @classmethod - def _rel(cls, x, y): - """ - Check if x > y. - """ - return (x > y) - - @classmethod - def _rel_inversed(cls, x, y): - """ - Check if x < y. - """ - return (x < y) - -
    -
    [docs]class Min(MinMaxBase, Application): - """ - Return, if possible, the minimum value of the list. - - Examples - ======== - - >>> from sympy import Min, Symbol, oo - >>> from sympy.abc import x, y - >>> p = Symbol('p', positive=True) - >>> n = Symbol('n', negative=True) - - >>> Min(x, -2) #doctest: +SKIP - Min(x, -2) - - >>> Min(x, -2).subs(x, 3) - -2 - - >>> Min(p, -3) - -3 - - >>> Min(x, y) #doctest: +SKIP - Min(x, y) - - >>> Min(n, 8, p, -7, p, oo) #doctest: +SKIP - Min(n, -7) - - See Also - ======== - - Max : find maximum values - """ - zero = S.NegativeInfinity - identity = S.Infinity - - @classmethod - def _rel(cls, x, y): - """ - Check if x < y. - """ - return (x < y) - - @classmethod - def _rel_inversed(cls, x, y): - """ - Check if x > y. - """ - return (x > y)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/functions/elementary/piecewise.html b/dev-py3k/_modules/sympy/functions/elementary/piecewise.html deleted file mode 100644 index 63d383ceb18..00000000000 --- a/dev-py3k/_modules/sympy/functions/elementary/piecewise.html +++ /dev/null @@ -1,645 +0,0 @@ - - - - - - - - - - sympy.functions.elementary.piecewise — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.functions.elementary.piecewise

    -from sympy.core import Basic, S, Function, diff, Tuple, Expr
    -from sympy.core.relational import Equality, Relational
    -from sympy.core.symbol import Dummy
    -from sympy.functions.elementary.miscellaneous import Max, Min
    -from sympy.logic.boolalg import And, Boolean, Or
    -from sympy.core.compatibility import default_sort_key
    -
    -
    -
    [docs]class ExprCondPair(Tuple): - """Represents an expression, condition pair.""" - - true_sentinel = Dummy('True') - - def __new__(cls, expr, cond): - if cond is True: - cond = ExprCondPair.true_sentinel - return Tuple.__new__(cls, expr, cond) - - @property -
    [docs] def expr(self): - """ - Returns the expression of this pair. - """ - return self.args[0] -
    - @property -
    [docs] def cond(self): - """ - Returns the condition of this pair. - """ - if self.args[1] == ExprCondPair.true_sentinel: - return True - return self.args[1] -
    - @property -
    [docs] def free_symbols(self): - """ - Return the free symbols of this pair. - """ - # Overload Basic.free_symbols because self.args[1] may contain non-Basic - result = self.expr.free_symbols - if hasattr(self.cond, 'free_symbols'): - result |= self.cond.free_symbols - return result -
    - @property - def is_commutative(self): - return self.expr.is_commutative - - def __iter__(self): - yield self.expr - yield self.cond - -
    -
    [docs]class Piecewise(Function): - """ - Represents a piecewise function. - - Usage: - - Piecewise( (expr,cond), (expr,cond), ... ) - - Each argument is a 2-tuple defining a expression and condition - - The conds are evaluated in turn returning the first that is True. - If any of the evaluated conds are not determined explicitly False, - e.g. x < 1, the function is returned in symbolic form. - - If the function is evaluated at a place where all conditions are False, - a ValueError exception will be raised. - - Pairs where the cond is explicitly False, will be removed. - - Examples - ======== - - >>> from sympy import Piecewise, log - >>> from sympy.abc import x - >>> f = x**2 - >>> g = log(x) - >>> p = Piecewise( (0, x<-1), (f, x<=1), (g, True)) - >>> p.subs(x,1) - 1 - >>> p.subs(x,5) - log(5) - - See Also - ======== - - piecewise_fold - """ - - nargs = None - is_Piecewise = True - - def __new__(cls, *args, **options): - # (Try to) sympify args first - newargs = [] - for ec in args: - pair = ExprCondPair(*ec) - cond = pair.cond - if cond is False: - continue - if not isinstance(cond, (bool, Relational, Boolean)): - raise TypeError( - "Cond %s is of type %s, but must be a Relational," - " Boolean, or a built-in bool." % (cond, type(cond))) - newargs.append(pair) - if cond is True: - break - - if options.pop('evaluate', True): - r = cls.eval(*newargs) - else: - r = None - - if r is None: - return Basic.__new__(cls, *newargs, **options) - else: - return r - - @classmethod - def eval(cls, *args): - # Check for situations where we can evaluate the Piecewise object. - # 1) Hit an unevaluable cond (e.g. x<1) -> keep object - # 2) Hit a true condition -> return that expr - # 3) Remove false conditions, if no conditions left -> raise ValueError - all_conds_evaled = True # Do all conds eval to a bool? - piecewise_again = False # Should we pass args to Piecewise again? - non_false_ecpairs = [] - or1 = Or(*[cond for (_, cond) in args if cond is not True]) - for expr, cond in args: - # Check here if expr is a Piecewise and collapse if one of - # the conds in expr matches cond. This allows the collapsing - # of Piecewise((Piecewise(x,x<0),x<0)) to Piecewise((x,x<0)). - # This is important when using piecewise_fold to simplify - # multiple Piecewise instances having the same conds. - # Eventually, this code should be able to collapse Piecewise's - # having different intervals, but this will probably require - # using the new assumptions. - if isinstance(expr, Piecewise): - or2 = Or(*[c for (_, c) in expr.args if c is not True]) - for e, c in expr.args: - # Don't collapse if cond is "True" as this leads to - # incorrect simplifications with nested Piecewises. - if c == cond and (or1 == or2 or cond is not True): - expr = e - piecewise_again = True - cond_eval = cls.__eval_cond(cond) - if cond_eval is None: - all_conds_evaled = False - elif cond_eval: - if all_conds_evaled: - return expr - if len(non_false_ecpairs) != 0: - if non_false_ecpairs[-1].cond == cond: - continue - elif non_false_ecpairs[-1].expr == expr: - non_false_ecpairs[-1] = ExprCondPair( - expr, Or(cond, non_false_ecpairs[-1].cond)) - continue - non_false_ecpairs.append(ExprCondPair(expr, cond)) - if len(non_false_ecpairs) != len(args) or piecewise_again: - return Piecewise(*non_false_ecpairs) - - return None - -
    [docs] def doit(self, **hints): - """ - Evaluate this piecewise function. - """ - newargs = [] - for e, c in self.args: - if hints.get('deep', True): - if isinstance(e, Basic): - e = e.doit(**hints) - if isinstance(c, Basic): - c = c.doit(**hints) - newargs.append((e, c)) - return Piecewise(*newargs) -
    - def _eval_as_leading_term(self, x): - for e, c in self.args: - if c is True or c.subs(x, 0) is True: - return e.as_leading_term(x) - - def _eval_adjoint(self): - return Piecewise(*[(e.adjoint(), c) for e, c in self.args]) - - def _eval_conjugate(self): - return Piecewise(*[(e.conjugate(), c) for e, c in self.args]) - - def _eval_derivative(self, x): - return Piecewise(*[(diff(e, x), c) for e, c in self.args]) - - def _eval_evalf(self, prec): - return Piecewise(*[(e.evalf(prec), c) for e, c in self.args]) - - def _eval_integral(self, x): - from sympy.integrals import integrate - return Piecewise(*[(integrate(e, x), c) for e, c in self.args]) - - def _eval_interval(self, sym, a, b): - """Evaluates the function along the sym in a given interval ab""" - # FIXME: Currently complex intervals are not supported. A possible - # replacement algorithm, discussed in issue 2128, can be found in the - # following papers; - # http://portal.acm.org/citation.cfm?id=281649 - # http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.70.4127&rep=rep1&type=pdf - - if a is None or b is None: - # In this case, it is just simple substitution - return super(Piecewise, self)._eval_interval(sym, a, b) - - mul = 1 - if (a == b) is True: - return S.Zero - elif (a > b) is True: - a, b, mul = b, a, -1 - elif (a <= b) is not True: - newargs = [] - for e, c in self.args: - intervals = self._sort_expr_cond( - sym, S.NegativeInfinity, S.Infinity, c) - values = [] - for lower, upper in intervals: - if (a < lower) is True: - mid = lower - rep = b - val = e.subs(sym, b) - e.subs(sym, mid) - val += self._eval_interval(sym, a, mid) - elif (a > upper) is True: - mid = upper - rep = b - val = e.subs(sym, b) - e.subs(sym, mid) - val += self._eval_interval(sym, a, mid) - elif (a >= lower) is True and (a <= upper) is True: - rep = b - val = e.subs(sym, b) - e.subs(sym, a) - elif (b < lower) is True: - mid = lower - rep = a - val = e.subs(sym, mid) - e.subs(sym, a) - val += self._eval_interval(sym, mid, b) - elif (b > upper) is True: - mid = upper - rep = a - val = e.subs(sym, mid) - e.subs(sym, a) - val += self._eval_interval(sym, mid, b) - elif ((b >= lower) is True) and ((b <= upper) is True): - rep = a - val = e.subs(sym, b) - e.subs(sym, a) - else: - raise NotImplementedError( - """The evaluation of a Piecewise interval when both the lower - and the upper limit are symbolic is not yet implemented.""") - values.append(val) - if len(set(values)) == 1: - try: - c = c.subs(sym, rep) - except AttributeError: - pass - e = values[0] - newargs.append((e, c)) - else: - for i in range(len(values)): - newargs.append((values[i], (c is True and i == len(values) - 1) or - And(rep >= intervals[i][0], rep <= intervals[i][1]))) - return Piecewise(*newargs) - - # Determine what intervals the expr,cond pairs affect. - int_expr = self._sort_expr_cond(sym, a, b) - - # Finally run through the intervals and sum the evaluation. - ret_fun = 0 - for int_a, int_b, expr in int_expr: - ret_fun += expr._eval_interval(sym, Max(a, int_a), Min(b, int_b)) - return mul * ret_fun - - def _sort_expr_cond(self, sym, a, b, targetcond=None): - """Determine what intervals the expr, cond pairs affect. - - 1) If cond is True, then log it as default - 1.1) Currently if cond can't be evaluated, throw NotImplementedError. - 2) For each inequality, if previous cond defines part of the interval - update the new conds interval. - - eg x < 1, x < 3 -> [oo,1],[1,3] instead of [oo,1],[oo,3] - 3) Sort the intervals to make it easier to find correct exprs - - Under normal use, we return the expr,cond pairs in increasing order - along the real axis corresponding to the symbol sym. If targetcond - is given, we return a list of (lowerbound, upperbound) pairs for - this condition.""" - default = None - int_expr = [] - expr_cond = [] - or_cond = False - or_intervals = [] - for expr, cond in self.args: - if isinstance(cond, Or): - for cond2 in sorted(cond.args, key=default_sort_key): - expr_cond.append((expr, cond2)) - else: - expr_cond.append((expr, cond)) - if cond is True: - break - for expr, cond in expr_cond: - if cond is True: - default = expr - break - elif isinstance(cond, Equality): - continue - elif isinstance(cond, And): - lower = S.NegativeInfinity - upper = S.Infinity - for cond2 in cond.args: - if cond2.lts.has(sym): - upper = Min(cond2.gts, upper) - elif cond2.gts.has(sym): - lower = Max(cond2.lts, lower) - else: - lower, upper = cond.lts, cond.gts # part 1: initialize with givens - if cond.lts.has(sym): # part 1a: expand the side ... - lower = S.NegativeInfinity # e.g. x <= 0 ---> -oo <= 0 - elif cond.gts.has(sym): # part 1a: ... that can be expanded - upper = S.Infinity # e.g. x >= 0 ---> oo >= 0 - else: - raise NotImplementedError( - "Unable to handle interval evaluation of expression.") - - # part 1b: Reduce (-)infinity to what was passed in. - lower, upper = Max(a, lower), Min(b, upper) - - for n in range(len(int_expr)): - # Part 2: remove any interval overlap. For any conflicts, the - # iterval already there wins, and the incoming interval updates - # its bounds accordingly. - if self.__eval_cond(lower < int_expr[n][1]) and \ - self.__eval_cond(lower >= int_expr[n][0]): - lower = int_expr[n][1] - elif len(int_expr[n][1].free_symbols) and \ - self.__eval_cond(lower >= int_expr[n][0]): - if self.__eval_cond(lower == int_expr[n][0]): - lower = int_expr[n][1] - else: - int_expr[n][1] = Min(lower, int_expr[n][1]) - elif len(int_expr[n][1].free_symbols) and \ - lower < int_expr[n][0] is not True: - upper = Min(upper, int_expr[n][0]) - elif self.__eval_cond(upper > int_expr[n][0]) and \ - self.__eval_cond(upper <= int_expr[n][1]): - upper = int_expr[n][0] - elif len(int_expr[n][0].free_symbols) and \ - self.__eval_cond(upper < int_expr[n][1]): - int_expr[n][0] = Max(upper, int_expr[n][0]) - - if self.__eval_cond(lower >= upper) is not True: # Is it still an interval? - int_expr.append([lower, upper, expr]) - if cond is targetcond: - return [(lower, upper)] - elif isinstance(targetcond, Or) and cond in targetcond.args: - or_cond = Or(or_cond, cond) - or_intervals.append((lower, upper)) - if or_cond == targetcond: - or_intervals.sort(key=lambda x: x[0]) - return or_intervals - - int_expr.sort(key=lambda x: x[1].sort_key( - ) if x[1].is_number else S.NegativeInfinity.sort_key()) - int_expr.sort(key=lambda x: x[0].sort_key( - ) if x[0].is_number else S.Infinity.sort_key()) - from sympy.functions.elementary.miscellaneous import MinMaxBase - for n in range(len(int_expr)): - if len(int_expr[n][0].free_symbols) or len(int_expr[n][1].free_symbols): - if isinstance(int_expr[n][1], Min) or int_expr[n][1] == b: - newval = Min(*int_expr[n][:-1]) - if n > 0 and int_expr[n][0] == int_expr[n - 1][1]: - int_expr[n - 1][1] = newval - int_expr[n][0] = newval - else: - newval = Max(*int_expr[n][:-1]) - if n < len(int_expr) - 1 and int_expr[n][1] == int_expr[n + 1][0]: - int_expr[n + 1][0] = newval - int_expr[n][1] = newval - - # Add holes to list of intervals if there is a default value, - # otherwise raise a ValueError. - holes = [] - curr_low = a - for int_a, int_b, expr in int_expr: - if (curr_low < int_a) is True: - holes.append([curr_low, Min(b, int_a), default]) - elif (curr_low >= int_a) is not True: - holes.append([curr_low, Min(b, int_a), default]) - curr_low = Min(b, int_b) - if (curr_low < b) is True: - holes.append([Min(b, curr_low), b, default]) - elif (curr_low >= b) is not True: - holes.append([Min(b, curr_low), b, default]) - - if holes and default is not None: - int_expr.extend(holes) - if targetcond is True: - return [(h[0], h[1]) for h in holes] - elif holes and default is None: - raise ValueError("Called interval evaluation over piecewise " - "function on undefined intervals %s" % - ", ".join([str((h[0], h[1])) for h in holes])) - - return int_expr - - def _eval_nseries(self, x, n, logx): - args = [(ec.expr._eval_nseries(x, n, logx), ec.cond) for ec in self.args] - return self.func(*args) - - def _eval_power(self, s): - return Piecewise(*[(e**s, c) for e, c in self.args]) - - def _eval_subs(self, old, new): - """ - Piecewise conditions may contain bool which are not of Basic type. - """ - from sympy import checksol, solve - args = list(self.args) - for i, (e, c) in enumerate(args): - e = e._subs(old, new) - - if isinstance(c, bool): - pass - elif isinstance(c, Basic): - c = c._subs(old, new) - if isinstance(c, Equality): - if checksol(c, {}, minimal=True): - # the equality is trivially solved - c = True - else: - # try to solve the equality - try: - slns = solve(c, dict=True) - if not slns: - c = False - elif len(slns) == 1: - c = And(*[Equality(key, value) - for key, value in slns[0].items()]) - except NotImplementedError: - pass - - args[i] = e, c - - return Piecewise(*args) - - def _eval_transpose(self): - return Piecewise(*[(e.transpose(), c) for e, c in self.args]) - - def _eval_template_is_attr(self, is_attr, when_multiple=None): - b = None - for expr, _ in self.args: - a = getattr(expr, is_attr) - if a is None: - return None - if b is None: - b = a - elif b is not a: - return when_multiple - return b - - _eval_is_bounded = lambda self: self._eval_template_is_attr( - 'is_bounded', when_multiple=False) - _eval_is_complex = lambda self: self._eval_template_is_attr('is_complex') - _eval_is_even = lambda self: self._eval_template_is_attr('is_even') - _eval_is_imaginary = lambda self: self._eval_template_is_attr( - 'is_imaginary') - _eval_is_integer = lambda self: self._eval_template_is_attr('is_integer') - _eval_is_irrational = lambda self: self._eval_template_is_attr( - 'is_irrational') - _eval_is_negative = lambda self: self._eval_template_is_attr('is_negative') - _eval_is_nonnegative = lambda self: self._eval_template_is_attr( - 'is_nonnegative') - _eval_is_nonpositive = lambda self: self._eval_template_is_attr( - 'is_nonpositive') - _eval_is_nonzero = lambda self: self._eval_template_is_attr( - 'is_nonzero', when_multiple=True) - _eval_is_odd = lambda self: self._eval_template_is_attr('is_odd') - _eval_is_polar = lambda self: self._eval_template_is_attr('is_polar') - _eval_is_positive = lambda self: self._eval_template_is_attr('is_positive') - _eval_is_real = lambda self: self._eval_template_is_attr('is_real') - _eval_is_zero = lambda self: self._eval_template_is_attr( - 'is_zero', when_multiple=False) - - @classmethod - def __eval_cond(cls, cond): - """Return the truth value of the condition.""" - if cond is True: - return True - return None - -
    -
    [docs]def piecewise_fold(expr): - """ - Takes an expression containing a piecewise function and returns the - expression in piecewise form. - - Examples - ======== - - >>> from sympy import Piecewise, piecewise_fold, sympify as S - >>> from sympy.abc import x - >>> p = Piecewise((x, x < 1), (1, S(1) <= x)) - >>> piecewise_fold(x*p) - Piecewise((x**2, x < 1), (x, 1 <= x)) - - See Also - ======== - - Piecewise - """ - if not isinstance(expr, Basic) or not expr.has(Piecewise): - return expr - new_args = list(map(piecewise_fold, expr.args)) - if expr.func is ExprCondPair: - return ExprCondPair(*new_args) - piecewise_args = [] - for n, arg in enumerate(new_args): - if arg.func is Piecewise: - piecewise_args.append(n) - if len(piecewise_args) > 0: - n = piecewise_args[0] - new_args = [(expr.func(*(new_args[:n] + [e] + new_args[n + 1:])), c) - for e, c in new_args[n].args] - if len(piecewise_args) > 1: - return piecewise_fold(Piecewise(*new_args)) - return Piecewise(*new_args)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/functions/elementary/trigonometric.html b/dev-py3k/_modules/sympy/functions/elementary/trigonometric.html deleted file mode 100644 index 81c6ba1e4c2..00000000000 --- a/dev-py3k/_modules/sympy/functions/elementary/trigonometric.html +++ /dev/null @@ -1,1788 +0,0 @@ - - - - - - - - - - sympy.functions.elementary.trigonometric — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.functions.elementary.trigonometric

    -from sympy.core.add import Add
    -from sympy.core.basic import C, sympify, cacheit
    -from sympy.core.singleton import S
    -from sympy.core.numbers import igcdex
    -from sympy.core.function import Function, ArgumentIndexError
    -from sympy.functions.elementary.miscellaneous import sqrt
    -from sympy.functions.elementary.exponential import log
    -from sympy.functions.elementary.hyperbolic import HyperbolicFunction
    -from sympy.utilities.iterables import numbered_symbols
    -
    -###############################################################################
    -########################## TRIGONOMETRIC FUNCTIONS ############################
    -###############################################################################
    -
    -
    -class TrigonometricFunction(Function):
    -    """Base class for trigonometric functions. """
    -
    -    unbranched = True
    -
    -
    -def _peeloff_pi(arg):
    -    """
    -    Split ARG into two parts, a "rest" and a multiple of pi/2.
    -    This assumes ARG to be an Add.
    -    The multiple of pi returned in the second position is always a Rational.
    -
    -    Examples:
    -    >>> from sympy.functions.elementary.trigonometric import _peeloff_pi as peel
    -    >>> from sympy import pi
    -    >>> from sympy.abc import x, y
    -    >>> peel(x + pi/2)
    -    (x, pi/2)
    -    >>> peel(x + 2*pi/3 + pi*y)
    -    (x + pi*y + pi/6, pi/2)
    -    """
    -    for a in Add.make_args(arg):
    -        if a is S.Pi:
    -            K = S.One
    -            break
    -        elif a.is_Mul:
    -            K, p = a.as_two_terms()
    -            if p is S.Pi and K.is_Rational:
    -                break
    -    else:
    -        return arg, S.Zero
    -
    -    m1 = (K % S.Half) * S.Pi
    -    m2 = K*S.Pi - m1
    -    return arg - m2, m2
    -
    -
    -def _pi_coeff(arg, cycles=1):
    -    """
    -    When arg is a Number times pi (e.g. 3*pi/2) then return the Number
    -    normalized to be in the range [0, 2], else None.
    -
    -    When an even multiple of pi is encountered, if it is multiplying
    -    something with known parity then the multiple is returned as 0 otherwise
    -    as 2.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.functions.elementary.trigonometric import _pi_coeff as coeff
    -    >>> from sympy import pi
    -    >>> from sympy.abc import x, y
    -    >>> coeff(3*x*pi)
    -    3*x
    -    >>> coeff(11*pi/7)
    -    11/7
    -    >>> coeff(-11*pi/7)
    -    3/7
    -    >>> coeff(4*pi)
    -    0
    -    >>> coeff(5*pi)
    -    1
    -    >>> coeff(5.0*pi)
    -    1
    -    >>> coeff(5.5*pi)
    -    3/2
    -    >>> coeff(2 + pi)
    -
    -    """
    -    arg = sympify(arg)
    -    if arg is S.Pi:
    -        return S.One
    -    elif not arg:
    -        return S.Zero
    -    elif arg.is_Mul:
    -        cx = arg.coeff(S.Pi)
    -        if cx:
    -            c, x = cx.as_coeff_Mul()  # pi is not included as coeff
    -            if c.is_Float:
    -                # recast exact binary fractions to Rationals
    -                f = abs(c) % 1
    -                if f != 0:
    -                    p = -int(round(log(f, 2).evalf()))
    -                    m = 2**p
    -                    cm = c*m
    -                    i = int(cm)
    -                    if i == cm:
    -                        c = C.Rational(i, m)
    -                        cx = c*x
    -                else:
    -                    c = C.Rational(int(c))
    -                    cx = c*x
    -            if x.is_integer:
    -                c2 = c % 2
    -                if c2 == 1:
    -                    return x
    -                elif not c2:
    -                    if x.is_even is not None:  # known parity
    -                        return S.Zero
    -                    return 2*x
    -                else:
    -                    return c2*x
    -            return cx
    -
    -
    -
    [docs]class sin(TrigonometricFunction): - """ - The sine function. - - * sin(x) -> Returns the sine of x (measured in radians) - - Notes - ===== - - * sin(x) will evaluate automatically in the case x - is a multiple of pi, pi/2, pi/3, pi/4 and pi/6. - - Examples - ======== - - >>> from sympy import sin, pi - >>> from sympy.abc import x - >>> sin(x**2).diff(x) - 2*x*cos(x**2) - >>> sin(1).diff(x) - 0 - >>> sin(pi) - 0 - >>> sin(pi/2) - 1 - >>> sin(pi/6) - 1/2 - - See Also - ======== - - cos, tan, asin - - References - ========== - - U{Definitions in trigonometry<http://planetmath.org/encyclopedia/DefinitionsInTrigonometry.html>} - - """ - - nargs = 1 - - def fdiff(self, argindex=1): - if argindex == 1: - return cos(self.args[0]) - else: - raise ArgumentIndexError(self, argindex) - -
    [docs] def inverse(self, argindex=1): - """ - Returns the inverse of this function. - """ - return asin -
    - @classmethod - def eval(cls, arg): - if arg.is_Number: - if arg is S.NaN: - return S.NaN - elif arg is S.Zero: - return S.Zero - elif arg is S.Infinity or arg is S.NegativeInfinity: - return - - if arg.could_extract_minus_sign(): - return -cls(-arg) - - i_coeff = arg.as_coefficient(S.ImaginaryUnit) - if i_coeff is not None: - return S.ImaginaryUnit * C.sinh(i_coeff) - - pi_coeff = _pi_coeff(arg) - if pi_coeff is not None: - if pi_coeff.is_integer: - return S.Zero - - if not pi_coeff.is_Rational: - narg = pi_coeff*S.Pi - if narg != arg: - return cls(narg) - return None - - # http://code.google.com/p/sympy/issues/detail?id=2949 - # transform a sine to a cosine, to avoid redundant code - if pi_coeff.is_Rational: - x = pi_coeff % 2 - if x > 1: - return -cls((x % 1)*S.Pi) - if 2*x > 1: - return cls((1 - x)*S.Pi) - narg = ((pi_coeff + C.Rational(3, 2)) % 2)*S.Pi - result = cos(narg) - if not isinstance(result, cos): - return result - if pi_coeff*S.Pi != arg: - return cls(pi_coeff*S.Pi) - return None - - if arg.is_Add: - x, m = _peeloff_pi(arg) - if m: - return sin(m)*cos(x) + cos(m)*sin(x) - - if arg.func is asin: - return arg.args[0] - - if arg.func is atan: - x = arg.args[0] - return x / sqrt(1 + x**2) - - if arg.func is atan2: - y, x = arg.args - return y / sqrt(x**2 + y**2) - - if arg.func is acos: - x = arg.args[0] - return sqrt(1 - x**2) - - if arg.func is acot: - x = arg.args[0] - return 1 / (sqrt(1 + 1 / x**2) * x) - - @staticmethod - @cacheit - def taylor_term(n, x, *previous_terms): - if n < 0 or n % 2 == 0: - return S.Zero - else: - x = sympify(x) - - if len(previous_terms) > 2: - p = previous_terms[-2] - return -p * x**2 / (n*(n - 1)) - else: - return (-1)**(n//2) * x**(n)/C.factorial(n) - - def _eval_rewrite_as_exp(self, arg): - exp, I = C.exp, S.ImaginaryUnit - if isinstance(arg, TrigonometricFunction) or isinstance(arg, HyperbolicFunction): - arg = arg.func(arg.args[0]).rewrite(exp) - return (exp(arg*I) - exp(-arg*I)) / (2*I) - - def _eval_rewrite_as_Pow(self, arg): - if arg.func is log: - I = S.ImaginaryUnit - x = arg.args[0] - return I*x**-I / 2 - I*x**I /2 - - def _eval_rewrite_as_cos(self, arg): - return -cos(arg + S.Pi/2) - - def _eval_rewrite_as_tan(self, arg): - tan_half = tan(S.Half*arg) - return 2*tan_half/(1 + tan_half**2) - - def _eval_rewrite_as_sincos(self, arg): - return sin(arg)*cos(arg)/cos(arg) - - def _eval_rewrite_as_cot(self, arg): - cot_half = cot(S.Half*arg) - return 2*cot_half/(1 + cot_half**2) - - def _eval_rewrite_as_pow(self, arg): - return self.rewrite(cos).rewrite(pow) - - def _eval_rewrite_as_sqrt(self, arg): - return self.rewrite(cos).rewrite(sqrt) - - def _eval_conjugate(self): - return self.func(self.args[0].conjugate()) - - def as_real_imag(self, deep=True, **hints): - if self.args[0].is_real: - if deep: - hints['complex'] = False - return (self.expand(deep, **hints), S.Zero) - else: - return (self, S.Zero) - if deep: - re, im = self.args[0].expand(deep, **hints).as_real_imag() - else: - re, im = self.args[0].as_real_imag() - return (sin(re)*C.cosh(im), cos(re)*C.sinh(im)) - - def _eval_expand_trig(self, **hints): - from sympy import expand_mul - arg = self.args[0] - x = None - if arg.is_Add: # TODO, implement more if deep stuff here - # TODO: Do this more efficiently for more than two terms - x, y = arg.as_two_terms() - sx = sin(x, evaluate=False)._eval_expand_trig() - sy = sin(y, evaluate=False)._eval_expand_trig() - cx = cos(x, evaluate=False)._eval_expand_trig() - cy = cos(y, evaluate=False)._eval_expand_trig() - return sx*cy + sy*cx - else: - n, x = arg.as_coeff_Mul(rational=True) - if n.is_Integer: # n will be positive because of .eval - # canonicalization - - # See http://mathworld.wolfram.com/Multiple-AngleFormulas.html - if n.is_odd: - return (-1)**((n - 1)/2)*C.chebyshevt(n, sin(x)) - else: - return expand_mul((-1)**(n/2 - 1)*cos(x)*C.chebyshevu(n - - 1, sin(x)), deep=False) - pi_coeff = _pi_coeff(arg) - if pi_coeff is not None: - if pi_coeff.is_Rational: - return self.rewrite(sqrt) - return sin(arg) - - def _eval_as_leading_term(self, x): - arg = self.args[0].as_leading_term(x) - - if x in arg.free_symbols and C.Order(1, x).contains(arg): - return arg - else: - return self.func(arg) - - def _eval_is_real(self): - return self.args[0].is_real - - def _eval_is_bounded(self): - arg = self.args[0] - if arg.is_real: - return True - - def _sage_(self): - import sage.all as sage - return sage.sin(self.args[0]._sage_()) - -
    -
    [docs]class cos(TrigonometricFunction): - """ - The cosine function. - - * cos(x) -> Returns the cosine of x (measured in radians) - - Notes - ===== - - * cos(x) will evaluate automatically in the case x - is a multiple of pi, pi/2, pi/3, pi/4 and pi/6. - - Examples - ======== - - >>> from sympy import cos, pi - >>> from sympy.abc import x - >>> cos(x**2).diff(x) - -2*x*sin(x**2) - >>> cos(1).diff(x) - 0 - >>> cos(pi) - -1 - >>> cos(pi/2) - 0 - >>> cos(2*pi/3) - -1/2 - - See Also - ======== - - sin, tan, acos - - References - ========== - - U{Definitions in trigonometry<http://planetmath.org/encyclopedia/DefinitionsInTrigonometry.html>} - - """ - - nargs = 1 - - def fdiff(self, argindex=1): - if argindex == 1: - return -sin(self.args[0]) - else: - raise ArgumentIndexError(self, argindex) - - def inverse(self, argindex=1): - return acos - - @classmethod - def eval(cls, arg): - if arg.is_Number: - if arg is S.NaN: - return S.NaN - elif arg is S.Zero: - return S.One - elif arg is S.Infinity or arg is S.NegativeInfinity: - # In this cases, it is unclear if we should - # return S.NaN or leave un-evaluated. One - # useful test case is how "limit(sin(x)/x,x,oo)" - # is handled. - # See test_sin_cos_with_infinity() an - # Test for issue 209 - # http://code.google.com/p/sympy/issues/detail?id=2097 - # For now, we return un-evaluated. - return - - if arg.could_extract_minus_sign(): - return cls(-arg) - - i_coeff = arg.as_coefficient(S.ImaginaryUnit) - if i_coeff is not None: - return C.cosh(i_coeff) - - pi_coeff = _pi_coeff(arg) - if pi_coeff is not None: - if pi_coeff.is_integer: - return (S.NegativeOne)**pi_coeff - if not pi_coeff.is_Rational: - narg = pi_coeff*S.Pi - if narg != arg: - return cls(narg) - return None - - # cosine formula ##################### - # http://code.google.com/p/sympy/issues/detail?id=2949 - # explicit calculations are preformed for - # cos(k pi / 8), cos(k pi /10), and cos(k pi / 12) - # Some other exact values like cos(k pi/15) can be - # calculated using a partial-fraction decomposition - # by calling cos( X ).rewrite(sqrt) - cst_table_some = { - 3: S.Half, - 5: (sqrt(5) + 1)/4, - } - if pi_coeff.is_Rational: - q = pi_coeff.q - p = pi_coeff.p % (2*q) - if p > q: - narg = (pi_coeff - 1)*S.Pi - return -cls(narg) - if 2*p > q: - narg = (1 - pi_coeff)*S.Pi - return -cls(narg) - - # If nested sqrt's are worse than un-evaluation - # you can require q in (1, 2, 3, 4, 6) - # q <= 12 returns expressions with 2 or fewer nestings. - if q > 12: - return None - - if q in cst_table_some: - cts = cst_table_some[pi_coeff.q] - return C.chebyshevt(pi_coeff.p, cts).expand() - - if 0 == q % 2: - narg = (pi_coeff*2)*S.Pi - nval = cls(narg) - if None == nval: - return None - x = (2*pi_coeff + 1)/2 - sign_cos = (-1)**((-1 if x < 0 else 1)*int(abs(x))) - return sign_cos*sqrt( (1 + nval)/2 ) - return None - - if arg.is_Add: - x, m = _peeloff_pi(arg) - if m: - return cos(m)*cos(x) - sin(m)*sin(x) - - if arg.func is acos: - return arg.args[0] - - if arg.func is atan: - x = arg.args[0] - return 1 / sqrt(1 + x**2) - - if arg.func is atan2: - y, x = arg.args - return x / sqrt(x**2 + y**2) - - if arg.func is asin: - x = arg.args[0] - return sqrt(1 - x ** 2) - - if arg.func is acot: - x = arg.args[0] - return 1 / sqrt(1 + 1 / x**2) - - @staticmethod - @cacheit - def taylor_term(n, x, *previous_terms): - if n < 0 or n % 2 == 1: - return S.Zero - else: - x = sympify(x) - - if len(previous_terms) > 2: - p = previous_terms[-2] - return -p * x**2 / (n*(n - 1)) - else: - return (-1)**(n//2)*x**(n)/C.factorial(n) - - def _eval_rewrite_as_exp(self, arg): - exp, I = C.exp, S.ImaginaryUnit - if isinstance(arg, TrigonometricFunction) or isinstance(arg, HyperbolicFunction): - arg = arg.func(arg.args[0]).rewrite(exp) - return (exp(arg*I) + exp(-arg*I)) / 2 - - def _eval_rewrite_as_Pow(self, arg): - if arg.func is log: - I = S.ImaginaryUnit - x = arg.args[0] - return x**I/2 + x**-I/2 - - def _eval_rewrite_as_sin(self, arg): - return sin(arg + S.Pi/2) - - def _eval_rewrite_as_tan(self, arg): - tan_half = tan(S.Half*arg)**2 - return (1 - tan_half)/(1 + tan_half) - - def _eval_rewrite_as_sincos(self, arg): - return sin(arg)*cos(arg)/sin(arg) - - def _eval_rewrite_as_cot(self, arg): - cot_half = cot(S.Half*arg)**2 - return (cot_half - 1)/(cot_half + 1) - - def _eval_rewrite_as_pow(self, arg): - return self._eval_rewrite_as_sqrt(arg) - - def _eval_rewrite_as_sqrt(self, arg): - _EXPAND_INTS = False - - def migcdex(x): - # recursive calcuation of gcd and linear combination - # for a sequence of integers. - # Given (x1, x2, x3) - # Returns (y1, y1, y3, g) - # such that g is the gcd and x1*y1+x2*y2+x3*y3 - g = 0 - # Note, that this is only one such linear combination. - if len(x) == 1: - return (1, x[0]) - if len(x) == 2: - return igcdex(x[0], x[-1]) - g = migcdex(x[1:]) - u, v, h = igcdex(x[0], g[-1]) - return tuple([u] + [v*i for i in g[0:-1] ] + [h]) - - def ipartfrac(r, factors=None): - if isinstance(r, int): - return r - assert isinstance(r, C.Rational) - n = r.q - if 2 > r.q*r.q: - return r.q - - if None == factors: - a = [n//x**y for x, y in factorint(r.q).items()] - else: - a = [n//x for x in factors] - if len(a) == 1: - return [ r ] - h = migcdex(a) - ans = [ r.p*C.Rational(i*j, r.q) for i, j in zip(h[:-1], a) ] - assert r == sum(ans) - return ans - pi_coeff = _pi_coeff(arg) - if pi_coeff is None: - return None - - assert not pi_coeff.is_integer, "should have been simplified already" - - if not pi_coeff.is_Rational: - return None - - cst_table_some = { - 3: S.Half, - 5: (sqrt(5) + 1)/4, - 17: sqrt((15 + sqrt(17))/32 + sqrt(2)*(sqrt(17 - sqrt(17)) + - sqrt(sqrt(2)*(-8*sqrt(17 + sqrt(17)) - (1 - sqrt(17)) - *sqrt(17 - sqrt(17))) + 6*sqrt(17) + 34))/32) - # 65537 and 257 are the only other known Fermat primes - # Please add if you would like them - } - - def fermatCoords(n): - assert isinstance(n, int) - assert n > 0 - if n == 1 or 0 == n % 2: - return False - primes = dict( [(p, 0) for p in cst_table_some ] ) - assert 1 not in primes - for p_i in primes: - while 0 == n % p_i: - n = n/p_i - primes[p_i] += 1 - if 1 != n: - return False - if max(primes.values()) > 1: - return False - return tuple([ p for p in primes if primes[p] == 1]) - - if pi_coeff.q in cst_table_some: - return C.chebyshevt(pi_coeff.p, cst_table_some[pi_coeff.q]).expand() - - if 0 == pi_coeff.q % 2: # recursively remove powers of 2 - narg = (pi_coeff*2)*S.Pi - nval = cos(narg) - if None == nval: - return None - nval = nval.rewrite(sqrt) - if not _EXPAND_INTS: - if (isinstance(nval, cos) or isinstance(-nval, cos)): - return None - x = (2*pi_coeff + 1)/2 - sign_cos = (-1)**((-1 if x < 0 else 1)*int(abs(x))) - return sign_cos*sqrt( (1 + nval)/2 ) - - FC = fermatCoords(pi_coeff.q) - if FC: - decomp = ipartfrac(pi_coeff, FC) - X = [(x[1], x[0]*S.Pi) for x in zip(decomp, numbered_symbols('z'))] - pcls = cos(sum([x[0] for x in X]))._eval_expand_trig().subs(X) - return pcls.rewrite(sqrt) - if _EXPAND_INTS: - decomp = ipartfrac(pi_coeff) - X = [(x[1], x[0]*S.Pi) for x in zip(decomp, numbered_symbols('z'))] - pcls = cos(sum([x[0] for x in X]))._eval_expand_trig().subs(X) - return pcls - return None - - def _eval_conjugate(self): - return self.func(self.args[0].conjugate()) - - def as_real_imag(self, deep=True, **hints): - if self.args[0].is_real: - if deep: - hints['complex'] = False - return (self.expand(deep, **hints), S.Zero) - else: - return (self, S.Zero) - if deep: - re, im = self.args[0].expand(deep, **hints).as_real_imag() - else: - re, im = self.args[0].as_real_imag() - return (cos(re)*C.cosh(im), -sin(re)*C.sinh(im)) - - def _eval_expand_trig(self, **hints): - arg = self.args[0] - x = None - if arg.is_Add: # TODO: Do this more efficiently for more than two terms - x, y = arg.as_two_terms() - sx = sin(x, evaluate=False)._eval_expand_trig() - sy = sin(y, evaluate=False)._eval_expand_trig() - cx = cos(x, evaluate=False)._eval_expand_trig() - cy = cos(y, evaluate=False)._eval_expand_trig() - return cx*cy - sx*sy - else: - coeff, terms = arg.as_coeff_Mul(rational=True) - if coeff.is_Integer: - return C.chebyshevt(coeff, cos(terms)) - pi_coeff = _pi_coeff(arg) - if pi_coeff is not None: - if pi_coeff.is_Rational: - return self.rewrite(sqrt) - return cos(arg) - - def _eval_as_leading_term(self, x): - arg = self.args[0].as_leading_term(x) - - if x in arg.free_symbols and C.Order(1, x).contains(arg): - return S.One - else: - return self.func(arg) - - def _eval_is_real(self): - return self.args[0].is_real - - def _eval_is_bounded(self): - arg = self.args[0] - - if arg.is_real: - return True - - def _sage_(self): - import sage.all as sage - return sage.cos(self.args[0]._sage_()) - -
    -class sec(TrigonometricFunction): # TODO implement rest all functions for sec. see cos, sin, tan. - - def _eval_rewrite_as_cos(self, arg): - return (1/cos(arg)) - - def _eval_rewrite_as_sincos(self, arg): - return sin(arg)/(cos(arg)*sin(arg)) - - -class csc(TrigonometricFunction): # TODO implement rest all functions for csc. see cos, sin, tan. - - def _eval_rewrite_as_sin(self, arg): - return (1/sin(arg)) - - def _eval_rewrite_as_sincos(self, arg): - return cos(arg)/(sin(arg)*cos(arg)) - - -
    [docs]class tan(TrigonometricFunction): - """ - tan(x) -> Returns the tangent of x (measured in radians) - - Notes - ===== - - * tan(x) will evaluate automatically in the case x is a - multiple of pi. - - Examples - ======== - - >>> from sympy import tan - >>> from sympy.abc import x - >>> tan(x**2).diff(x) - 2*x*(tan(x**2)**2 + 1) - >>> tan(1).diff(x) - 0 - - See Also - ======== - - sin, cos, atan - - References - ========== - - U{Definitions in trigonometry<http://planetmath.org/encyclopedia/DefinitionsInTrigonometry.html>} - - """ - - nargs = 1 - - def fdiff(self, argindex=1): - if argindex == 1: - return S.One + self**2 - else: - raise ArgumentIndexError(self, argindex) - -
    [docs] def inverse(self, argindex=1): - """ - Returns the inverse of this function. - """ - return atan -
    - @classmethod - def eval(cls, arg): - if arg.is_Number: - if arg is S.NaN: - return S.NaN - elif arg is S.Zero: - return S.Zero - - if arg.could_extract_minus_sign(): - return -cls(-arg) - - i_coeff = arg.as_coefficient(S.ImaginaryUnit) - if i_coeff is not None: - return S.ImaginaryUnit * C.tanh(i_coeff) - - pi_coeff = _pi_coeff(arg, 2) - if pi_coeff is not None: - if pi_coeff.is_integer: - return S.Zero - - if not pi_coeff.is_Rational: - narg = pi_coeff*S.Pi - if narg != arg: - return cls(narg) - return None - - if pi_coeff.is_Rational: - narg = ((pi_coeff + S.Half) % 1 - S.Half)*S.Pi - # see cos() to specify which expressions should be - # expanded automatically in terms of radicals - cresult, sresult = cos(narg), cos(narg - S.Pi/2) - if not isinstance(cresult, cos) \ - and not isinstance(sresult, cos): - if cresult == 0: - return S.ComplexInfinity - return (sresult/cresult) - if narg != arg: - return cls(narg) - - if arg.is_Add: - x, m = _peeloff_pi(arg) - if m: - tanm = tan(m) - tanx = tan(x) - if tanm is S.ComplexInfinity: - return -cot(x) - return (tanm + tanx)/(1 - tanm*tanx) - - if arg.func is atan: - return arg.args[0] - - if arg.func is atan2: - y, x = arg.args - return y/x - - if arg.func is asin: - x = arg.args[0] - return x / sqrt(1 - x**2) - - if arg.func is acos: - x = arg.args[0] - return sqrt(1 - x**2) / x - - if arg.func is acot: - x = arg.args[0] - return 1 / x - - @staticmethod - @cacheit - def taylor_term(n, x, *previous_terms): - if n < 0 or n % 2 == 0: - return S.Zero - else: - x = sympify(x) - - a, b = ((n - 1)//2), 2**(n + 1) - - B = C.bernoulli(n + 1) - F = C.factorial(n + 1) - - return (-1)**a * b*(b - 1) * B/F * x**n - - def _eval_nseries(self, x, n, logx): - i = self.args[0].limit(x, 0)*2/S.Pi - if i and i.is_Integer: - return self.rewrite(cos)._eval_nseries(x, n=n, logx=logx) - return Function._eval_nseries(self, x, n=n, logx=logx) - - def _eval_rewrite_as_Pow(self, arg): - if arg.func is log: - I = S.ImaginaryUnit - x = arg.args[0] - return I*(x**-I - x**I)/(x**-I + x**I) - - def _eval_conjugate(self): - return self.func(self.args[0].conjugate()) - - def as_real_imag(self, deep=True, **hints): - if self.args[0].is_real: - if deep: - hints['complex'] = False - return (self.expand(deep, **hints), S.Zero) - else: - return (self, S.Zero) - if deep: - re, im = self.args[0].expand(deep, **hints).as_real_imag() - else: - re, im = self.args[0].as_real_imag() - denom = cos(re)**2 + C.sinh(im)**2 - return (sin(re)*cos(re)/denom, C.sinh(im)*C.cosh(im)/denom) - - def _eval_expand_trig(self, **hints): - arg = self.args[0] - x = None - if arg.is_Add: - from sympy import symmetric_poly - n = len(arg.args) - TX = [] - for x in arg.args: - tx = tan(x, evaluate=False)._eval_expand_trig() - TX.append(tx) - - Yg = numbered_symbols('Y') - Y = [ next(Yg) for i in range(n) ] - - p = [0, 0] - for i in range(n + 1): - p[1 - i % 2] += symmetric_poly(i, Y)*(-1)**((i % 4)//2) - return (p[0]/p[1]).subs(list(zip(Y, TX))) - - else: - coeff, terms = arg.as_coeff_Mul(rational=True) - if coeff.is_Integer and coeff > 1: - I = S.ImaginaryUnit - z = C.Symbol('dummy', real=True) - P = ((1 + I*z)**coeff).expand() - return (C.im(P)/C.re(P)).subs([(z, tan(terms))]) - return tan(arg) - - def _eval_rewrite_as_exp(self, arg): - exp, I = C.exp, S.ImaginaryUnit - if isinstance(arg, TrigonometricFunction) or isinstance(arg, HyperbolicFunction): - arg = arg.func(arg.args[0]).rewrite(exp) - neg_exp, pos_exp = exp(-arg*I), exp(arg*I) - return I*(neg_exp - pos_exp)/(neg_exp + pos_exp) - - def _eval_rewrite_as_sin(self, x): - return 2*sin(x)**2/sin(2*x) - - def _eval_rewrite_as_cos(self, x): - return -cos(x + S.Pi/2)/cos(x) - - def _eval_rewrite_as_sincos(self, arg): - return sin(arg)/cos(arg) - - def _eval_rewrite_as_cot(self, arg): - return 1/cot(arg) - - def _eval_rewrite_as_pow(self, arg): - y = self.rewrite(cos).rewrite(pow) - if y.has(cos): - return None - return y - - def _eval_rewrite_as_sqrt(self, arg): - y = self.rewrite(cos).rewrite(sqrt) - if y.has(cos): - return None - return y - - def _eval_as_leading_term(self, x): - arg = self.args[0].as_leading_term(x) - - if x in arg.free_symbols and C.Order(1, x).contains(arg): - return arg - else: - return self.func(arg) - - def _eval_is_real(self): - return self.args[0].is_real - - def _eval_is_bounded(self): - arg = self.args[0] - - if arg.is_imaginary: - return True - - def _sage_(self): - import sage.all as sage - return sage.tan(self.args[0]._sage_()) - -
    -
    [docs]class cot(TrigonometricFunction): - """ - cot(x) -> Returns the cotangent of x (measured in radians) - """ - - nargs = 1 - - def fdiff(self, argindex=1): - if argindex == 1: - return S.NegativeOne - self**2 - else: - raise ArgumentIndexError(self, argindex) - -
    [docs] def inverse(self, argindex=1): - """ - Return the inverse of this function. - """ - return acot -
    - @classmethod - def eval(cls, arg): - if arg.is_Number: - if arg is S.NaN: - return S.NaN - if arg is S.Zero: - return S.ComplexInfinity - - if arg.could_extract_minus_sign(): - return -cls(-arg) - - i_coeff = arg.as_coefficient(S.ImaginaryUnit) - if i_coeff is not None: - return -S.ImaginaryUnit * C.coth(i_coeff) - - pi_coeff = _pi_coeff(arg, 2) - if pi_coeff is not None: - if pi_coeff.is_integer: - return S.ComplexInfinity - - if not pi_coeff.is_Rational: - narg = pi_coeff*S.Pi - if narg != arg: - return cls(narg) - return None - - if pi_coeff.is_Rational: - narg = (((pi_coeff + S.Half) % 1) - S.Half)*S.Pi - # see cos() to specify which expressions should be - # expanded automatically in terms of radicals - cresult, sresult = cos(narg), cos(narg - S.Pi/2) - if not isinstance(cresult, cos) \ - and not isinstance(sresult, cos): - if sresult == 0: - return S.ComplexInfinity - return cresult / sresult - if narg != arg: - return cls(narg) - - if arg.is_Add: - x, m = _peeloff_pi(arg) - if m: - cotm = cot(m) - if cotm == 0: - return -tan(x) - cotx = cot(x) - if cotm is S.ComplexInfinity: - return cotx - if cotm.is_Rational: - return (cotm*cotx - 1) / (cotm + cotx) - return None - - if arg.func is acot: - return arg.args[0] - - if arg.func is atan: - x = arg.args[0] - return 1 / x - - if arg.func is atan2: - y, x = arg.args - return x/y - - if arg.func is asin: - x = arg.args[0] - return sqrt(1 - x**2) / x - - if arg.func is acos: - x = arg.args[0] - return x / sqrt(1 - x**2) - - @staticmethod - @cacheit - def taylor_term(n, x, *previous_terms): - if n == 0: - return 1 / sympify(x) - elif n < 0 or n % 2 == 0: - return S.Zero - else: - x = sympify(x) - - B = C.bernoulli(n + 1) - F = C.factorial(n + 1) - - return (-1)**((n + 1)//2) * 2**(n + 1) * B/F * x**n - - def _eval_nseries(self, x, n, logx): - i = self.args[0].limit(x, 0)/S.Pi - if i and i.is_Integer: - return self.rewrite(cos)._eval_nseries(x, n=n, logx=logx) - return Function._eval_nseries(self, x, n=n, logx=logx) - - def _eval_conjugate(self): - assert len(self.args) == 1 - return self.func(self.args[0].conjugate()) - - def as_real_imag(self, deep=True, **hints): - if self.args[0].is_real: - if deep: - hints['complex'] = False - return (self.expand(deep, **hints), S.Zero) - else: - return (self, S.Zero) - if deep: - re, im = self.args[0].expand(deep, **hints).as_real_imag() - else: - re, im = self.args[0].as_real_imag() - denom = sin(re)**2 + C.sinh(im)**2 - return (sin(re)*cos(re)/denom, -C.sinh(im)*C.cosh(im)/denom) - - def _eval_rewrite_as_exp(self, arg): - exp, I = C.exp, S.ImaginaryUnit - if isinstance(arg, TrigonometricFunction) or isinstance(arg, HyperbolicFunction): - arg = arg.func(arg.args[0]).rewrite(exp) - neg_exp, pos_exp = exp(-arg*I), exp(arg*I) - return I*(pos_exp + neg_exp)/(pos_exp - neg_exp) - - def _eval_rewrite_as_Pow(self, arg): - if arg.func is log: - I = S.ImaginaryUnit - x = arg.args[0] - return -I*(x**-I + x**I)/(x**-I - x**I) - - def _eval_rewrite_as_sin(self, x): - return 2*sin(2*x)/sin(x)**2 - - def _eval_rewrite_as_cos(self, x): - return -cos(x)/cos(x + S.Pi/2) - - def _eval_rewrite_as_sincos(self, arg): - return cos(arg)/sin(arg) - - def _eval_rewrite_as_tan(self, arg): - return 1/tan(arg) - - def _eval_rewrite_as_pow(self, arg): - y = self.rewrite(cos).rewrite(pow) - if y.has(cos): - return None - return y - - def _eval_rewrite_as_sqrt(self, arg): - y = self.rewrite(cos).rewrite(sqrt) - if y.has(cos): - return None - return y - - def _eval_as_leading_term(self, x): - arg = self.args[0].as_leading_term(x) - - if x in arg.free_symbols and C.Order(1, x).contains(arg): - return 1/arg - else: - return self.func(arg) - - def _eval_is_real(self): - return self.args[0].is_real - - def _eval_expand_trig(self, **hints): - arg = self.args[0] - x = None - if arg.is_Add: - from sympy import symmetric_poly - n = len(arg.args) - CX = [] - for x in arg.args: - cx = cot(x, evaluate=False)._eval_expand_trig() - CX.append(cx) - - Yg = numbered_symbols('Y') - Y = [ next(Yg) for i in range(n) ] - - p = [0, 0] - for i in range(n, -1, -1): - p[(n - i) % 2] += symmetric_poly(i, Y)*(-1)**(((n - i) % 4)//2) - return (p[0]/p[1]).subs(list(zip(Y, CX))) - else: - coeff, terms = arg.as_coeff_Mul(rational=True) - if coeff.is_Integer and coeff > 1: - I = S.ImaginaryUnit - z = C.Symbol('dummy', real=True) - P = ((z + I)**coeff).expand() - return (C.re(P)/C.im(P)).subs([(z, cot(terms))]) - return cot(arg) - - def _sage_(self): - import sage.all as sage - return sage.cot(self.args[0]._sage_()) - -############################################################################### -########################### TRIGONOMETRIC INVERSES ############################ -############################################################################### - -
    -
    [docs]class asin(Function): - """ - asin(x) -> Returns the arc sine of x (measured in radians) - - Notes - ===== - - * asin(x) will evaluate automatically in the cases - oo, -oo, 0, 1, -1 - - Examples - ======== - - >>> from sympy import asin, oo, pi - >>> asin(1) - pi/2 - >>> asin(-1) - -pi/2 - - See Also - ======== - - acos, atan, sin - """ - - nargs = 1 - - def fdiff(self, argindex=1): - if argindex == 1: - return 1/sqrt(1 - self.args[0]**2) - else: - raise ArgumentIndexError(self, argindex) - - @classmethod - def eval(cls, arg): - if arg.is_Number: - if arg is S.NaN: - return S.NaN - elif arg is S.Infinity: - return S.NegativeInfinity * S.ImaginaryUnit - elif arg is S.NegativeInfinity: - return S.Infinity * S.ImaginaryUnit - elif arg is S.Zero: - return S.Zero - elif arg is S.One: - return S.Pi / 2 - elif arg is S.NegativeOne: - return -S.Pi / 2 - - if arg.could_extract_minus_sign(): - return -cls(-arg) - - if arg.is_number: - cst_table = { - sqrt(3)/2: 3, - -sqrt(3)/2: -3, - sqrt(2)/2: 4, - -sqrt(2)/2: -4, - 1/sqrt(2): 4, - -1/sqrt(2): -4, - sqrt((5 - sqrt(5))/8): 5, - -sqrt((5 - sqrt(5))/8): -5, - S.Half: 6, - -S.Half: -6, - sqrt(2 - sqrt(2))/2: 8, - -sqrt(2 - sqrt(2))/2: -8, - (sqrt(5) - 1)/4: 10, - (1 - sqrt(5))/4: -10, - (sqrt(3) - 1)/sqrt(2**3): 12, - (1 - sqrt(3))/sqrt(2**3): -12, - (sqrt(5) + 1)/4: S(10)/3, - -(sqrt(5) + 1)/4: -S(10)/3 - } - - if arg in cst_table: - return S.Pi / cst_table[arg] - - i_coeff = arg.as_coefficient(S.ImaginaryUnit) - if i_coeff is not None: - return S.ImaginaryUnit * C.asinh(i_coeff) - - @staticmethod - @cacheit - def taylor_term(n, x, *previous_terms): - if n < 0 or n % 2 == 0: - return S.Zero - else: - x = sympify(x) - if len(previous_terms) >= 2 and n > 2: - p = previous_terms[-2] - return p * (n - 2)**2/(n*(n - 1)) * x**2 - else: - k = (n - 1) // 2 - R = C.RisingFactorial(S.Half, k) - F = C.factorial(k) - return R / F * x**n / n - - def _eval_as_leading_term(self, x): - arg = self.args[0].as_leading_term(x) - - if x in arg.free_symbols and C.Order(1, x).contains(arg): - return arg - else: - return self.func(arg) - - def _eval_rewrite_as_acos(self, x): - return S.Pi/2 - acos(x) - - def _eval_rewrite_as_atan(self, x): - return 2*atan(x/(1 + sqrt(1 - x**2))) - - def _eval_rewrite_as_log(self, x): - return -S.ImaginaryUnit*C.log(S.ImaginaryUnit*x + sqrt(1 - x**2)) - - def _eval_is_real(self): - return self.args[0].is_real and (self.args[0] >= -1 and self.args[0] <= 1) - - def _sage_(self): - import sage.all as sage - return sage.asin(self.args[0]._sage_()) - -
    -
    [docs]class acos(Function): - """ - acos(x) -> Returns the arc cosine of x (measured in radians) - - Notes - ===== - - * acos(x) will evaluate automatically in the cases - oo, -oo, 0, 1, -1 - - Examples - ======== - - >>> from sympy import acos, oo, pi - >>> acos(1) - 0 - >>> acos(0) - pi/2 - >>> acos(oo) - oo*I - - See Also - ======== - - asin, atan, cos - """ - - nargs = 1 - - def fdiff(self, argindex=1): - if argindex == 1: - return -1/sqrt(1 - self.args[0]**2) - else: - raise ArgumentIndexError(self, argindex) - - @classmethod - def eval(cls, arg): - if arg.is_Number: - if arg is S.NaN: - return S.NaN - elif arg is S.Infinity: - return S.Infinity * S.ImaginaryUnit - elif arg is S.NegativeInfinity: - return S.NegativeInfinity * S.ImaginaryUnit - elif arg is S.Zero: - return S.Pi / 2 - elif arg is S.One: - return S.Zero - elif arg is S.NegativeOne: - return S.Pi - - if arg.is_number: - cst_table = { - S.Half: S.Pi/3, - -S.Half: 2*S.Pi/3, - sqrt(2)/2: S.Pi/4, - -sqrt(2)/2: 3*S.Pi/4, - 1/sqrt(2): S.Pi/4, - -1/sqrt(2): 3*S.Pi/4, - sqrt(3)/2: S.Pi/6, - -sqrt(3)/2: 5*S.Pi/6, - } - - if arg in cst_table: - return cst_table[arg] - - @staticmethod - @cacheit - def taylor_term(n, x, *previous_terms): - if n == 0: - return S.Pi / 2 - elif n < 0 or n % 2 == 0: - return S.Zero - else: - x = sympify(x) - if len(previous_terms) >= 2 and n > 2: - p = previous_terms[-2] - return p * (n - 2)**2/(n*(n - 1)) * x**2 - else: - k = (n - 1) // 2 - R = C.RisingFactorial(S.Half, k) - F = C.factorial(k) - return -R / F * x**n / n - - def _eval_as_leading_term(self, x): - arg = self.args[0].as_leading_term(x) - - if x in arg.free_symbols and C.Order(1, x).contains(arg): - return arg - else: - return self.func(arg) - - def _eval_is_real(self): - return self.args[0].is_real and (self.args[0] >= -1 and self.args[0] <= 1) - - def _eval_rewrite_as_log(self, x): - return S.Pi/2 + S.ImaginaryUnit * C.log(S.ImaginaryUnit * x + sqrt(1 - x**2)) - - def _eval_rewrite_as_asin(self, x): - return S.Pi/2 - asin(x) - - def _eval_rewrite_as_atan(self, x): - if x > -1 and x <= 1: - return 2 * atan(sqrt(1 - x**2)/(1 + x)) - else: - raise ValueError( - "The argument must be bounded in the interval (-1,1]") - - def _sage_(self): - import sage.all as sage - return sage.acos(self.args[0]._sage_()) - -
    -
    [docs]class atan(Function): - """ - atan(x) -> Returns the arc tangent of x (measured in radians) - - Notes - ===== - - * atan(x) will evaluate automatically in the cases - oo, -oo, 0, 1, -1 - - Examples - ======== - - >>> from sympy import atan, oo, pi - >>> atan(0) - 0 - >>> atan(1) - pi/4 - >>> atan(oo) - pi/2 - - See Also - ======== - - acos, asin, tan - """ - - nargs = 1 - - def fdiff(self, argindex=1): - if argindex == 1: - return 1/(1 + self.args[0]**2) - else: - raise ArgumentIndexError(self, argindex) - - @classmethod - def eval(cls, arg): - if arg.is_Number: - if arg is S.NaN: - return S.NaN - elif arg is S.Infinity: - return S.Pi / 2 - elif arg is S.NegativeInfinity: - return -S.Pi / 2 - elif arg is S.Zero: - return S.Zero - elif arg is S.One: - return S.Pi / 4 - elif arg is S.NegativeOne: - return -S.Pi / 4 - if arg.could_extract_minus_sign(): - return -cls(-arg) - - if arg.is_number: - cst_table = { - sqrt(3)/3: 6, - -sqrt(3)/3: -6, - 1/sqrt(3): 6, - -1/sqrt(3): -6, - sqrt(3): 3, - -sqrt(3): -3, - (1 + sqrt(2)): S(8)/3, - -(1 + sqrt(2)): S(8)/3, - (sqrt(2) - 1): 8, - (1 - sqrt(2)): -8, - sqrt((5 + 2*sqrt(5))): S(5)/2, - -sqrt((5 + 2*sqrt(5))): -S(5)/2, - (2 - sqrt(3)): 12, - -(2 - sqrt(3)): -12 - } - - if arg in cst_table: - return S.Pi / cst_table[arg] - - i_coeff = arg.as_coefficient(S.ImaginaryUnit) - if i_coeff is not None: - return S.ImaginaryUnit * C.atanh(i_coeff) - - @staticmethod - @cacheit - def taylor_term(n, x, *previous_terms): - if n < 0 or n % 2 == 0: - return S.Zero - else: - x = sympify(x) - return (-1)**((n - 1)//2) * x**n / n - - def _eval_as_leading_term(self, x): - arg = self.args[0].as_leading_term(x) - - if x in arg.free_symbols and C.Order(1, x).contains(arg): - return arg - else: - return self.func(arg) - - def _eval_is_real(self): - return self.args[0].is_real - - def _eval_rewrite_as_log(self, x): - return S.ImaginaryUnit/2 * (C.log( - (S(1) - S.ImaginaryUnit * x)/(S(1) + S.ImaginaryUnit * x))) - - def _eval_aseries(self, n, args0, x, logx): - if args0[0] == S.Infinity: - return S.Pi/2 - atan(1/self.args[0]) - elif args0[0] == S.NegativeInfinity: - return -S.Pi/2 - atan(1/self.args[0]) - else: - return super(atan, self)._eval_aseries(n, args0, x, logx) - - def _sage_(self): - import sage.all as sage - return sage.atan(self.args[0]._sage_()) - -
    -
    [docs]class acot(Function): - """ - acot(x) -> Returns the arc cotangent of x (measured in radians) - """ - - nargs = 1 - - def fdiff(self, argindex=1): - if argindex == 1: - return -1 / (1 + self.args[0]**2) - else: - raise ArgumentIndexError(self, argindex) - - @classmethod - def eval(cls, arg): - if arg.is_Number: - if arg is S.NaN: - return S.NaN - elif arg is S.Infinity: - return S.Zero - elif arg is S.NegativeInfinity: - return S.Zero - elif arg is S.Zero: - return S.Pi/ 2 - elif arg is S.One: - return S.Pi / 4 - elif arg is S.NegativeOne: - return -S.Pi / 4 - - if arg.could_extract_minus_sign(): - return -cls(-arg) - - if arg.is_number: - cst_table = { - sqrt(3)/3: 3, - -sqrt(3)/3: -3, - 1/sqrt(3): 3, - -1/sqrt(3): -3, - sqrt(3): 6, - -sqrt(3): -6, - (1 + sqrt(2)): 8, - -(1 + sqrt(2)): -8, - (1 - sqrt(2)): -S(8)/3, - (sqrt(2) - 1): S(8)/3, - sqrt(5 + 2*sqrt(5)): 10, - -sqrt(5 + 2*sqrt(5)): -10, - (2 + sqrt(3)): 12, - -(2 + sqrt(3)): -12, - (2 - sqrt(3)): S(12)/5, - -(2 - sqrt(3)): -S(12)/5, - } - - if arg in cst_table: - return S.Pi / cst_table[arg] - - i_coeff = arg.as_coefficient(S.ImaginaryUnit) - if i_coeff is not None: - return -S.ImaginaryUnit * C.acoth(i_coeff) - - @staticmethod - @cacheit - def taylor_term(n, x, *previous_terms): - if n == 0: - return S.Pi / 2 # FIX THIS - elif n < 0 or n % 2 == 0: - return S.Zero - else: - x = sympify(x) - return (-1)**((n + 1)//2) * x**n / n - - def _eval_as_leading_term(self, x): - arg = self.args[0].as_leading_term(x) - - if x in arg.free_symbols and C.Order(1, x).contains(arg): - return arg - else: - return self.func(arg) - - def _eval_is_real(self): - return self.args[0].is_real - - def _eval_aseries(self, n, args0, x, logx): - if args0[0] == S.Infinity: - return S.Pi/2 - acot(1/self.args[0]) - elif args0[0] == S.NegativeInfinity: - return 3*S.Pi/2 - acot(1/self.args[0]) - else: - return super(atan, self)._eval_aseries(n, args0, x, logx) - - def _sage_(self): - import sage.all as sage - return sage.acot(self.args[0]._sage_()) - - def _eval_rewrite_as_log(self, x): - return S.ImaginaryUnit/2 * \ - (C.log((x - S.ImaginaryUnit)/(x + S.ImaginaryUnit))) - -
    -
    [docs]class atan2(Function): - """ - atan2(y,x) -> Returns the atan(y/x) taking two arguments y and x. - Signs of both y and x are considered to determine the appropriate - quadrant of atan(y/x). The range is (-pi, pi]. - """ - - nargs = 2 - - @classmethod - def eval(cls, y, x): - sign_y = C.sign(y) - - if y.is_zero: - if x.is_positive: - return S.Zero - elif x.is_zero: - return S.NaN - elif x.is_negative: - return S.Pi - elif x.is_zero: - if sign_y.is_Number: - return sign_y * S.Pi/2 - elif x.is_zero is False: - abs_yx = C.Abs(y/x) - if sign_y.is_Number and abs_yx.is_number: - phi = C.atan(abs_yx) - if x.is_positive: - return sign_y * phi - else: - return sign_y * (S.Pi - phi) - - def _eval_is_real(self): - return self.args[0].is_real and self.args[1].is_real - - def fdiff(self, argindex): - x, y = self.args - if argindex == 1: - return y/(x**2 + y**2) - elif argindex == 2: - return -x/(x**2 + y**2) - else: - raise ArgumentIndexError(self, argindex) - - def _sage_(self): - import sage.all as sage - return sage.atan2(self.args[0]._sage_(), self.args[1]._sage_())
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/functions/special/bessel.html b/dev-py3k/_modules/sympy/functions/special/bessel.html deleted file mode 100644 index a372d21ff5e..00000000000 --- a/dev-py3k/_modules/sympy/functions/special/bessel.html +++ /dev/null @@ -1,696 +0,0 @@ - - - - - - - - - - sympy.functions.special.bessel — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.functions.special.bessel

    -"""Bessel type functions"""
    -
    -from sympy import S, pi, I
    -from sympy.core.function import Function, ArgumentIndexError
    -from sympy.functions.elementary.trigonometric import sin, cos
    -from sympy.functions.elementary.miscellaneous import sqrt
    -
    -# TODO
    -# o Airy Ai and Bi functions
    -# o Scorer functions G1 and G2
    -# o Asymptotic expansions
    -#   These are possible, e.g. for fixed order, but since the bessel type
    -#   functions are oscillatory they are not actually tractable at
    -#   infinity, so this is not particularly useful right now.
    -# o Series Expansions for functions of the second kind about zero
    -# o Nicer series expansions.
    -# o More rewriting.
    -# o Add solvers to ode.py (or rather add solvers for the hypergeometric equation).
    -
    -
    -
    [docs]class BesselBase(Function): - """ - Abstract base class for bessel-type functions. - - This class is meant to reduce code duplication. - All bessel type functions can 1) be differentiated, and the derivatives - expressed in terms of similar functions and 2) be rewritten in terms - of other bessel-type functions. - - Here "bessel-type functions" are assumed to have one complex parameter. - - To use this base class, define class attributes ``_a`` and ``_b`` such that - ``2*F_n' = -_a*F_{n+1} b*F_{n-1}``. - """ - - nargs = 2 - - @property -
    [docs] def order(self): - """ The order of the bessel-type function. """ - return self.args[0] -
    - @property -
    [docs] def argument(self): - """ The argument of the bessel-type function. """ - return self.args[1] -
    - def fdiff(self, argindex=2): - if argindex != 2: - raise ArgumentIndexError(self, argindex) - return self._b/2 * self.__class__(self.order - 1, self.argument) \ - - self._a/2 * self.__class__(self.order + 1, self.argument) \ - - -
    -
    [docs]class besselj(BesselBase): - r""" - Bessel function of the first kind. - - The Bessel J function of order :math:`\nu` is defined to be the function - satisfying Bessel's differential equation - - .. math :: - z^2 \frac{\mathrm{d}^2 w}{\mathrm{d}z^2} - + z \frac{\mathrm{d}w}{\mathrm{d}z} + (z^2 - \nu^2) w = 0, - - with Laurent expansion - - .. math :: - J_\nu(z) = z^\nu \left(\frac{1}{\Gamma(\nu + 1) 2^\nu} + O(z^2) \right), - - if :math:`\nu` is not a negative integer. If :math:`\nu=-n \in \mathbb{Z}_{<0}` - *is* a negative integer, then the definition is - - .. math :: - J_{-n}(z) = (-1)^n J_n(z). - - Examples - ======== - - Create a bessel function object: - - >>> from sympy import besselj, jn - >>> from sympy.abc import z, n - >>> b = besselj(n, z) - - Differentiate it: - - >>> b.diff(z) - besselj(n - 1, z)/2 - besselj(n + 1, z)/2 - - Rewrite in terms of spherical bessel functions: - - >>> b.rewrite(jn) - sqrt(2)*sqrt(z)*jn(n - 1/2, z)/sqrt(pi) - - Access the parameter and argument: - - >>> b.order - n - >>> b.argument - z - - See Also - ======== - - bessely, besseli, besselk - - - References - ========== - - - Abramowitz, Milton; Stegun, Irene A., eds. (1965), "Chapter 9", - Handbook of Mathematical Functions with Formulas, Graphs, and Mathematical - Tables - - Luke, Y. L. (1969), The Special Functions and Their Approximations, - Volume 1 - - http://en.wikipedia.org/wiki/Bessel_function - """ - - _a = S.One - _b = S.One - - def _eval_rewrite_as_jn(self, nu, z, expand=False): - jn_part = jn(nu - S('1/2'), self.argument) - if expand: - jn_part = jn_part._eval_expand_func() - return sqrt(2*z/pi) * jn_part - - @classmethod - def eval(cls, nu, z): - if nu.is_Integer: - if nu < 0: - return S(-1)**nu*besselj(-nu, z) - if z.could_extract_minus_sign(): - return S(-1)**nu*besselj(nu, -z) - newz = z.extract_multiplicatively(I) - if newz: # NOTE we don't want to change the function if z==0 - return I**(nu)*besseli(nu, newz) - - # branch handling: - from sympy import unpolarify, exp - if nu.is_integer: - newz = unpolarify(z) - if newz != z: - return besselj(nu, newz) - else: - newz, n = z.extract_branch_factor() - if n != 0: - return exp(2*n*pi*nu*I)*besselj(nu, newz) - nnu = unpolarify(nu) - if nu != nnu: - return besselj(nnu, z) - - def _eval_expand_func(self, **hints): - if self.order.is_Rational and self.order.q == 2: - return self._eval_rewrite_as_jn(*self.args, **{'expand': True}) - return self - - def _eval_rewrite_as_besseli(self, nu, z): - from sympy import polar_lift, exp - return exp(I*pi*nu/2)*besseli(nu, polar_lift(-I)*z) - -
    -
    [docs]class bessely(BesselBase): - r""" - Bessel function of the second kind. - - The Bessel Y function of order :math:`\nu` is defined as - - .. math :: - Y_\nu(z) = \lim_{\mu \to \nu} \frac{J_\mu(z) \cos(\pi \mu) - - J_{-\mu}(z)}{\sin(\pi \mu)}, - - where :math:`J_\mu(z)` is the Bessel function of the first kind. - - It is a solution to Bessel's equation, and linearly independent from - :math:`J_\nu`. - - Examples - ======== - - >>> from sympy import bessely, yn - >>> from sympy.abc import z, n - >>> b = bessely(n, z) - >>> b.diff(z) - bessely(n - 1, z)/2 - bessely(n + 1, z)/2 - >>> b.rewrite(yn) - sqrt(2)*sqrt(z)*yn(n - 1/2, z)/sqrt(pi) - - See Also - ======== - - besselj, besseli, besselk - - """ - - _a = S.One - _b = S.One - - def _eval_rewrite_as_yn(self, nu, z, expand=False): - yn_part = yn(nu - S('1/2'), self.argument) - if expand: - yn_part = yn_part._eval_expand_func() - return sqrt(2*z/pi) * yn_part - - @classmethod - def eval(cls, nu, z): - if nu.is_Integer: - if nu < 0: - return S(-1)**nu*bessely(-nu, z) - - def _eval_expand_func(self, **hints): - if self.order.is_Rational and self.order.q == 2: - return self._eval_rewrite_as_yn(*self.args, **{'expand': True}) - return self - -
    -
    [docs]class besseli(BesselBase): - r""" - Modified Bessel function of the first kind. - - The Bessel I function is a solution to the modified Bessel equation - - .. math :: - z^2 \frac{\mathrm{d}^2 w}{\mathrm{d}z^2} - + z \frac{\mathrm{d}w}{\mathrm{d}z} + (z^2 + \nu^2)^2 w = 0. - - It can be defined as - - .. math :: - I_\nu(z) = i^{-\nu} J_\nu(iz), - - where :math:`J_\mu(z)` is the Bessel function of the first kind. - - Examples - ======== - - >>> from sympy import besseli - >>> from sympy.abc import z, n - >>> besseli(n, z).diff(z) - besseli(n - 1, z)/2 + besseli(n + 1, z)/2 - - See Also - ======== - - besselj, bessely, besselk - - """ - - _a = -S.One - _b = S.One - - @classmethod - def eval(cls, nu, z): - if nu.is_Integer: - newz = z.extract_multiplicatively(I) - if newz: # NOTE we don't want to change the function if z==0 - return I**(-nu)*besselj(nu, -newz) - - # branch handling: - from sympy import unpolarify, exp - if nu.is_integer: - newz = unpolarify(z) - if newz != z: - return besseli(nu, newz) - else: - newz, n = z.extract_branch_factor() - if n != 0: - return exp(2*n*pi*nu*I)*besseli(nu, newz) - nnu = unpolarify(nu) - if nu != nnu: - return besseli(nnu, z) - - def _eval_rewrite_as_besselj(self, nu, z): - from sympy import polar_lift, exp - return exp(-I*pi*nu/2)*besselj(nu, polar_lift(I)*z) - -
    -
    [docs]class besselk(BesselBase): - r""" - Modified Bessel function of the second kind. - - The Bessel K function of order :math:`\nu` is defined as - - .. math :: - K_\nu(z) = \lim_{\mu \to \nu} \frac{\pi}{2} - \frac{I_{-\mu}(z) -I_\mu(z)}{\sin(\pi \mu)}, - - where :math:`I_\mu(z)` is the modified Bessel function of the first kind. - - It is a solution of the modified Bessel equation, and linearly independent - from :math:`Y_\nu`. - - Examples - ======== - - >>> from sympy import besselk - >>> from sympy.abc import z, n - >>> besselk(n, z).diff(z) - -besselk(n - 1, z)/2 - besselk(n + 1, z)/2 - - See Also - ======== - - besselj, besseli, bessely - - """ - - _a = S.One - _b = -S.One - -
    -
    [docs]class hankel1(BesselBase): - r""" - Hankel function of the first kind. - - This function is defined as - - .. math :: - H_\nu^{(1)} = J_\nu(z) + iY_\nu(z), - - where :math:`J_\nu(z)` is the Bessel function of the first kind, and - :math:`Y_\nu(z)` is the Bessel function of the second kind. - - It is a solution to Bessel's equation. - - Examples - ======== - - >>> from sympy import hankel1 - >>> from sympy.abc import z, n - >>> hankel1(n, z).diff(z) - hankel1(n - 1, z)/2 - hankel1(n + 1, z)/2 - - See Also - ======== - - hankel2, besselj, bessely - - """ - - _a = S.One - _b = S.One - -
    -
    [docs]class hankel2(BesselBase): - r""" - Hankel function of the second kind. - - This function is defined as - - .. math :: - H_\nu^{(2)} = J_\nu(z) - iY_\nu(z), - - where :math:`J_\nu(z)` is the Bessel function of the first kind, and - :math:`Y_\nu(z)` is the Bessel function of the second kind. - - It is a solution to Bessel's equation, and linearly independent from - :math:`H_\nu^{(1)}`. - - Examples - ======== - - >>> from sympy import hankel2 - >>> from sympy.abc import z, n - >>> hankel2(n, z).diff(z) - hankel2(n - 1, z)/2 - hankel2(n + 1, z)/2 - - See Also - ======== - - hankel1, besselj, bessely - - """ - - _a = S.One - _b = S.One -
    -from sympy.polys.orthopolys import spherical_bessel_fn as fn - - -class SphericalBesselBase(BesselBase): - """ - Base class for spherical bessel functions. - - These are thin wrappers around ordinary bessel functions, - since spherical bessel functions differ from the ordinary - ones just by a slight change in order. - - To use this class, define the _rewrite and _expand methods. - """ - - def _expand(self, **hints): - """ Expand self into a polynomial. Nu is guaranteed to be Integer. """ - raise NotImplementedError('expansion') - - def _rewrite(self): - """ Rewrite self in terms of ordinary bessel functions. """ - raise NotImplementedError('rewriting') - - def _eval_expand_func(self, **hints): - if self.order.is_Integer: - return self._expand(**hints) - else: - return self - - def _eval_evalf(self, prec): - return self._rewrite()._eval_evalf(prec) - - def fdiff(self, argindex=2): - if argindex != 2: - raise ArgumentIndexError(self, argindex) - return self.__class__(self.order - 1, self.argument) - \ - self * (self.order + 1)/self.argument - - -
    [docs]class jn(SphericalBesselBase): - r""" - Spherical Bessel function of the first kind. - - This function is a solution to the spherical bessel equation - - .. math :: - z^2 \frac{\mathrm{d}^2 w}{\mathrm{d}z^2} - + 2z \frac{\mathrm{d}w}{\mathrm{d}z} + (z^2 - \nu(\nu + 1)) w = 0. - - It can be defined as - - .. math :: - j_\nu(z) = \sqrt{\frac{\pi}{2z}} J_{\nu + \frac{1}{2}}(z), - - where :math:`J_\nu(z)` is the Bessel function of the first kind. - - Examples - ======== - - >>> from sympy import Symbol, jn, sin, cos, expand_func - >>> z = Symbol("z") - >>> print(jn(0, z).expand(func=True)) - sin(z)/z - >>> jn(1, z).expand(func=True) == sin(z)/z**2 - cos(z)/z - True - >>> expand_func(jn(3, z)) - (-6/z**2 + 15/z**4)*sin(z) + (1/z - 15/z**3)*cos(z) - - The spherical Bessel functions of integral order - are calculated using the formula: - - .. math:: j_n(z) = f_n(z) \sin{z} + (-1)^{n+1} f_{-n-1}(z) \cos{z}, - - where the coefficients :math:`f_n(z)` are available as - :func:`polys.orthopolys.spherical_bessel_fn`. - - See Also - ======== - - besselj, bessely, besselk, yn - - """ - - def _rewrite(self): - return self._eval_rewrite_as_besselj(self.order, self.argument) - - def _eval_rewrite_as_besselj(self, nu, z): - return sqrt(pi/(2*z)) * besselj(nu + S('1/2'), z) - - def _expand(self, **hints): - n = self.order - z = self.argument - return fn(n, z) * sin(z) + (-1)**(n + 1) * fn(-n - 1, z) * cos(z) - -
    -
    [docs]class yn(SphericalBesselBase): - r""" - Spherical Bessel function of the second kind. - - This function is another solution to the spherical bessel equation, and - linearly independent from :math:`j_n`. It can be defined as - - .. math :: - j_\nu(z) = \sqrt{\frac{\pi}{2z}} Y_{\nu + \frac{1}{2}}(z), - - where :math:`Y_\nu(z)` is the Bessel function of the second kind. - - Examples - ======== - - >>> from sympy import Symbol, yn, sin, cos, expand_func - >>> z = Symbol("z") - >>> print(expand_func(yn(0, z))) - -cos(z)/z - >>> expand_func(yn(1, z)) == -cos(z)/z**2-sin(z)/z - True - - For integral orders :math:`n`, :math:`y_n` is calculated using the formula: - - .. math:: y_n(z) = (-1)^{n+1} j_{-n-1}(z) - - See Also - ======== - - besselj, bessely, besselk, jn - - """ - - def _rewrite(self): - return self._eval_rewrite_as_bessely(self.order, self.argument) - - def _eval_rewrite_as_bessely(self, nu, z): - return sqrt(pi/(2*z)) * bessely(nu + S('1/2'), z) - - def _expand(self, **hints): - n = self.order - z = self.argument - return (-1)**(n + 1) * \ - (fn(-n - 1, z) * sin(z) + (-1)**(-n) * fn(n, z) * cos(z)) - -
    -
    [docs]def jn_zeros(n, k, method="sympy", dps=15): - """ - Zeros of the spherical Bessel function of the first kind. - - This returns an array of zeros of jn up to the k-th zero. - - * method = "sympy": uses mpmath besseljzero - * method = "scipy": uses the SciPy's sph_jn and newton to find all - roots, which is faster than computing the zeros using a general - numerical solver, but it requires SciPy and only works with low - precision floating point numbers. [the function used with - method="sympy" is a recent addition to mpmath, before that a general - solver was used] - - Examples - ======== - - >>> from sympy import jn_zeros - >>> jn_zeros(2, 4, dps=5) - [5.7635, 9.095, 12.323, 15.515] - - See Also - ======== - - jn, yn, besselj, besselk, bessely - """ - from math import pi - - if method == "sympy": - from sympy.mpmath import besseljzero - from sympy.mpmath.libmp.libmpf import dps_to_prec - from sympy import Expr - prec = dps_to_prec(dps) - return [Expr._from_mpmath(besseljzero(S(n + 0.5)._to_mpmath(prec), - int(k)), prec) - for k in range(1, k + 1)] - elif method == "scipy": - from scipy.special import sph_jn - from scipy.optimize import newton - f = lambda x: sph_jn(n, x)[0][-1] - else: - raise NotImplementedError("Unknown method.") - - def solver(f, x): - if method == "scipy": - root = newton(f, x) - else: - raise NotImplementedError("Unknown method.") - return root - - # we need to approximate the position of the first root: - root = n + pi - # determine the first root exactly: - root = solver(f, root) - roots = [root] - for i in range(k - 1): - # estimate the position of the next root using the last root + pi: - root = solver(f, root + pi) - roots.append(root) - return roots
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/functions/special/bsplines.html b/dev-py3k/_modules/sympy/functions/special/bsplines.html deleted file mode 100644 index 53498205002..00000000000 --- a/dev-py3k/_modules/sympy/functions/special/bsplines.html +++ /dev/null @@ -1,267 +0,0 @@ - - - - - - - - - - sympy.functions.special.bsplines — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.functions.special.bsplines

    -from sympy.core import S, sympify, expand
    -from sympy.functions import Piecewise, piecewise_fold
    -from sympy.functions.elementary.piecewise import ExprCondPair
    -from sympy.core.sets import Interval
    -
    -
    -def _add_splines(c, b1, d, b2):
    -    """Construct c*b1 + d*b2."""
    -    if b1 == S.Zero or c == S.Zero:
    -        return expand(piecewise_fold(d*b2))
    -    if b2 == S.Zero or d == S.Zero:
    -        return expand(piecewise_fold(c*b1))
    -    new_args = []
    -    n_intervals = len(b1.args)
    -    assert(n_intervals == len(b2.args))
    -    new_args.append((expand(c*b1.args[0].expr), b1.args[0].cond))
    -    for i in range(1, n_intervals - 1):
    -        new_args.append((
    -            expand(c*b1.args[i].expr + d*b2.args[i - 1].expr),
    -            b1.args[i].cond
    -        ))
    -    new_args.append((expand(d*b2.args[-2].expr), b2.args[-2].cond))
    -    new_args.append(b2.args[-1])
    -    return Piecewise(*new_args)
    -
    -
    -
    [docs]def bspline_basis(d, knots, n, x, close=True): - """The n-th B-spline at x of degree d with knots. - - B-Splines are piecewise polynomials of degree d [1]. They are defined on - a set of knots, which is a sequence of integers or floats. - - The 0th degree splines have a value of one on a single interval: - - >>> from sympy import bspline_basis - >>> from sympy.abc import x - >>> d = 0 - >>> knots = list(range(5)) - >>> bspline_basis(d, knots, 0, x) - Piecewise((1, And(x <= 1, x >= 0)), (0, True)) - - For a given (d, knots) there are len(knots)-d-1 B-splines defined, that - are indexed by n (starting at 0). - - Here is an example of a cubic B-spline: - - >>> bspline_basis(3, list(range(5)), 0, x) - Piecewise((x**3/6, And(x < 1, x >= 0)), - (-x**3/2 + 2*x**2 - 2*x + 2/3, And(x < 2, x >= 1)), - (x**3/2 - 4*x**2 + 10*x - 22/3, And(x < 3, x >= 2)), - (-x**3/6 + 2*x**2 - 8*x + 32/3, And(x <= 4, x >= 3)), - (0, True)) - - By repeating knot points, you can introduce discontinuities in the - B-splines and their derivatives: - - >>> d = 1 - >>> knots = [0,0,2,3,4] - >>> bspline_basis(d, knots, 0, x) - Piecewise((-x/2 + 1, And(x <= 2, x >= 0)), (0, True)) - - It is quite time consuming to construct and evaluate B-splines. If you - need to evaluate a B-splines many times, it is best to lambdify them - first: - - >>> from sympy import lambdify - >>> d = 3 - >>> knots = list(range(10)) - >>> b0 = bspline_basis(d, knots, 0, x) - >>> f = lambdify(x, b0) - >>> y = f(0.5) - - See Also - ======== - - bsplines_basis_set - - References - ========== - - [1] http://en.wikipedia.org/wiki/B-spline - - """ - knots = [sympify(k) for k in knots] - d = int(d) - n = int(n) - n_knots = len(knots) - n_intervals = n_knots - 1 - if n + d + 1 > n_intervals: - raise ValueError('n+d+1 must not exceed len(knots)-1') - if d == 0: - result = Piecewise( - (S.One, Interval(knots[n], knots[n + 1], False, - not close).contains(x)), - (0, True) - ) - elif d > 0: - denom = knots[n + d + 1] - knots[n + 1] - if denom != S.Zero: - B = (knots[n + d + 1] - x)/denom - b2 = bspline_basis(d - 1, knots, n + 1, x, close) - else: - b2 = B = S.Zero - - denom = knots[n + d] - knots[n] - if denom != S.Zero: - A = (x - knots[n])/denom - b1 = bspline_basis( - d - 1, knots, n, x, close and (B == S.Zero or b2 == S.Zero)) - else: - b1 = A = S.Zero - - result = _add_splines(A, b1, B, b2) - else: - raise ValueError('degree must be non-negative: %r' % n) - return result - -
    -
    [docs]def bspline_basis_set(d, knots, x): - """Return the len(knots)-d-1 B-splines at x of degree d with knots. - - This function returns a list of Piecewise polynomials that are the - len(knots)-d-1 B-splines of degree d for the given knots. This function - calls bspline_basis(d, knots, n, x) for different values of n. - - Examples - ======== - - >>> from sympy import bspline_basis_set - >>> from sympy.abc import x - >>> d = 2 - >>> knots = list(range(5)) - >>> splines = bspline_basis_set(d, knots, x) - >>> splines - [Piecewise((x**2/2, And(x < 1, x >= 0)), - (-x**2 + 3*x - 3/2, And(x < 2, x >= 1)), - (x**2/2 - 3*x + 9/2, And(x <= 3, x >= 2)), - (0, True)), - Piecewise((x**2/2 - x + 1/2, And(x < 2, x >= 1)), - (-x**2 + 5*x - 11/2, And(x < 3, x >= 2)), - (x**2/2 - 4*x + 8, And(x <= 4, x >= 3)), - (0, True))] - - See Also - ======== - - bsplines_basis - """ - n_splines = len(knots) - d - 1 - return [bspline_basis(d, knots, i, x) for i in range(n_splines)]
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/functions/special/delta_functions.html b/dev-py3k/_modules/sympy/functions/special/delta_functions.html deleted file mode 100644 index f0905db5eae..00000000000 --- a/dev-py3k/_modules/sympy/functions/special/delta_functions.html +++ /dev/null @@ -1,347 +0,0 @@ - - - - - - - - - - sympy.functions.special.delta_functions — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.functions.special.delta_functions

    -from sympy.core import S, sympify, diff
    -from sympy.core.function import Function, ArgumentIndexError
    -from sympy.polys.polyerrors import PolynomialError
    -from sympy.functions.elementary.complexes import im
    -
    -###############################################################################
    -################################ DELTA FUNCTION ###############################
    -###############################################################################
    -
    -
    -
    [docs]class DiracDelta(Function): - """ - The DiracDelta function and its derivatives. - - DiracDelta function has the following properties: - - 1) ``diff(Heaviside(x),x) = DiracDelta(x)`` - 2) ``integrate(DiracDelta(x-a)*f(x),(x,-oo,oo)) = f(a)`` and - ``integrate(DiracDelta(x-a)*f(x),(x,a-e,a+e)) = f(a)`` - 3) ``DiracDelta(x) = 0`` for all ``x != 0`` - 4) ``DiracDelta(g(x)) = Sum_i(DiracDelta(x-x_i)/abs(g'(x_i)))`` - Where ``x_i``-s are the roots of ``g`` - - Derivatives of ``k``-th order of DiracDelta have the following property: - - 5) ``DiracDelta(x,k) = 0``, for all ``x != 0`` - - See Also - ======== - - Heaviside - simplify, is_simple - sympy.functions.special.tensor_functions.KroneckerDelta - - References - ========== - - http://mathworld.wolfram.com/DeltaFunction.html - """ - - nargs = (1, 2) - - is_real = True - - def fdiff(self, argindex=1): - if argindex == 1: - #I didn't know if there is a better way to handle default arguments - k = 0 - if len(self.args) > 1: - k = self.args[1] - return DiracDelta(self.args[0], k + 1) - else: - raise ArgumentIndexError(self, argindex) - - @classmethod - def eval(cls, arg, k=0): - k = sympify(k) - if not k.is_Integer or k.is_negative: - raise ValueError("Error: the second argument of DiracDelta must be \ - a non-negative integer, %s given instead." % (k,)) - arg = sympify(arg) - if arg is S.NaN: - return S.NaN - if arg.is_positive or arg.is_negative: - return S.Zero - -
    [docs] def simplify(self, x): - """simplify(self, x) - - Compute a simplified representation of the function using - property number 4. - - x can be: - - - a symbol - - Examples - ======== - - >>> from sympy import DiracDelta - >>> from sympy.abc import x, y - - >>> DiracDelta(x*y).simplify(x) - DiracDelta(x)/Abs(y) - >>> DiracDelta(x*y).simplify(y) - DiracDelta(y)/Abs(x) - - >>> DiracDelta(x**2 + x - 2).simplify(x) - DiracDelta(x - 1)/3 + DiracDelta(x + 2)/3 - - See Also - ======== - - is_simple, Directdelta - - """ - from sympy.polys.polyroots import roots - - if not self.args[0].has(x) or (len(self.args) > 1 and self.args[1] != 0 ): - return self - try: - argroots = roots(self.args[0], x, multiple=True) - result = 0 - valid = True - darg = diff(self.args[0], x) - for r in argroots: - #should I care about multiplicities of roots? - if r.is_real is not False and not darg.subs(x, r).is_zero: - result += DiracDelta(x - r)/abs(darg.subs(x, r)) - else: - valid = False - break - if valid: - return result - except PolynomialError: - pass - return self -
    -
    [docs] def is_simple(self, x): - """is_simple(self, x) - - Tells whether the argument(args[0]) of DiracDelta is a linear - expression in x. - - x can be: - - - a symbol - - Examples - ======== - - >>> from sympy import DiracDelta, cos - >>> from sympy.abc import x, y - - >>> DiracDelta(x*y).is_simple(x) - True - >>> DiracDelta(x*y).is_simple(y) - True - - >>> DiracDelta(x**2+x-2).is_simple(x) - False - - >>> DiracDelta(cos(x)).is_simple(x) - False - - See Also - ======== - - simplify, Directdelta - - """ - p = self.args[0].as_poly(x) - if p: - return p.degree() == 1 - return False - -############################################################################### -############################## HEAVISIDE FUNCTION ############################# -############################################################################### - -
    -
    [docs]class Heaviside(Function): - """Heaviside Piecewise function - - Heaviside function has the following properties [*]_: - - 1) ``diff(Heaviside(x),x) = DiracDelta(x)`` - ``( 0, if x < 0`` - 2) ``Heaviside(x) = < ( 1/2 if x==0 [*]`` - ``( 1, if x > 0`` - - .. [*] Regarding to the value at 0, Mathematica defines ``H(0) = 1``, - but Maple uses ``H(0) = undefined`` - - I think is better to have H(0) = 1/2, due to the following:: - - integrate(DiracDelta(x), x) = Heaviside(x) - integrate(DiracDelta(x), (x, -oo, oo)) = 1 - - and since DiracDelta is a symmetric function, - ``integrate(DiracDelta(x), (x, 0, oo))`` should be 1/2 (which is what - Maple returns). - - If we take ``Heaviside(0) = 1/2``, we would have - ``integrate(DiracDelta(x), (x, 0, oo)) = `` - ``Heaviside(oo) - Heaviside(0) = 1 - 1/2 = 1/2`` - and - ``integrate(DiracDelta(x), (x, -oo, 0)) = `` - ``Heaviside(0) - Heaviside(-oo) = 1/2 - 0 = 1/2`` - - If we consider, instead ``Heaviside(0) = 1``, we would have - ``integrate(DiracDelta(x), (x, 0, oo)) = Heaviside(oo) - Heaviside(0) = 0`` - and - ``integrate(DiracDelta(x), (x, -oo, 0)) = Heaviside(0) - Heaviside(-oo) = 1`` - - See Also - ======== - - DiracDelta - - References - ========== - - http://mathworld.wolfram.com/HeavisideStepFunction.html - - """ - nargs = 1 - - is_real = True - - def fdiff(self, argindex=1): - if argindex == 1: - # property number 1 - return DiracDelta(self.args[0]) - else: - raise ArgumentIndexError(self, argindex) - - @classmethod - def eval(cls, arg): - arg = sympify(arg) - if arg is S.NaN: - return S.NaN - elif im(arg).is_nonzero: - raise ValueError("Function defined only for Real Values. Complex part: %s found in %s ." % (repr(im(arg)), repr(arg)) ) - elif arg.is_negative: - return S.Zero - elif arg.is_zero: - return S.Half - elif arg.is_positive: - return S.One
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/functions/special/error_functions.html b/dev-py3k/_modules/sympy/functions/special/error_functions.html deleted file mode 100644 index d8ea1265c0e..00000000000 --- a/dev-py3k/_modules/sympy/functions/special/error_functions.html +++ /dev/null @@ -1,1297 +0,0 @@ - - - - - - - - - - sympy.functions.special.error_functions — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.functions.special.error_functions

    -""" This module contains various functions that are special cases
    -    of incomplete gamma functions. It should probably be renamed. """
    -
    -from sympy.core import Add, S, C, sympify, cacheit, pi, I
    -from sympy.core.function import Function, ArgumentIndexError
    -from sympy.functions.elementary.miscellaneous import sqrt, root
    -from sympy.functions.elementary.complexes import polar_lift
    -from sympy.functions.special.hyper import hyper, meijerg
    -
    -# TODO series expansions
    -# TODO see the "Note:" in Ei
    -
    -###############################################################################
    -################################ ERROR FUNCTION ###############################
    -###############################################################################
    -
    -
    -
    [docs]class erf(Function): - """ - The Gauss error function. - - This function is defined as: - - :math:`\\mathrm{erf}(x)=\\frac{2}{\\sqrt{\\pi}} \\int_0^x e^{-t^2} \\, \\mathrm{d}x` - - Or, in ASCII:: - - x - / - | - | 2 - | -t - 2* | e dt - | - / - 0 - ------------- - ____ - \/ pi - - Examples - ======== - - >>> from sympy import I, oo, erf - >>> from sympy.abc import z - - Several special values are known: - - >>> erf(0) - 0 - >>> erf(oo) - 1 - >>> erf(-oo) - -1 - >>> erf(I*oo) - oo*I - >>> erf(-I*oo) - -oo*I - - In general one can pull out factors of -1 and I from the argument: - - >>> erf(-z) - -erf(z) - - The error function obeys the mirror symmetry: - - >>> from sympy import conjugate - >>> conjugate(erf(z)) - erf(conjugate(z)) - - Differentiation with respect to z is supported: - - >>> from sympy import diff - >>> diff(erf(z), z) - 2*exp(-z**2)/sqrt(pi) - - We can numerically evaluate the error function to arbitrary precision - on the whole complex plane: - - >>> erf(4).evalf(30) - 0.999999984582742099719981147840 - - >>> erf(-4*I).evalf(30) - -1296959.73071763923152794095062*I - - References - ========== - - .. [1] http://en.wikipedia.org/wiki/Error_function - .. [2] http://dlmf.nist.gov/7 - .. [3] http://mathworld.wolfram.com/Erf.html - .. [4] http://functions.wolfram.com/GammaBetaErf/Erf - """ - - nargs = 1 - unbranched = True - - def fdiff(self, argindex=1): - if argindex == 1: - return 2*C.exp(-self.args[0]**2)/sqrt(S.Pi) - else: - raise ArgumentIndexError(self, argindex) - - @classmethod - def eval(cls, arg): - if arg.is_Number: - if arg is S.NaN: - return S.NaN - elif arg is S.Infinity: - return S.One - elif arg is S.NegativeInfinity: - return S.NegativeOne - elif arg is S.Zero: - return S.Zero - - t = arg.extract_multiplicatively(S.ImaginaryUnit) - if t == S.Infinity or t == S.NegativeInfinity: - return arg - - if arg.could_extract_minus_sign(): - return -cls(-arg) - - @staticmethod - @cacheit - def taylor_term(n, x, *previous_terms): - if n < 0 or n % 2 == 0: - return S.Zero - else: - x = sympify(x) - k = C.floor((n - 1)/S(2)) - if len(previous_terms) > 2: - return -previous_terms[-2] * x**2 * (n - 2)/(n*k) - else: - return 2*(-1)**k * x**n/(n*C.factorial(k)*sqrt(S.Pi)) - - def _eval_conjugate(self): - return self.func(self.args[0].conjugate()) - - def _eval_is_real(self): - return self.args[0].is_real - - def _eval_rewrite_as_uppergamma(self, z): - return sqrt(z**2)/z*(S.One - C.uppergamma(S.Half, z**2)/sqrt(S.Pi)) - - def _eval_rewrite_as_tractable(self, z): - return S.One - _erfs(z)*C.exp(-z**2) - - def _eval_as_leading_term(self, x): - arg = self.args[0].as_leading_term(x) - - if x in arg.free_symbols and C.Order(1, x).contains(arg): - return 2*x/sqrt(pi) - else: - return self.func(arg) - - -############################################################################### -#################### EXPONENTIAL INTEGRALS #################################### -############################################################################### -
    -
    [docs]class Ei(Function): - r""" - The classical exponential integral. - - For the use in SymPy, this function is defined as - - .. math:: \operatorname{Ei}(x) = \sum_{n=1}^\infty \frac{x^n}{n\, n!} - + \log(x) + \gamma, - - where :math:`\gamma` is the Euler-Mascheroni constant. - - If :math:`x` is a polar number, this defines an analytic function on the - riemann surface of the logarithm. Otherwise this defines an analytic - function in the cut plane :math:`\mathbb{C} \setminus (-\infty, 0]`. - - **Background** - - The name 'exponential integral' comes from the following statement: - - .. math:: \operatorname{Ei}(x) = \int_{-\infty}^x \frac{e^t}{t} \mathrm{d}t - - If the integral is interpreted as a Cauchy principal value, this statement - holds for :math:`x > 0` and :math:`\operatorname{Ei}(x)` as defined above. - - Note that we carefully avoided defining :math:`\operatorname{Ei}(x)` for - negative real x. This is because above integral formula does not hold for - any polar lift of such :math:`x`, indeed all branches of - :math:`\operatorname{Ei}(x)` above the negative reals are imaginary. - - However, the following statement holds for all :math:`x \in \mathbb{R}^*`: - - .. math:: \int_{-\infty}^x \frac{e^t}{t} \mathrm{d}t = - \frac{\operatorname{Ei}\left(|x|e^{i \arg(x)}\right) + - \operatorname{Ei}\left(|x|e^{- i \arg(x)}\right)}{2}, - - where the integral is again understood to be a principal value if - :math:`x > 0`, and :math:`|x|e^{i \arg(x)}`, - :math:`|x|e^{- i \arg(x)}` denote two conjugate polar lifts of :math:`x`. - - See Also - ======== - - expint, sympy.functions.special.gamma_functions.uppergamma - - References - ========== - - - Abramowitz & Stegun, section 5: http://www.math.sfu.ca/~cbm/aands/page_228.htm - - http://en.wikipedia.org/wiki/Exponential_integral - - Examples - ======== - - >>> from sympy import Ei, polar_lift, exp_polar, I, pi - >>> from sympy.abc import x - - The exponential integral in SymPy is strictly undefined for negative values - of the argument. For convenience, exponential integrals with negative - arguments are immediately converted into an expression that agrees with - the classical integral definition: - - >>> Ei(-1) - -I*pi + Ei(exp_polar(I*pi)) - - This yields a real value: - - >>> Ei(-1).n(chop=True) - -0.219383934395520 - - On the other hand the analytic continuation is not real: - - >>> Ei(polar_lift(-1)).n(chop=True) - -0.21938393439552 + 3.14159265358979*I - - The exponential integral has a logarithmic branch point at the origin: - - >>> Ei(x*exp_polar(2*I*pi)) - Ei(x) + 2*I*pi - - Differentiation is supported: - - >>> Ei(x).diff(x) - exp(x)/x - - The exponential integral is related to many other special functions. - For example: - - >>> from sympy import uppergamma, expint, Shi - >>> Ei(x).rewrite(expint) - -expint(1, x*exp_polar(I*pi)) - I*pi - >>> Ei(x).rewrite(Shi) - Chi(x) + Shi(x) - - """ - - nargs = 1 - - @classmethod - def eval(cls, z): - if not z.is_polar and z.is_negative: - # Note: is this a good idea? - return Ei(polar_lift(z)) - pi*I - nz, n = z.extract_branch_factor() - if n: - return Ei(nz) + 2*I*pi*n - - def fdiff(self, argindex=1): - from sympy import unpolarify - arg = unpolarify(self.args[0]) - if argindex == 1: - return C.exp(arg)/arg - else: - raise ArgumentIndexError(self, argindex) - - def _eval_evalf(self, prec): - if (self.args[0]/polar_lift(-1)).is_positive: - return Function._eval_evalf(self, prec) + (I*pi)._eval_evalf(prec) - return Function._eval_evalf(self, prec) - - def _eval_rewrite_as_uppergamma(self, z): - from sympy import uppergamma - # XXX this does not currently work usefully because uppergamma - # immediately turns into expint - return -uppergamma(0, polar_lift(-1)*z) - I*pi - - def _eval_rewrite_as_expint(self, z): - return -expint(1, polar_lift(-1)*z) - I*pi - - def _eval_rewrite_as_Si(self, z): - return Shi(z) + Chi(z) - _eval_rewrite_as_Ci = _eval_rewrite_as_Si - _eval_rewrite_as_Chi = _eval_rewrite_as_Si - _eval_rewrite_as_Shi = _eval_rewrite_as_Si - -
    -
    [docs]class expint(Function): - r""" - Generalized exponential integral. - - This function is defined as - - .. math:: \operatorname{E}_\nu(z) = z^{\nu - 1} \Gamma(1 - \nu, z), - - where `\Gamma(1 - \nu, z)` is the upper incomplete gamma function - (``uppergamma``). - - Hence for :math:`z` with positive real part we have - - .. math:: \operatorname{E}_\nu(z) - = \int_1^\infty \frac{e^{-zt}}{z^\nu} \mathrm{d}t, - - which explains the name. - - The representation as an incomplete gamma function provides an analytic - continuation for :math:`\operatorname{E}_\nu(z)`. If :math:`\nu` is a - non-positive integer the exponential integral is thus an unbranched - function of :math:`z`, otherwise there is a branch point at the origin. - Refer to the incomplete gamma function documentation for details of the - branching behavior. - - See Also - ======== - - E1: The classical case, returns expint(1, z). - Ei: Another related function called exponential integral. - sympy.functions.special.gamma_functions.uppergamma - - References - ========== - - - http://dlmf.nist.gov/8.19 - - http://functions.wolfram.com/GammaBetaErf/ExpIntegralE/ - - http://en.wikipedia.org/wiki/Exponential_integral - - Examples - ======== - - >>> from sympy import expint, S - >>> from sympy.abc import nu, z - - Differentiation is supported. Differentiation with respect to z explains - further the name: for integral orders, the exponential integral is an - iterated integral of the exponential function. - - >>> expint(nu, z).diff(z) - -expint(nu - 1, z) - - Differentiation with respect to nu has no classical expression: - - >>> expint(nu, z).diff(nu) - -z**(nu - 1)*meijerg(((), (1, 1)), ((0, 0, -nu + 1), ()), z) - - At non-postive integer orders, the exponential integral reduces to the - exponential function: - - >>> expint(0, z) - exp(-z)/z - >>> expint(-1, z) - exp(-z)/z + exp(-z)/z**2 - - At half-integers it reduces to error functions: - - >>> expint(S(1)/2, z) - -sqrt(pi)*erf(sqrt(z))/sqrt(z) + sqrt(pi)/sqrt(z) - - At positive integer orders it can be rewritten in terms of exponentials - and expint(1, z). Use expand_func() to do this: - - >>> from sympy import expand_func - >>> expand_func(expint(5, z)) - z**4*expint(1, z)/24 + (-z**3 + z**2 - 2*z + 6)*exp(-z)/24 - - The generalised exponential integral is essentially equivalent to the - incomplete gamma function: - - >>> from sympy import uppergamma - >>> expint(nu, z).rewrite(uppergamma) - z**(nu - 1)*uppergamma(-nu + 1, z) - - As such it is branched at the origin: - - >>> from sympy import exp_polar, pi, I - >>> expint(4, z*exp_polar(2*pi*I)) - I*pi*z**3/3 + expint(4, z) - >>> expint(nu, z*exp_polar(2*pi*I)) - z**(nu - 1)*(exp(2*I*pi*nu) - 1)*gamma(-nu + 1) + expint(nu, z) - - """ - - nargs = 2 - - @classmethod - def eval(cls, nu, z): - from sympy import (unpolarify, expand_mul, uppergamma, exp, gamma, - factorial) - nu2 = unpolarify(nu) - if nu != nu2: - return expint(nu2, z) - if nu.is_Integer and nu <= 0 or (not nu.is_Integer and (2*nu).is_Integer): - return unpolarify(expand_mul(z**(nu - 1)*uppergamma(1 - nu, z))) - - # Extract branching information. This can be deduced from what is - # explained in lowergamma.eval(). - z, n = z.extract_branch_factor() - if n == 0: - return - if nu.is_integer: - if (nu > 0) is not True: - return - return expint(nu, z) \ - - 2*pi*I*n*(-1)**(nu - 1)/factorial(nu - 1)*unpolarify(z)**(nu - 1) - else: - return (exp(2*I*pi*nu*n) - 1)*z**(nu - 1)*gamma(1 - nu) + expint(nu, z) - - def fdiff(self, argindex): - from sympy import meijerg - nu, z = self.args - if argindex == 1: - return -z**(nu - 1)*meijerg([], [1, 1], [0, 0, 1 - nu], [], z) - elif argindex == 2: - return -expint(nu - 1, z) - else: - raise ArgumentIndexError(self, argindex) - - def _eval_rewrite_as_uppergamma(self, nu, z): - from sympy import uppergamma - return z**(nu - 1)*uppergamma(1 - nu, z) - - def _eval_rewrite_as_Ei(self, nu, z): - from sympy import exp_polar, unpolarify, exp, factorial - if nu == 1: - return -Ei(z*exp_polar(-I*pi)) - I*pi - elif nu.is_Integer and nu > 1: - # DLMF, 8.19.7 - x = -unpolarify(z) - return x**(nu - 1)/factorial(nu - 1)*E1(z).rewrite(Ei) + \ - exp(x)/factorial(nu - 1) * \ - Add(*[factorial(nu - k - 2)*x**k for k in range(nu - 1)]) - else: - return self - - def _eval_expand_func(self, **hints): - return self.rewrite(Ei).rewrite(expint, **hints) - - def _eval_rewrite_as_Si(self, nu, z): - if nu != 1: - return self - return Shi(z) - Chi(z) - _eval_rewrite_as_Ci = _eval_rewrite_as_Si - _eval_rewrite_as_Chi = _eval_rewrite_as_Si - _eval_rewrite_as_Shi = _eval_rewrite_as_Si - -
    -
    [docs]def E1(z): - """ - Classical case of the generalized exponential integral. - - This is equivalent to ``expint(1, z)``. - - """ - return expint(1, z) - - -############################################################################### -#################### TRIGONOMETRIC INTEGRALS ################################## -############################################################################### -
    -class TrigonometricIntegral(Function): - """ Base class for trigonometric integrals. """ - - nargs = 1 - - @classmethod - def eval(cls, z): - if z == 0: - return cls._atzero - elif z is S.Infinity: - return cls._atinf - elif z is S.NegativeInfinity: - return cls._atneginf - - nz = z.extract_multiplicatively(polar_lift(I)) - if nz is None and cls._trigfunc(0) == 0: - nz = z.extract_multiplicatively(I) - if nz is not None: - return cls._Ifactor(nz, 1) - nz = z.extract_multiplicatively(polar_lift(-I)) - if nz is not None: - return cls._Ifactor(nz, -1) - - nz = z.extract_multiplicatively(polar_lift(-1)) - if nz is None and cls._trigfunc(0) == 0: - nz = z.extract_multiplicatively(-1) - if nz is not None: - return cls._minusfactor(nz) - - nz, n = z.extract_branch_factor() - if n == 0 and nz == z: - return - return 2*pi*I*n*cls._trigfunc(0) + cls(nz) - - def fdiff(self, argindex=1): - from sympy import unpolarify - arg = unpolarify(self.args[0]) - if argindex == 1: - return self._trigfunc(arg)/arg - - def _eval_rewrite_as_Ei(self, z): - return self._eval_rewrite_as_expint(z).rewrite(Ei) - - def _eval_rewrite_as_uppergamma(self, z): - from sympy import uppergamma - return self._eval_rewrite_as_expint(z).rewrite(uppergamma) - - def _eval_nseries(self, x, n, logx): - # NOTE this is fairly inefficient - from sympy import log, EulerGamma, Pow - n += 1 - if self.args[0].subs(x, 0) != 0: - return super(TrigonometricIntegral, self)._eval_nseries(x, n, logx) - baseseries = self._trigfunc(x)._eval_nseries(x, n, logx) - if self._trigfunc(0) != 0: - baseseries -= 1 - baseseries = baseseries.replace(Pow, lambda t, n: t**n/n) - if self._trigfunc(0) != 0: - baseseries += EulerGamma + log(x) - return baseseries.subs(x, self.args[0])._eval_nseries(x, n, logx) - - -
    [docs]class Si(TrigonometricIntegral): - r""" - Sine integral. - - This function is defined by - - .. math:: \operatorname{Si}(z) = \int_0^z \frac{\sin{t}}{t} \mathrm{d}t. - - It is an entire function. - - See Also - ======== - - Ci: Cosine integral. - Shi: Sinh integral. - Chi: Cosh integral. - expint: The generalised exponential integral. - - References - ========== - - - http://en.wikipedia.org/wiki/Trigonometric_integral - - Examples - ======== - - >>> from sympy import Si - >>> from sympy.abc import z - - The sine integral is an antiderivative of sin(z)/z: - - >>> Si(z).diff(z) - sin(z)/z - - It is unbranched: - - >>> from sympy import exp_polar, I, pi - >>> Si(z*exp_polar(2*I*pi)) - Si(z) - - Sine integral behaves much like ordinary sine under multiplication by I: - - >>> Si(I*z) - I*Shi(z) - >>> Si(-z) - -Si(z) - - It can also be expressed in terms of exponential integrals, but beware - that the latter is branched: - - >>> from sympy import expint - >>> Si(z).rewrite(expint) - -I*(-expint(1, z*exp_polar(-I*pi/2))/2 + - expint(1, z*exp_polar(I*pi/2))/2) + pi/2 - - """ - - _trigfunc = C.sin - _atzero = S(0) - _atinf = pi*S.Half - _atneginf = -pi*S.Half - - @classmethod - def _minusfactor(cls, z): - return -Si(z) - - @classmethod - def _Ifactor(cls, z, sign): - return I*Shi(z)*sign - - def _eval_rewrite_as_expint(self, z): - # XXX should we polarify z? - return pi/2 + (E1(polar_lift(I)*z) - E1(polar_lift(-I)*z))/2/I - -
    -
    [docs]class Ci(TrigonometricIntegral): - r""" - Cosine integral. - - This function is defined for positive :math:`x` by - - .. math:: \operatorname{Ci}(x) = \gamma + \log{x} - + \int_0^x \frac{\cos{t} - 1}{t} \mathrm{d}t - = -\int_x^\infty \frac{\cos{t}}{t} \mathrm{d}t, - - where :math:`\gamma` is the Euler-Mascheroni constant. - - We have - - .. math:: \operatorname{Ci}(z) = - -\frac{\operatorname{E}_1\left(e^{i\pi/2} z\right) - + \operatorname{E}_1\left(e^{-i \pi/2} z\right)}{2} - - which holds for all polar :math:`z` and thus provides an analytic - continuation to the Riemann surface of the logarithm. - - The formula also holds as stated - for :math:`z \in \mathbb{C}` with :math:`Re(z) > 0`. - By lifting to the principal branch we obtain an analytic function on the - cut complex plane. - - See Also - ======== - - Si: Sine integral. - Shi: Sinh integral. - Chi: Cosh integral. - expint: The generalised exponential integral. - - References - ========== - - - http://en.wikipedia.org/wiki/Trigonometric_integral - - Examples - ======== - - >>> from sympy import Ci - >>> from sympy.abc import z - - The cosine integral is a primitive of cos(z)/z: - - >>> Ci(z).diff(z) - cos(z)/z - - It has a logarithmic branch point at the origin: - - >>> from sympy import exp_polar, I, pi - >>> Ci(z*exp_polar(2*I*pi)) - Ci(z) + 2*I*pi - - Cosine integral behaves somewhat like ordinary cos under multiplication by I: - - >>> from sympy import polar_lift - >>> Ci(polar_lift(I)*z) - Chi(z) + I*pi/2 - >>> Ci(polar_lift(-1)*z) - Ci(z) + I*pi - - It can also be expressed in terms of exponential integrals: - - >>> from sympy import expint - >>> Ci(z).rewrite(expint) - -expint(1, z*exp_polar(-I*pi/2))/2 - expint(1, z*exp_polar(I*pi/2))/2 - - """ - - _trigfunc = C.cos - _atzero = S.ComplexInfinity - _atinf = S.Zero - _atneginf = I*pi - - @classmethod - def _minusfactor(cls, z): - return Ci(z) + I*pi - - @classmethod - def _Ifactor(cls, z, sign): - return Chi(z) + I*pi/2*sign - - def _eval_rewrite_as_expint(self, z): - return -(E1(polar_lift(I)*z) + E1(polar_lift(-I)*z))/2 - -
    -
    [docs]class Shi(TrigonometricIntegral): - r""" - Sinh integral. - - This function is defined by - - .. math:: \operatorname{Shi}(z) = \int_0^z \frac{\sinh{t}}{t} \mathrm{d}t. - - It is an entire function. - - See Also - ======== - - Si: Sine integral. - Ci: Cosine integral. - Chi: Cosh integral. - expint: The generalised exponential integral. - - References - ========== - - - http://en.wikipedia.org/wiki/Trigonometric_integral - - Examples - ======== - - >>> from sympy import Shi - >>> from sympy.abc import z - - The Sinh integral is a primitive of sinh(z)/z: - - >>> Shi(z).diff(z) - sinh(z)/z - - It is unbranched: - - >>> from sympy import exp_polar, I, pi - >>> Shi(z*exp_polar(2*I*pi)) - Shi(z) - - Sinh integral behaves much like ordinary sinh under multiplication by I: - - >>> Shi(I*z) - I*Si(z) - >>> Shi(-z) - -Shi(z) - - It can also be expressed in terms of exponential integrals, but beware - that the latter is branched: - - >>> from sympy import expint - >>> Shi(z).rewrite(expint) - expint(1, z)/2 - expint(1, z*exp_polar(I*pi))/2 - I*pi/2 - - """ - - _trigfunc = C.sinh - _atzero = S(0) - _atinf = S.Infinity - _atneginf = S.NegativeInfinity - - @classmethod - def _minusfactor(cls, z): - return -Shi(z) - - @classmethod - def _Ifactor(cls, z, sign): - return I*Si(z)*sign - - def _eval_rewrite_as_expint(self, z): - from sympy import exp_polar - # XXX should we polarify z? - return (E1(z) - E1(exp_polar(I*pi)*z))/2 - I*pi/2 - -
    -
    [docs]class Chi(TrigonometricIntegral): - r""" - Cosh integral. - - This function is defined for positive :math:`x` by - - .. math:: \operatorname{Chi}(x) = \gamma + \log{x} - + \int_0^x \frac{\cosh{t} - 1}{t} \mathrm{d}t, - - where :math:`\gamma` is the Euler-Mascheroni constant. - - We have - - .. math:: \operatorname{Chi}(z) = \operatorname{Ci}\left(e^{i \pi/2}z\right) - - i\frac{\pi}{2}, - - which holds for all polar :math:`z` and thus provides an analytic - continuation to the Riemann surface of the logarithm. - By lifting to the principal branch we obtain an analytic function on the - cut complex plane. - - See Also - ======== - - Si: Sine integral. - Ci: Cosine integral. - Shi: Sinh integral. - expint: The generalised exponential integral. - - References - ========== - - - http://en.wikipedia.org/wiki/Trigonometric_integral - - Examples - ======== - - >>> from sympy import Chi - >>> from sympy.abc import z - - The cosh integral is a primitive of cosh(z)/z: - - >>> Chi(z).diff(z) - cosh(z)/z - - It has a logarithmic branch point at the origin: - - >>> from sympy import exp_polar, I, pi - >>> Chi(z*exp_polar(2*I*pi)) - Chi(z) + 2*I*pi - - Cosh integral behaves somewhat like ordinary cosh under multiplication by I: - - >>> from sympy import polar_lift - >>> Chi(polar_lift(I)*z) - Ci(z) + I*pi/2 - >>> Chi(polar_lift(-1)*z) - Chi(z) + I*pi - - It can also be expressed in terms of exponential integrals: - - >>> from sympy import expint - >>> Chi(z).rewrite(expint) - -expint(1, z)/2 - expint(1, z*exp_polar(I*pi))/2 - I*pi/2 - - """ - - _trigfunc = C.cosh - _atzero = S.ComplexInfinity - _atinf = S.Infinity - _atneginf = S.Infinity - - @classmethod - def _minusfactor(cls, z): - return Chi(z) + I*pi - - @classmethod - def _Ifactor(cls, z, sign): - return Ci(z) + I*pi/2*sign - - def _eval_rewrite_as_expint(self, z): - from sympy import exp_polar - return -I*pi/2 - (E1(z) + E1(exp_polar(I*pi)*z))/2 - - -############################################################################### -#################### FRESNEL INTEGRALS ######################################## -############################################################################### -
    -
    [docs]class FresnelIntegral(Function): - """ Base class for the Fresnel integrals.""" - - nargs = 1 - unbranched = True - - @classmethod - def eval(cls, z): - # Value at zero - if z is S.Zero: - return S(0) - - # Try to pull out factors of -1 and I - prefact = S.One - newarg = z - changed = False - - nz = newarg.extract_multiplicatively(-1) - if nz is not None: - prefact = -prefact - newarg = nz - changed = True - - nz = newarg.extract_multiplicatively(I) - if nz is not None: - prefact = cls._sign*I*prefact - newarg = nz - changed = True - - if changed: - return prefact*cls(newarg) - - # Values at positive infinities signs - # if any were extracted automatically - if z is S.Infinity: - return S.Half - elif z is I*S.Infinity: - return cls._sign*I*S.Half - - def fdiff(self, argindex=1): - if argindex == 1: - return self._trigfunc(S.Half*pi*self.args[0]**2) - else: - raise ArgumentIndexError(self, argindex) - - def _eval_is_real(self): - return self.args[0].is_real - - def _eval_conjugate(self): - return self.func(self.args[0].conjugate()) - - def _as_real_imag(self, deep=True, **hints): - if self.args[0].is_real: - if deep: - hints['complex'] = False - return (self.expand(deep, **hints), S.Zero) - else: - return (self, S.Zero) - if deep: - re, im = self.args[0].expand(deep, **hints).as_real_imag() - else: - re, im = self.args[0].as_real_imag() - return (re, im) - - def as_real_imag(self, deep=True, **hints): - # Fresnel S - # http://functions.wolfram.com/06.32.19.0003.01 - # http://functions.wolfram.com/06.32.19.0006.01 - # Fresnel C - # http://functions.wolfram.com/06.33.19.0003.01 - # http://functions.wolfram.com/06.33.19.0006.01 - x, y = self._as_real_imag(deep=deep, **hints) - sq = -y**2/x**2 - re = S.Half*(self.func(x + x*sqrt(sq)) + self.func(x - x*sqrt(sq))) - im = x/(2*y) * sqrt(sq) * (self.func(x - x*sqrt(sq)) - - self.func(x + x*sqrt(sq))) - return (re, im) - -
    -
    [docs]class fresnels(FresnelIntegral): - r""" - Fresnel integral S. - - This function is defined by - - .. math:: \operatorname{S}(z) = \int_0^z \sin{\frac{\pi}{2} t^2} \mathrm{d}t. - - It is an entire function. - - Examples - ======== - - >>> from sympy import I, oo, fresnels - >>> from sympy.abc import z - - Several special values are known: - - >>> fresnels(0) - 0 - >>> fresnels(oo) - 1/2 - >>> fresnels(-oo) - -1/2 - >>> fresnels(I*oo) - -I/2 - >>> fresnels(-I*oo) - I/2 - - In general one can pull out factors of -1 and I from the argument: - >>> fresnels(-z) - -fresnels(z) - - >>> fresnels(I*z) - -I*fresnels(z) - - The Fresnel S integral obeys the mirror symmetry: - - >>> from sympy import conjugate - >>> conjugate(fresnels(z)) - fresnels(conjugate(z)) - - Differentiation with respect to z is supported: - - >>> from sympy import diff - >>> diff(fresnels(z), z) - sin(pi*z**2/2) - - Defining the Fresnel functions via an integral - - >>> from sympy import integrate, pi, sin, gamma, expand_func - >>> integrate(sin(pi*z**2/2), z) - 3*fresnels(z)*gamma(3/4)/(4*gamma(7/4)) - >>> expand_func(integrate(sin(pi*z**2/2), z)) - fresnels(z) - - We can numerically evaluate the Fresnel integral to arbitrary precision - on the whole complex plane: - - >>> fresnels(2).evalf(30) - 0.343415678363698242195300815958 - - >>> fresnels(-2*I).evalf(30) - 0.343415678363698242195300815958*I - - See Also - ======== - - fresnelc - - References - ========== - - .. [1] http://en.wikipedia.org/wiki/Fresnel_integral - .. [2] http://dlmf.nist.gov/7 - .. [3] http://mathworld.wolfram.com/FresnelIntegrals.html - .. [4] http://functions.wolfram.com/GammaBetaErf/FresnelS - """ - - _trigfunc = C.sin - _sign = -S.One - - @staticmethod - @cacheit - def taylor_term(n, x, *previous_terms): - if n < 0: - return S.Zero - else: - x = sympify(x) - if len(previous_terms) > 1: - p = previous_terms[-1] - return (-pi**2*x**4*(4*n - 1)/(8*n*(2*n + 1)*(4*n + 3))) * p - else: - return x**3 * (-x**4)**n * (S(2)**(-2*n - 1)*pi**(2*n + 1)) / ((4*n + 3)*C.factorial(2*n + 1)) - - def _eval_rewrite_as_erf(self, z): - return (S.One + I)/4 * (erf((S.One + I)/2*sqrt(pi)*z) - I*erf((S.One - I)/2*sqrt(pi)*z)) - - def _eval_rewrite_as_hyper(self, z): - return pi*z**3/6 * hyper([S(3)/4], [S(3)/2, S(7)/4], -pi**2*z**4/16) - - def _eval_rewrite_as_meijerg(self, z): - return (pi*z**(S(9)/4) / (sqrt(2)*(z**2)**(S(3)/4)*(-z)**(S(3)/4)) - * meijerg([], [1], [S(3)/4], [S(1)/4, 0], -pi**2*z**4/16)) - -
    -
    [docs]class fresnelc(FresnelIntegral): - r""" - Fresnel integral C. - - This function is defined by - - .. math:: \operatorname{C}(z) = \int_0^z \cos{\frac{\pi}{2} t^2} \mathrm{d}t. - - It is an entire function. - - Examples - ======== - - >>> from sympy import I, oo, fresnelc - >>> from sympy.abc import z - - Several special values are known: - - >>> fresnelc(0) - 0 - >>> fresnelc(oo) - 1/2 - >>> fresnelc(-oo) - -1/2 - >>> fresnelc(I*oo) - I/2 - >>> fresnelc(-I*oo) - -I/2 - - In general one can pull out factors of -1 and I from the argument: - >>> fresnelc(-z) - -fresnelc(z) - - >>> fresnelc(I*z) - I*fresnelc(z) - - The Fresnel C integral obeys the mirror symmetry: - - >>> from sympy import conjugate - >>> conjugate(fresnelc(z)) - fresnelc(conjugate(z)) - - Differentiation with respect to z is supported: - - >>> from sympy import diff - >>> diff(fresnelc(z), z) - cos(pi*z**2/2) - - Defining the Fresnel functions via an integral - - >>> from sympy import integrate, pi, cos, gamma, expand_func - >>> integrate(cos(pi*z**2/2), z) - fresnelc(z)*gamma(1/4)/(4*gamma(5/4)) - >>> expand_func(integrate(cos(pi*z**2/2), z)) - fresnelc(z) - - We can numerically evaluate the Fresnel integral to arbitrary precision - on the whole complex plane: - - >>> fresnelc(2).evalf(30) - 0.488253406075340754500223503357 - - >>> fresnelc(-2*I).evalf(30) - -0.488253406075340754500223503357*I - - See Also - ======== - - fresnels - - References - ========== - - .. [1] http://en.wikipedia.org/wiki/Fresnel_integral - .. [2] http://dlmf.nist.gov/7 - .. [3] http://mathworld.wolfram.com/FresnelIntegrals.html - .. [4] http://functions.wolfram.com/GammaBetaErf/FresnelC - """ - - _trigfunc = C.cos - _sign = S.One - - @staticmethod - @cacheit - def taylor_term(n, x, *previous_terms): - if n < 0: - return S.Zero - else: - x = sympify(x) - if len(previous_terms) > 1: - p = previous_terms[-1] - return (-pi**2*x**4*(4*n - 3)/(8*n*(2*n - 1)*(4*n + 1))) * p - else: - return x * (-x**4)**n * (S(2)**(-2*n)*pi**(2*n)) / ((4*n + 1)*C.factorial(2*n)) - - def _eval_rewrite_as_erf(self, z): - return (S.One - I)/4 * (erf((S.One + I)/2*sqrt(pi)*z) + I*erf((S.One - I)/2*sqrt(pi)*z)) - - def _eval_rewrite_as_hyper(self, z): - return z * hyper([S.One/4], [S.One/2, S(5)/4], -pi**2*z**4/16) - - def _eval_rewrite_as_meijerg(self, z): - return (pi*z**(S(3)/4) / (sqrt(2)*root(z**2, 4)*root(-z, 4)) - * meijerg([], [1], [S(1)/4], [S(3)/4, 0], -pi**2*z**4/16)) - -############################################################################### -#################### HELPER FUNCTIONS ######################################### -############################################################################### - -
    -class _erfs(Function): - """ - Helper function to make the :math:`erf(z)` function - tractable for the Gruntz algorithm. - """ - - nargs = 1 - - def _eval_aseries(self, n, args0, x, logx): - if args0[0] != S.Infinity: - return super(_erfs, self)._eval_aseries(n, args0, x, logx) - - z = self.args[0] - l = [ 1/sqrt(S.Pi) * C.factorial(2*k)*(-S( - 4))**(-k)/C.factorial(k) * (1/z)**(2*k + 1) for k in range(0, n) ] - o = C.Order(1/z**(2*n + 1), x) - # It is very inefficient to first add the order and then do the nseries - return (Add(*l))._eval_nseries(x, n, logx) + o - - def fdiff(self, argindex=1): - if argindex == 1: - z = self.args[0] - return -2/sqrt(S.Pi) + 2*z*_erfs(z) - else: - raise ArgumentIndexError(self, argindex) - - def _eval_rewrite_as_intractable(self, z): - return (S.One - erf(z))*C.exp(z**2) -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/functions/special/gamma_functions.html b/dev-py3k/_modules/sympy/functions/special/gamma_functions.html deleted file mode 100644 index 11271faaf63..00000000000 --- a/dev-py3k/_modules/sympy/functions/special/gamma_functions.html +++ /dev/null @@ -1,710 +0,0 @@ - - - - - - - - - - sympy.functions.special.gamma_functions — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.functions.special.gamma_functions

    -from sympy.core import Add, S, C, sympify, oo, pi
    -from sympy.core.function import Function, ArgumentIndexError
    -from .zeta_functions import zeta
    -from .error_functions import erf
    -from sympy.core import Dummy, Rational
    -from sympy.functions.elementary.exponential import log
    -from sympy.functions.elementary.integers import floor
    -from sympy.functions.elementary.miscellaneous import sqrt
    -from sympy.functions.combinatorial.numbers import bernoulli
    -from sympy.functions.combinatorial.factorials import rf
    -
    -###############################################################################
    -############################ COMPLETE GAMMA FUNCTION ##########################
    -###############################################################################
    -
    -
    -
    [docs]class gamma(Function): - """The gamma function returns a function which passes through the integral - values of the factorial function, i.e. though defined in the complex plane, - when n is an integer, `gamma(n) = (n - 1)!` - - Reference: - http://en.wikipedia.org/wiki/Gamma_function - """ - - nargs = 1 - unbranched = True - - def fdiff(self, argindex=1): - if argindex == 1: - return gamma(self.args[0])*polygamma(0, self.args[0]) - else: - raise ArgumentIndexError(self, argindex) - - @classmethod - def eval(cls, arg): - if arg.is_Number: - if arg is S.NaN: - return S.NaN - elif arg is S.Infinity: - return S.Infinity - elif arg.is_Integer: - if arg.is_positive: - return C.factorial(arg - 1) - else: - return S.ComplexInfinity - elif arg.is_Rational: - if arg.q == 2: - n = abs(arg.p) // arg.q - - if arg.is_positive: - k, coeff = n, S.One - else: - n = k = n + 1 - - if n & 1 == 0: - coeff = S.One - else: - coeff = S.NegativeOne - - for i in range(3, 2*k, 2): - coeff *= i - - if arg.is_positive: - return coeff*sqrt(S.Pi) / 2**n - else: - return 2**n*sqrt(S.Pi) / coeff - - def _eval_expand_func(self, **hints): - arg = self.args[0] - if arg.is_Rational: - if abs(arg.p) > arg.q: - x = Dummy('x') - n = arg.p // arg.q - p = arg.p - n*arg.q - return gamma(x + n)._eval_expand_func().subs(x, Rational(p, arg.q)) - - if arg.is_Add: - coeff, tail = arg.as_coeff_add() - if coeff and coeff.q != 1: - intpart = floor(coeff) - tail = (coeff - intpart,) + tail - coeff = intpart - tail = arg._new_rawargs(*tail, **dict(reeval=False)) - return gamma(tail)*C.RisingFactorial(tail, coeff) - - return self.func(*self.args) - - def _eval_is_real(self): - return self.args[0].is_real - - def _eval_rewrite_as_tractable(self, z): - return C.exp(loggamma(z)) - - def _eval_nseries(self, x, n, logx): - x0 = self.args[0].limit(x, 0) - if not (x0.is_Integer and x0 <= 0): - return super(gamma, self)._eval_nseries(x, n, logx) - t = self.args[0] - x0 - return (gamma(t + 1)/rf(self.args[0], -x0 + 1))._eval_nseries(x, n, logx) - - -############################################################################### -################## LOWER and UPPER INCOMPLETE GAMMA FUNCTIONS ################# -############################################################################### -
    -
    [docs]class lowergamma(Function): - r""" - The lower incomplete gamma function. - - It can be defined as the meromorphic continuation of - - .. math :: - \gamma(s, x) = \int_0^x t^{s-1} e^{-t} \mathrm{d}t. - - This can be shown to be the same as - - .. math :: - \gamma(s, x) = \frac{x^s}{s} {}_1F_1\left({s \atop s+1} \middle| -x\right), - - where :math:`{}_1F_1` is the (confluent) hypergeometric function. - - See Also - ======== - - gamma, uppergamma - sympy.functions.special.hyper.hyper - - Examples - ======== - - >>> from sympy import lowergamma, S - >>> from sympy.abc import s, x - >>> lowergamma(s, x) - lowergamma(s, x) - >>> lowergamma(3, x) - -x**2*exp(-x) - 2*x*exp(-x) + 2 - 2*exp(-x) - >>> lowergamma(-S(1)/2, x) - -2*sqrt(pi)*erf(sqrt(x)) - 2*exp(-x)/sqrt(x) - - References - ========== - - - Abramowitz, Milton; Stegun, Irene A., eds. (1965), Chapter 6, Section 5, - Handbook of Mathematical Functions with Formulas, Graphs, and Mathematical - Tables - - http://en.wikipedia.org/wiki/Incomplete_gamma_function - - """ - - nargs = 2 - - def fdiff(self, argindex=2): - from sympy import meijerg, unpolarify - if argindex == 2: - a, z = self.args - return C.exp(-unpolarify(z))*z**(a - 1) - elif argindex == 1: - a, z = self.args - return gamma(a)*digamma(a) - log(z)*uppergamma(a, z) \ - + meijerg([], [1, 1], [0, 0, a], [], z) - - else: - raise ArgumentIndexError(self, argindex) - - @classmethod - def eval(cls, a, x): - # For lack of a better place, we use this one to extract branching - # information. The following can be - # found in the literature (c/f references given above), albeit scattered: - # 1) For fixed x != 0, lowergamma(s, x) is an entire function of s - # 2) For fixed positive integers s, lowergamma(s, x) is an entire - # function of x. - # 3) For fixed non-positive integers s, - # lowergamma(s, exp(I*2*pi*n)*x) = - # 2*pi*I*n*(-1)**(-s)/factorial(-s) + lowergamma(s, x) - # (this follows from lowergamma(s, x).diff(x) = x**(s-1)*exp(-x)). - # 4) For fixed non-integral s, - # lowergamma(s, x) = x**s*gamma(s)*lowergamma_unbranched(s, x), - # where lowergamma_unbranched(s, x) is an entire function (in fact - # of both s and x), i.e. - # lowergamma(s, exp(2*I*pi*n)*x) = exp(2*pi*I*n*a)*lowergamma(a, x) - from sympy import unpolarify, I, factorial, exp - nx, n = x.extract_branch_factor() - if a.is_integer and a > 0: - nx = unpolarify(x) - if nx != x: - return lowergamma(a, nx) - elif a.is_integer and a <= 0: - if n != 0: - return 2*pi*I*n*(-1)**(-a)/factorial(-a) + lowergamma(a, nx) - elif n != 0: - return exp(2*pi*I*n*a)*lowergamma(a, nx) - - # Special values. - if a.is_Number: - # TODO this should be non-recursive - if a is S.One: - return S.One - C.exp(-x) - elif a is S.Half: - return sqrt(pi)*erf(sqrt(x)) - elif a.is_Integer or (2*a).is_Integer: - b = a - 1 - if b.is_positive: - return b*cls(b, x) - x**b * C.exp(-x) - - if not a.is_Integer: - return (cls(a + 1, x) + x**a * C.exp(-x))/a - - def _eval_evalf(self, prec): - from sympy.mpmath import mp - from sympy import Expr - a = self.args[0]._to_mpmath(prec) - z = self.args[1]._to_mpmath(prec) - oprec = mp.prec - mp.prec = prec - res = mp.gammainc(a, 0, z) - mp.prec = oprec - return Expr._from_mpmath(res, prec) - - def _eval_rewrite_as_uppergamma(self, s, x): - return gamma(s) - uppergamma(s, x) - - def _eval_rewrite_as_expint(self, s, x): - from sympy import expint - if s.is_integer and s.is_nonpositive: - return self - return self.rewrite(uppergamma).rewrite(expint) - -
    -
    [docs]class uppergamma(Function): - r""" - Upper incomplete gamma function - - It can be defined as the meromorphic continuation of - - .. math :: - \Gamma(s, x) = \int_x^\infty t^{s-1} e^{-t} \mathrm{d}t - = \Gamma(s) - \gamma(s, x). - - This can be shown to be the same as - - .. math :: - \Gamma(s, x) = \Gamma(s) - - \frac{x^s}{s} {}_1F_1\left({s \atop s+1} \middle| -x\right), - - where :math:`{}_1F_1` is the (confluent) hypergeometric function. - - The upper incomplete gamma function is also essentially equivalent to the - generalized exponential integral. - - Examples - ======== - - >>> from sympy import uppergamma, S - >>> from sympy.abc import s, x - >>> uppergamma(s, x) - uppergamma(s, x) - >>> uppergamma(3, x) - x**2*exp(-x) + 2*x*exp(-x) + 2*exp(-x) - >>> uppergamma(-S(1)/2, x) - -2*sqrt(pi)*(-erf(sqrt(x)) + 1) + 2*exp(-x)/sqrt(x) - >>> uppergamma(-2, x) - expint(3, x)/x**2 - - See Also - ======== - - gamma, lowergamma - sympy.functions.special.hyper.hyper - - References - ========== - - - Abramowitz, Milton; Stegun, Irene A., eds. (1965), Chapter 6, Section 5, - Handbook of Mathematical Functions with Formulas, Graphs, and Mathematical - Tables - - http://en.wikipedia.org/wiki/Incomplete_gamma_function - - """ - - nargs = 2 - - def fdiff(self, argindex=2): - from sympy import meijerg, unpolarify - if argindex == 2: - a, z = self.args - return -C.exp(-unpolarify(z))*z**(a - 1) - elif argindex == 1: - a, z = self.args - return uppergamma(a, z)*log(z) + meijerg([], [1, 1], [0, 0, a], [], z) - else: - raise ArgumentIndexError(self, argindex) - - def _eval_evalf(self, prec): - from sympy.mpmath import mp - from sympy import Expr - a = self.args[0]._to_mpmath(prec) - z = self.args[1]._to_mpmath(prec) - oprec = mp.prec - mp.prec = prec - res = mp.gammainc(a, z, mp.inf) - mp.prec = oprec - return Expr._from_mpmath(res, prec) - - @classmethod - def eval(cls, a, z): - from sympy import unpolarify, I, factorial, exp, expint - if z.is_Number: - if z is S.NaN: - return S.NaN - elif z is S.Infinity: - return S.Zero - elif z is S.Zero: - return gamma(a) - - # We extract branching information here. C/f lowergamma. - nx, n = z.extract_branch_factor() - if a.is_integer and a > 0: - nx = unpolarify(z) - if z != nx: - return uppergamma(a, nx) - elif a.is_integer and a <= 0: - if n != 0: - return -2*pi*I*n*(-1)**(-a)/factorial(-a) + uppergamma(a, nx) - elif n != 0: - return gamma(a)*(1 - exp(2*pi*I*n*a)) + exp(2*pi*I*n*a)*uppergamma(a, nx) - - # Special values. - if a.is_Number: - # TODO this should be non-recursive - if a is S.One: - return C.exp(-z) - elif a is S.Half: - return sqrt(pi)*(1 - erf(sqrt(z))) # TODO could use erfc... - elif a.is_Integer or (2*a).is_Integer: - b = a - 1 - if b.is_positive: - return b*cls(b, z) + z**b * C.exp(-z) - elif b.is_Integer: - return expint(-b, z)*unpolarify(z)**(b + 1) - - if not a.is_Integer: - return (cls(a + 1, z) - z**a * C.exp(-z))/a - - def _eval_rewrite_as_lowergamma(self, s, x): - return gamma(s) - lowergamma(s, x) - - def _eval_rewrite_as_expint(self, s, x): - from sympy import expint - return expint(1 - s, x)*x**s - - -############################################################################### -########################### GAMMA RELATED FUNCTIONS ########################### -###############################################################################
    -
    [docs]class polygamma(Function): - """The function `polygamma(n, z)` returns `log(gamma(z)).diff(n + 1)` - - See Also - ======== - - gamma, digamma, trigamma - - Reference: - http://en.wikipedia.org/wiki/Polygamma_function - """ - - nargs = 2 - - def fdiff(self, argindex=2): - if argindex == 2: - n, z = self.args[:2] - return polygamma(n + 1, z) - else: - raise ArgumentIndexError(self, argindex) - - def _eval_is_positive(self): - if self.args[1].is_positive and self.args[0] > 0: - return self.args[0].is_odd - - def _eval_is_negative(self): - if self.args[1].is_positive and self.args[0] > 0: - return self.args[0].is_even - - def _eval_is_real(self): - return self.args[0].is_real - - def _eval_aseries(self, n, args0, x, logx): - if args0[1] != oo or not \ - (self.args[0].is_Integer and self.args[0].is_nonnegative): - return super(polygamma, self)._eval_aseries(n, args0, x, logx) - z = self.args[1] - N = self.args[0] - - if N == 0: - # digamma function series - # Abramowitz & Stegun, p. 259, 6.3.18 - r = log(z) - 1/(2*z) - o = None - if n < 2: - o = C.Order(1/z, x) - else: - m = C.ceiling((n + 1)//2) - l = [bernoulli(2*k) / (2*k*z**(2*k)) for k in range(1, m)] - r -= Add(*l) - o = C.Order(1/z**(2*m), x) - return r._eval_nseries(x, n, logx) + o - else: - # proper polygamma function - # Abramowitz & Stegun, p. 260, 6.4.10 - # We return terms to order higher than O(x**n) on purpose - # -- otherwise we would not be able to return any terms for - # quite a long time! - fac = gamma(N) - e0 = fac + N*fac/(2*z) - m = C.ceiling((n + 1)//2) - for k in range(1, m): - fac = fac*(2*k + N - 1)*(2*k + N - 2) / ((2*k)*(2*k - 1)) - e0 += bernoulli(2*k)*fac/z**(2*k) - o = C.Order(1/z**(2*m), x) - if n == 0: - o = C.Order(1/z, x) - elif n == 1: - o = C.Order(1/z**2, x) - r = e0._eval_nseries(z, n, logx) + o - return -1 * (-1/z)**N * r - - @classmethod - def eval(cls, n, z): - n, z = list(map(sympify, (n, z))) - from sympy import unpolarify - - if n.is_integer: - if n.is_nonnegative: - nz = unpolarify(z) - if z != nz: - return polygamma(n, nz) - - if n == -1: - return loggamma(z) - else: - if z.is_Number: - if z is S.NaN: - return S.NaN - elif z is S.Infinity: - if n.is_Number: - if n is S.Zero: - return S.Infinity - else: - return S.Zero - elif z.is_Integer: - if z.is_nonpositive: - return S.ComplexInfinity - else: - if n is S.Zero: - return -S.EulerGamma + C.harmonic(z - 1, 1) - elif n.is_odd: - return (-1)**(n + 1)*C.factorial(n)*zeta(n + 1, z) - - if n == 0 and z.is_Rational: - # TODO actually *any* n/m can be done, but that is messy - lookup = {S(1)/2: -2*log(2) - S.EulerGamma, - S(1)/3: -S.Pi/2/sqrt(3) - 3*log(3)/2 - S.EulerGamma, - S(1)/4: -S.Pi/2 - 3*log(2) - S.EulerGamma, - S(3)/4: -3*log(2) - S.EulerGamma + S.Pi/2, - S(2)/3: -3*log(3)/2 + S.Pi/2/sqrt(3) - S.EulerGamma} - if z > 0: - n = floor(z) - z0 = z - n - if z0 in lookup: - return lookup[z0] + Add(*[1/(z0 + k) for k in range(n)]) - elif z < 0: - n = floor(1 - z) - z0 = z + n - if z0 in lookup: - return lookup[z0] - Add(*[1/(z0 - 1 - k) for k in range(n)]) - - # TODO n == 1 also can do some rational z - - def _eval_expand_func(self, **hints): - n, z = self.args - - if n.is_Integer and n.is_nonnegative: - if z.is_Add: - coeff = z.args[0] - if coeff.is_Integer: - e = -(n + 1) - if coeff > 0: - tail = Add(*[C.Pow( - z - i, e) for i in range(1, int(coeff) + 1)]) - else: - tail = -Add(*[C.Pow( - z + i, e) for i in range(0, int(-coeff))]) - return polygamma(n, z - coeff) + (-1)**n*C.factorial(n)*tail - - elif z.is_Mul: - coeff, z = z.as_two_terms() - if coeff.is_Integer and coeff.is_positive: - tail = [ polygamma(n, z + C.Rational( - i, coeff)) for i in range(0, int(coeff)) ] - if n == 0: - return Add(*tail)/coeff + log(coeff) - else: - return Add(*tail)/coeff**(n + 1) - z *= coeff - - return polygamma(n, z) - - def _eval_rewrite_as_zeta(self, n, z): - return (-1)**(n + 1)*C.factorial(n)*zeta(n + 1, z - 1) - -
    -
    [docs]class loggamma(Function): - """ - The loggamma function is `ln(gamma(x))`. - - References - ========== - - http://mathworld.wolfram.com/LogGammaFunction.html - - """ - - nargs = 1 - - def _eval_aseries(self, n, args0, x, logx): - if args0[0] != oo: - return super(loggamma, self)._eval_aseries(n, args0, x, logx) - z = self.args[0] - m = min(n, C.ceiling((n + S(1))/2)) - r = log(z)*(z - S(1)/2) - z + log(2*pi)/2 - l = [bernoulli(2*k) / (2*k*(2*k - 1)*z**(2*k - 1)) for k in range(1, m)] - o = None - if m == 0: - o = C.Order(1, x) - else: - o = C.Order(1/z**(2*m - 1), x) - # It is very inefficient to first add the order and then do the nseries - return (r + Add(*l))._eval_nseries(x, n, logx) + o - - def _eval_rewrite_as_intractable(self, z): - return log(gamma(z)) - - def _eval_is_real(self): - return self.args[0].is_real - - def fdiff(self, argindex=1): - if argindex == 1: - return polygamma(0, self.args[0]) - else: - raise ArgumentIndexError(self, argindex) - -
    -
    [docs]def digamma(x): - """ - The digamma function is the logarithmic derivative of the gamma function. - - - In this case, `digamma(x) = polygamma(0, x)`. - - See Also - ======== - - gamma, trigamma, polygamma - - """ - return polygamma(0, x) - -
    -
    [docs]def trigamma(x): - """ - The trigamma function is the second of the polygamma functions. - - In this case, `trigamma(x) = polygamma(1, x)`. - - See Also - ======== - - gamma, digamma, polygamma - - """ - return polygamma(1, x) - -
    -
    [docs]def beta(x, y): - """ - Euler Beta function - - ``beta(x, y) == gamma(x)*gamma(y) / gamma(x+y)`` - - """ - return gamma(x)*gamma(y) / gamma(x + y)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/functions/special/hyper.html b/dev-py3k/_modules/sympy/functions/special/hyper.html deleted file mode 100644 index c83bb4ab437..00000000000 --- a/dev-py3k/_modules/sympy/functions/special/hyper.html +++ /dev/null @@ -1,1132 +0,0 @@ - - - - - - - - - - sympy.functions.special.hyper — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.functions.special.hyper

    -"""Hypergeometric and Meijer G-functions"""
    -
    -from sympy.core import S, I, pi, oo, ilcm, Mod
    -from sympy.core.function import Function, Derivative, ArgumentIndexError
    -from sympy.core.containers import Tuple
    -from sympy.core.mul import Mul
    -
    -from sympy.functions import (sqrt, exp, log, sin, cos, asin, atan,
    -        sinh, cosh, asinh, acosh, atanh, acoth)
    -from functools import reduce
    -
    -# TODO should __new__ accept **options?
    -# TODO should constructors should check if parameters are sensible?
    -
    -
    -def _prep_tuple(v):
    -    """
    -    Turn an iterable argument V into a Tuple and unpolarify, since both
    -    hypergeometric and meijer g-functions are unbranched in their parameters.
    -
    -    Examples:
    -    >>> from sympy.functions.special.hyper import _prep_tuple
    -    >>> _prep_tuple([1, 2, 3])
    -    (1, 2, 3)
    -    >>> _prep_tuple((4, 5))
    -    (4, 5)
    -    >>> _prep_tuple((7, 8, 9))
    -    (7, 8, 9)
    -    """
    -    from sympy.simplify.simplify import unpolarify
    -    return Tuple(*[unpolarify(x) for x in v])
    -
    -
    -class TupleParametersBase(Function):
    -    """ Base class that takes care of differentiation, when some of
    -        the arguments are actually tuples. """
    -    # This is not deduced automatically since there are Tuples as arguments.
    -    is_commutative = True
    -
    -    def _eval_derivative(self, s):
    -        try:
    -            res = 0
    -            if self.args[0].has(s) or self.args[1].has(s):
    -                for i, p in enumerate(self._diffargs):
    -                    m = self._diffargs[i].diff(s)
    -                    if m != 0:
    -                        res += self.fdiff((1, i))*m
    -            return res + self.fdiff(3)*self.args[2].diff(s)
    -        except (ArgumentIndexError, NotImplementedError):
    -            return Derivative(self, s)
    -
    -
    -
    [docs]class hyper(TupleParametersBase): - r""" - The (generalized) hypergeometric function is defined by a series where - the ratios of successive terms are a rational function of the summation - index. When convergent, it is continued analytically to the largest - possible domain. - - The hypergeometric function depends on two vectors of parameters, called - the numerator parameters :math:`a_p`, and the denominator parameters - :math:`b_q`. It also has an argument :math:`z`. The series definition is - - .. math :: - {}_pF_q\left(\begin{matrix} a_1, \dots, a_p \\ b_1, \dots, b_q \end{matrix} - \middle| z \right) - = \sum_{n=0}^\infty \frac{(a_1)_n \dots (a_p)_n}{(b_1)_n \dots (b_q)_n} - \frac{z^n}{n!}, - - where :math:`(a)_n = (a)(a+1)\dots(a+n-1)` denotes the rising factorial. - - If one of the :math:`b_q` is a non-positive integer then the series is - undefined unless one of the `a_p` is a larger (i.e. smaller in - magnitude) non-positive integer. If none of the :math:`b_q` is a - non-positive integer and one of the :math:`a_p` is a non-positive - integer, then the series reduces to a polynomial. To simplify the - following discussion, we assume that none of the :math:`a_p` or - :math:`b_q` is a non-positive integer. For more details, see the - references. - - The series converges for all :math:`z` if :math:`p \le q`, and thus - defines an entire single-valued function in this case. If :math:`p = - q+1` the series converges for :math:`|z| < 1`, and can be continued - analytically into a half-plane. If :math:`p > q+1` the series is - divergent for all :math:`z`. - - Note: The hypergeometric function constructor currently does *not* check - if the parameters actually yield a well-defined function. - - Examples - ======== - - The parameters :math:`a_p` and :math:`b_q` can be passed as arbitrary - iterables, for example: - - >>> from sympy.functions import hyper - >>> from sympy.abc import x, n, a - >>> hyper((1, 2, 3), [3, 4], x) - hyper((1, 2, 3), (3, 4), x) - - There is also pretty printing (it looks better using unicode): - - >>> from sympy import pprint - >>> pprint(hyper((1, 2, 3), [3, 4], x), use_unicode=False) - _ - |_ /1, 2, 3 | \ - | | | x| - 3 2 \ 3, 4 | / - - The parameters must always be iterables, even if they are vectors of - length one or zero: - - >>> hyper((1, ), [], x) - hyper((1,), (), x) - - But of course they may be variables (but if they depend on x then you - should not expect much implemented functionality): - - >>> hyper((n, a), (n**2,), x) - hyper((n, a), (n**2,), x) - - The hypergeometric function generalizes many named special functions. - The function hyperexpand() tries to express a hypergeometric function - using named special functions. - For example: - - >>> from sympy import hyperexpand - >>> hyperexpand(hyper([], [], x)) - exp(x) - - You can also use expand_func: - - >>> from sympy import expand_func - >>> expand_func(x*hyper([1, 1], [2], -x)) - log(x + 1) - - More examples: - - >>> from sympy import S - >>> hyperexpand(hyper([], [S(1)/2], -x**2/4)) - cos(x) - >>> hyperexpand(x*hyper([S(1)/2, S(1)/2], [S(3)/2], x**2)) - asin(x) - - We can also sometimes hyperexpand parametric functions: - - >>> from sympy.abc import a - >>> hyperexpand(hyper([-a], [], x)) - (-x + 1)**a - - See Also - ======== - - sympy.simplify.hyperexpand - sympy.functions.special.gamma_functions.gamma - meijerg - - References - ========== - - - Luke, Y. L. (1969), The Special Functions and Their Approximations, - Volume 1 - - http://en.wikipedia.org/wiki/Generalized_hypergeometric_function - """ - - nargs = 3 - - def __new__(cls, ap, bq, z): - # TODO should we check convergence conditions? - return Function.__new__(cls, _prep_tuple(ap), _prep_tuple(bq), z) - - @classmethod - def eval(cls, ap, bq, z): - from sympy import unpolarify - if len(ap) <= len(bq): - nz = unpolarify(z) - if z != nz: - return hyper(ap, bq, nz) - - def fdiff(self, argindex=3): - if argindex != 3: - raise ArgumentIndexError(self, argindex) - nap = Tuple(*[a + 1 for a in self.ap]) - nbq = Tuple(*[b + 1 for b in self.bq]) - fac = Mul(*self.ap)/Mul(*self.bq) - return fac*hyper(nap, nbq, self.argument) - - def _eval_expand_func(self, **hints): - from sympy import gamma, hyperexpand - if len(self.ap) == 2 and len(self.bq) == 1 and self.argument == 1: - a, b = self.ap - c = self.bq[0] - return gamma(c)*gamma(c - a - b)/gamma(c - a)/gamma(c - b) - return hyperexpand(self) - - @property -
    [docs] def argument(self): - """ Argument of the hypergeometric function. """ - return self.args[2] -
    - @property -
    [docs] def ap(self): - """ Numerator parameters of the hypergeometric function. """ - return self.args[0] -
    - @property -
    [docs] def bq(self): - """ Denominator parameters of the hypergeometric function. """ - return self.args[1] -
    - @property - def _diffargs(self): - return self.ap + self.bq - - @property -
    [docs] def eta(self): - """ A quantity related to the convergence of the series. """ - return sum(self.ap) - sum(self.bq) -
    - @property -
    [docs] def radius_of_convergence(self): - """ - Compute the radius of convergence of the defining series. - - Note that even if this is not oo, the function may still be evaluated - outside of the radius of convergence by analytic continuation. But if - this is zero, then the function is not actually defined anywhere else. - - >>> from sympy.functions import hyper - >>> from sympy.abc import z - >>> hyper((1, 2), [3], z).radius_of_convergence - 1 - >>> hyper((1, 2, 3), [4], z).radius_of_convergence - 0 - >>> hyper((1, 2), (3, 4), z).radius_of_convergence - oo - """ - if any(a.is_integer and a <= 0 for a in self.ap + self.bq): - aints = [a for a in self.ap if a.is_Integer and a <= 0] - bints = [a for a in self.bq if a.is_Integer and a <= 0] - if len(aints) < len(bints): - return S(0) - popped = False - for b in bints: - cancelled = False - while aints: - a = aints.pop() - if a >= b: - cancelled = True - break - popped = True - if not cancelled: - return S(0) - if aints or popped: - # There are still non-positive numerator parameters. - # This is a polynomial. - return oo - if len(self.ap) == len(self.bq) + 1: - return S(1) - elif len(self.ap) <= len(self.bq): - return oo - else: - return S(0) -
    - @property -
    [docs] def convergence_statement(self): - """ Return a condition on z under which the series converges. """ - from sympy import And, Or, re, Ne, oo - R = self.radius_of_convergence - if R == 0: - return False - if R == oo: - return True - # The special functions and their approximations, page 44 - e = self.eta - z = self.argument - c1 = And(re(e) < 0, abs(z) <= 1) - c2 = And(0 <= re(e), re(e) < 1, abs(z) <= 1, Ne(z, 1)) - c3 = And(re(e) >= 1, abs(z) < 1) - return Or(c1, c2, c3) - -
    -
    [docs]class meijerg(TupleParametersBase): - r""" - The Meijer G-function is defined by a Mellin-Barnes type integral that - resembles an inverse Mellin transform. It generalizes the hypergeometric - functions. - - The Meijer G-function depends on four sets of parameters. There are - "*numerator parameters*" - :math:`a_1, \dots, a_n` and :math:`a_{n+1}, \dots, a_p`, and there are - "*denominator parameters*" - :math:`b_1, \dots, b_m` and :math:`b_{m+1}, \dots, b_q`. - Confusingly, it is traditionally denoted as follows (note the position - of `m`, `n`, `p`, `q`, and how they relate to the lengths of the four - parameter vectors): - - .. math :: - G_{p,q}^{m,n} \left(\begin{matrix}a_1, \dots, a_n & a_{n+1}, \dots, a_p \\ - b_1, \dots, b_m & b_{m+1}, \dots, b_q - \end{matrix} \middle| z \right). - - However, in sympy the four parameter vectors are always available - separately (see examples), so that there is no need to keep track of the - decorating sub- and super-scripts on the G symbol. - - The G function is defined as the following integral: - - .. math :: - \frac{1}{2 \pi i} \int_L \frac{\prod_{j=1}^m \Gamma(b_j - s) - \prod_{j=1}^n \Gamma(1 - a_j + s)}{\prod_{j=m+1}^q \Gamma(1- b_j +s) - \prod_{j=n+1}^p \Gamma(a_j - s)} z^s \mathrm{d}s, - - where :math:`\Gamma(z)` is the gamma function. There are three possible - contours which we will not describe in detail here (see the references). - If the integral converges along more than one of them the definitions - agree. The contours all separate the poles of :math:`\Gamma(1-a_j+s)` - from the poles of :math:`\Gamma(b_k-s)`, so in particular the G function - is undefined if :math:`a_j - b_k \in \mathbb{Z}_{>0}` for some - :math:`j \le n` and :math:`k \le m`. - - The conditions under which one of the contours yields a convergent integral - are complicated and we do not state them here, see the references. - - Note: Currently the Meijer G-function constructor does *not* check any - convergence conditions. - - Examples - ======== - - You can pass the parameters either as four separate vectors: - - >>> from sympy.functions import meijerg - >>> from sympy.abc import x, a - >>> from sympy.core.containers import Tuple - >>> from sympy import pprint - >>> pprint(meijerg((1, 2), (a, 4), (5,), [], x), use_unicode=False) - __1, 2 /1, 2 a, 4 | \ - /__ | | x| - \_|4, 1 \ 5 | / - - or as two nested vectors: - - >>> pprint(meijerg([(1, 2), (3, 4)], ([5], Tuple()), x), use_unicode=False) - __1, 2 /1, 2 3, 4 | \ - /__ | | x| - \_|4, 1 \ 5 | / - - As with the hypergeometric function, the parameters may be passed as - arbitrary iterables. Vectors of length zero and one also have to be - passed as iterables. The parameters need not be constants, but if they - depend on the argument then not much implemented functionality should be - expected. - - All the subvectors of parameters are available: - - >>> from sympy import pprint - >>> g = meijerg([1], [2], [3], [4], x) - >>> pprint(g, use_unicode=False) - __1, 1 /1 2 | \ - /__ | | x| - \_|2, 2 \3 4 | / - >>> g.an - (1,) - >>> g.ap - (1, 2) - >>> g.aother - (2,) - >>> g.bm - (3,) - >>> g.bq - (3, 4) - >>> g.bother - (4,) - - The Meijer G-function generalizes the hypergeometric functions. - In some cases it can be expressed in terms of hypergeometric functions, - using Slater's theorem. For example: - - >>> from sympy import hyperexpand - >>> from sympy.abc import a, b, c - >>> hyperexpand(meijerg([a], [], [c], [b], x), allow_hyper=True) - x**c*gamma(-a + c + 1)*hyper((-a + c + 1,), - (-b + c + 1,), -x)/gamma(-b + c + 1) - - Thus the Meijer G-function also subsumes many named functions as special - cases. You can use expand_func or hyperexpand to (try to) rewrite a - Meijer G-function in terms of named special functions. For example: - - >>> from sympy import expand_func, S - >>> expand_func(meijerg([[],[]], [[0],[]], -x)) - exp(x) - >>> hyperexpand(meijerg([[],[]], [[S(1)/2],[0]], (x/2)**2)) - sin(x)/sqrt(pi) - - See Also - ======== - - hyper - sympy.simplify.hyperexpand - - References - ========== - - - Luke, Y. L. (1969), The Special Functions and Their Approximations, - Volume 1 - - http://en.wikipedia.org/wiki/Meijer_G-function - - """ - - nargs = 3 - - def __new__(cls, *args): - if len(args) == 5: - args = [(args[0], args[1]), (args[2], args[3]), args[4]] - if len(args) != 3: - raise TypeError("args must eiter be as, as', bs, bs', z or " - "as, bs, z") - - def tr(p): - if len(p) != 2: - raise TypeError("wrong argument") - return Tuple(_prep_tuple(p[0]), _prep_tuple(p[1])) - - # TODO should we check convergence conditions? - return Function.__new__(cls, tr(args[0]), tr(args[1]), args[2]) - - def fdiff(self, argindex=3): - if argindex != 3: - return self._diff_wrt_parameter(argindex[1]) - if len(self.an) >= 1: - a = list(self.an) - a[0] -= 1 - G = meijerg(a, self.aother, self.bm, self.bother, self.argument) - return 1/self.argument * ((self.an[0] - 1)*self + G) - elif len(self.bm) >= 1: - b = list(self.bm) - b[0] += 1 - G = meijerg(self.an, self.aother, b, self.bother, self.argument) - return 1/self.argument * (self.bm[0]*self - G) - else: - return S.Zero - - def _diff_wrt_parameter(self, idx): - # Differentiation wrt a parameter can only be done in very special - # cases. In particular, if we want to differentiate with respect to - # `a`, all other gamma factors have to reduce to rational functions. - # - # Let MT denote mellin transform. Suppose T(-s) is the gamma factor - # appearing in the definition of G. Then - # - # MT(log(z)G(z)) = d/ds T(s) = d/da T(s) + ... - # - # Thus d/da G(z) = log(z)G(z) - ... - # The ... can be evaluated as a G function under the above conditions, - # the formula being most easily derived by using - # - # d Gamma(s + n) Gamma(s + n) / 1 1 1 \ - # -- ------------ = ------------ | - + ---- + ... + --------- | - # ds Gamma(s) Gamma(s) \ s s + 1 s + n - 1 / - # - # which follows from the difference equation of the digamma function. - # (There is a similar equation for -n instead of +n). - - # We first figure out how to pair the parameters. - an = list(self.an) - ap = list(self.aother) - bm = list(self.bm) - bq = list(self.bother) - if idx < len(an): - an.pop(idx) - else: - idx -= len(an) - if idx < len(ap): - ap.pop(idx) - else: - idx -= len(ap) - if idx < len(bm): - bm.pop(idx) - else: - bq.pop(idx - len(bm)) - pairs1 = [] - pairs2 = [] - for l1, l2, pairs in [(an, bq, pairs1), (ap, bm, pairs2)]: - while l1: - x = l1.pop() - found = None - for i, y in enumerate(l2): - if not Mod((x - y).simplify(), 1): - found = i - break - if found is None: - raise NotImplementedError('Derivative not expressible ' - 'as G-function?') - y = l2[i] - l2.pop(i) - pairs.append((x, y)) - - # Now build the result. - res = log(self.argument)*self - - for a, b in pairs1: - sign = 1 - n = a - b - base = b - if n < 0: - sign = -1 - n = b - a - base = a - for k in range(n): - res -= sign*meijerg(self.an + (base + k + 1,), self.aother, - self.bm, self.bother + (base + k + 0,), - self.argument) - - for a, b in pairs2: - sign = 1 - n = b - a - base = a - if n < 0: - sign = -1 - n = a - b - base = b - for k in range(n): - res -= sign*meijerg(self.an, self.aother + (base + k + 1,), - self.bm + (base + k + 0,), self.bother, - self.argument) - - return res - -
    [docs] def get_period(self): - """ - Return a number P such that G(x*exp(I*P)) == G(x). - - >>> from sympy.functions.special.hyper import meijerg - >>> from sympy.abc import z - >>> from sympy import pi, S - - >>> meijerg([1], [], [], [], z).get_period() - 2*pi - >>> meijerg([pi], [], [], [], z).get_period() - oo - >>> meijerg([1, 2], [], [], [], z).get_period() - oo - >>> meijerg([1,1], [2], [1, S(1)/2, S(1)/3], [1], z).get_period() - 12*pi - """ - # This follows from slater's theorem. - def compute(l): - # first check that no two differ by an integer - for i, b in enumerate(l): - if not b.is_Rational: - return oo - for j in range(i + 1, len(l)): - if not Mod((b - l[j]).simplify(), 1): - return oo - return reduce(ilcm, (x.q for x in l), 1) - beta = compute(self.bm) - alpha = compute(self.an) - p, q = len(self.ap), len(self.bq) - if p == q: - if beta == oo or alpha == oo: - return oo - return 2*pi*ilcm(alpha, beta) - elif p < q: - return 2*pi*beta - else: - return 2*pi*alpha -
    - def _eval_expand_func(self, **hints): - from sympy import hyperexpand - return hyperexpand(self) - - def _eval_evalf(self, prec): - # The default code is insufficient for polar arguments. - # mpmath provides an optional argument "r", which evaluates - # G(z**(1/r)). I am not sure what its intended use is, but we hijack it - # here in the following way: to evaluate at a number z of |argument| - # less than (say) n*pi, we put r=1/n, compute z' = root(z, n) - # (carefully so as not to loose the branch information), and evaluate - # G(z'**(1/r)) = G(z'**n) = G(z). - from sympy.functions import exp_polar, ceiling - from sympy import mpmath, Expr - z = self.argument - znum = self.argument._eval_evalf(prec) - if znum.has(exp_polar): - znum, branch = znum.as_coeff_mul(exp_polar) - if len(branch) != 1: - return - branch = branch[0].args[0]/I - else: - branch = S(0) - n = ceiling(abs(branch/S.Pi)) + 1 - znum = znum**(S(1)/n)*exp(I*branch / n) - #print znum, branch, n - - # Convert all args to mpf or mpc - try: - [z, r, ap, bq] = [arg._to_mpmath(prec) - for arg in [znum, 1/n, self.args[0], self.args[1]]] - except ValueError: - return - - # Set mpmath precision and apply. Make sure precision is restored - # afterwards - orig = mpmath.mp.prec - try: - mpmath.mp.prec = prec - v = mpmath.meijerg(ap, bq, z, r) - #print ap, bq, z, r, v - finally: - mpmath.mp.prec = orig - - return Expr._from_mpmath(v, prec) - -
    [docs] def integrand(self, s): - """ Get the defining integrand D(s). """ - from sympy import gamma - return self.argument**s \ - * Mul(*(gamma(b - s) for b in self.bm)) \ - * Mul(*(gamma(1 - a + s) for a in self.an)) \ - / Mul(*(gamma(1 - b + s) for b in self.bother)) \ - / Mul(*(gamma(a - s) for a in self.aother)) -
    - @property -
    [docs] def argument(self): - """ Argument of the Meijer G-function. """ - return self.args[2] -
    - @property -
    [docs] def an(self): - """ First set of numerator parameters. """ - return self.args[0][0] -
    - @property -
    [docs] def ap(self): - """ Combined numerator parameters. """ - return self.args[0][0] + self.args[0][1] -
    - @property -
    [docs] def aother(self): - """ Second set of numerator parameters. """ - return self.args[0][1] -
    - @property -
    [docs] def bm(self): - """ First set of denominator parameters. """ - return self.args[1][0] -
    - @property -
    [docs] def bq(self): - """ Combined denominator parameters. """ - return self.args[1][0] + self.args[1][1] -
    - @property -
    [docs] def bother(self): - """ Second set of denominator parameters. """ - return self.args[1][1] -
    - @property - def _diffargs(self): - return self.ap + self.bq - - @property -
    [docs] def nu(self): - """ A quantity related to the convergence region of the integral, - c.f. references. """ - return sum(self.bq) - sum(self.ap) -
    - @property -
    [docs] def delta(self): - """ A quantity related to the convergence region of the integral, - c.f. references. """ - return len(self.bm) + len(self.an) - S(len(self.ap) + len(self.bq))/2 - -
    -class HyperRep(Function): - """ - A base class for "hyper representation functions". - - This is used exclusively in hyperexpand(), but fits more logically here. - - pFq is branched at 1 if p == q+1. For use with slater-expansion, we want - define an "analytic continuation" to all polar numbers, which is - continuous on circles and on the ray t*exp_polar(I*pi). Moreover, we want - a "nice" expression for the various cases. - - This base class contains the core logic, concrete derived classes only - supply the actual functions. - """ - - nargs = 1 - - @classmethod - def eval(cls, *args): - from sympy import unpolarify - nargs = tuple(map(unpolarify, args[:-1])) + args[-1:] - if args != nargs: - return cls(*nargs) - - @classmethod - def _expr_small(cls, x): - """ An expression for F(x) which holds for |x| < 1. """ - raise NotImplementedError - - @classmethod - def _expr_small_minus(cls, x): - """ An expression for F(-x) which holds for |x| < 1. """ - raise NotImplementedError - - @classmethod - def _expr_big(cls, x, n): - """ An expression for F(exp_polar(2*I*pi*n)*x), |x| > 1. """ - raise NotImplementedError - - @classmethod - def _expr_big_minus(cls, x, n): - """ An expression for F(exp_polar(2*I*pi*n + pi*I)*x), |x| > 1. """ - raise NotImplementedError - - def _eval_rewrite_as_nonrep(self, *args): - from sympy import Piecewise - x, n = self.args[-1].extract_branch_factor(allow_half=True) - minus = False - nargs = self.args[:-1] + (x,) - if not n.is_Integer: - minus = True - n -= S(1)/2 - nnargs = nargs + (n,) - if minus: - small = self._expr_small_minus(*nargs) - big = self._expr_big_minus(*nnargs) - else: - small = self._expr_small(*nargs) - big = self._expr_big(*nnargs) - - if big == small: - return small - return Piecewise((big, abs(x) > 1), (small, True)) - - def _eval_rewrite_as_nonrepsmall(self, *args): - x, n = self.args[-1].extract_branch_factor(allow_half=True) - args = self.args[:-1] + (x,) - if not n.is_Integer: - return self._expr_small_minus(*args) - return self._expr_small(*args) - - -class HyperRep_power1(HyperRep): - """ Return a representative for hyper([-a], [], z) == (1 - z)**a. """ - nargs = 2 - - @classmethod - def _expr_small(cls, a, x): - return (1 - x)**a - - @classmethod - def _expr_small_minus(cls, a, x): - return (1 + x)**a - - @classmethod - def _expr_big(cls, a, x, n): - if a.is_integer: - return cls._expr_small(a, x) - return (x - 1)**a*exp((2*n - 1)*pi*I*a) - - @classmethod - def _expr_big_minus(cls, a, x, n): - if a.is_integer: - return cls._expr_small_minus(a, x) - return (1 + x)**a*exp(2*n*pi*I*a) - - -class HyperRep_power2(HyperRep): - """ Return a representative for hyper([a, a - 1/2], [2*a], z). """ - nargs = 2 - - @classmethod - def _expr_small(cls, a, x): - return 2**(2*a - 1)*(1 + sqrt(1 - x))**(1 - 2*a) - - @classmethod - def _expr_small_minus(cls, a, x): - return 2**(2*a - 1)*(1 + sqrt(1 + x))**(1 - 2*a) - - @classmethod - def _expr_big(cls, a, x, n): - sgn = -1 - if n.is_odd: - sgn = 1 - n -= 1 - return 2**(2*a - 1)*(1 + sgn*I*sqrt(x - 1))**(1 - 2*a) \ - *exp(-2*n*pi*I*a) - - @classmethod - def _expr_big_minus(cls, a, x, n): - sgn = 1 - if n.is_odd: - sgn = -1 - return sgn*2**(2*a - 1)*(sqrt(1 + x) + sgn)**(1 - 2*a)*exp(-2*pi*I*a*n) - - -class HyperRep_log1(HyperRep): - """ Represent -z*hyper([1, 1], [2], z) == log(1 - z). """ - @classmethod - def _expr_small(cls, x): - return log(1 - x) - - @classmethod - def _expr_small_minus(cls, x): - return log(1 + x) - - @classmethod - def _expr_big(cls, x, n): - return log(x - 1) + (2*n - 1)*pi*I - - @classmethod - def _expr_big_minus(cls, x, n): - return log(1 + x) + 2*n*pi*I - - -class HyperRep_atanh(HyperRep): - """ Represent hyper([1/2, 1], [3/2], z) == atanh(sqrt(z))/sqrt(z). """ - @classmethod - def _expr_small(cls, x): - return atanh(sqrt(x))/sqrt(x) - - def _expr_small_minus(cls, x): - return atan(sqrt(x))/sqrt(x) - - def _expr_big(cls, x, n): - if n.is_even: - return (acoth(sqrt(x)) + I*pi/2)/sqrt(x) - else: - return (acoth(sqrt(x)) - I*pi/2)/sqrt(x) - - def _expr_big_minus(cls, x, n): - if n.is_even: - return atan(sqrt(x))/sqrt(x) - else: - return (atan(sqrt(x)) - pi)/sqrt(x) - - -class HyperRep_asin1(HyperRep): - """ Represent hyper([1/2, 1/2], [3/2], z) == asin(sqrt(z))/sqrt(z). """ - @classmethod - def _expr_small(cls, z): - return asin(sqrt(z))/sqrt(z) - - @classmethod - def _expr_small_minus(cls, z): - return asinh(sqrt(z))/sqrt(z) - - @classmethod - def _expr_big(cls, z, n): - return S(-1)**n*((S(1)/2 - n)*pi/sqrt(z) + I*acosh(sqrt(z))/sqrt(z)) - - @classmethod - def _expr_big_minus(cls, z, n): - return S(-1)**n*(asinh(sqrt(z))/sqrt(z) + n*pi*I/sqrt(z)) - - -class HyperRep_asin2(HyperRep): - """ Represent hyper([1, 1], [3/2], z) == asin(sqrt(z))/sqrt(z)/sqrt(1-z). """ - # TODO this can be nicer - @classmethod - def _expr_small(cls, z): - return HyperRep_asin1._expr_small(z) \ - /HyperRep_power1._expr_small(S(1)/2, z) - - @classmethod - def _expr_small_minus(cls, z): - return HyperRep_asin1._expr_small_minus(z) \ - /HyperRep_power1._expr_small_minus(S(1)/2, z) - - @classmethod - def _expr_big(cls, z, n): - return HyperRep_asin1._expr_big(z, n) \ - /HyperRep_power1._expr_big(S(1)/2, z, n) - - @classmethod - def _expr_big_minus(cls, z, n): - return HyperRep_asin1._expr_big_minus(z, n) \ - /HyperRep_power1._expr_big_minus(S(1)/2, z, n) - - -class HyperRep_sqrts1(HyperRep): - """ Return a representative for hyper([-a, 1/2 - a], [1/2], z). """ - nargs = 2 - - @classmethod - def _expr_small(cls, a, z): - return ((1 - sqrt(z))**(2*a) + (1 + sqrt(z))**(2*a))/2 - - @classmethod - def _expr_small_minus(cls, a, z): - return (1 + z)**a*cos(2*a*atan(sqrt(z))) - - @classmethod - def _expr_big(cls, a, z, n): - if n.is_even: - return ((sqrt(z) + 1)**(2*a)*exp(2*pi*I*n*a) + - (sqrt(z) - 1)**(2*a)*exp(2*pi*I*(n - 1)*a))/2 - else: - n -= 1 - return ((sqrt(z) - 1)**(2*a)*exp(2*pi*I*a*(n + 1)) + - (sqrt(z) + 1)**(2*a)*exp(2*pi*I*a*n))/2 - - @classmethod - def _expr_big_minus(cls, a, z, n): - if n.is_even: - return (1 + z)**a*exp(2*pi*I*n*a)*cos(2*a*atan(sqrt(z))) - else: - return (1 + z)**a*exp(2*pi*I*n*a)*cos(2*a*atan(sqrt(z)) - 2*pi*a) - - -class HyperRep_sqrts2(HyperRep): - """ Return a representative for - sqrt(z)/2*[(1-sqrt(z))**2a - (1 + sqrt(z))**2a] - == -2*z/(2*a+1) d/dz hyper([-a - 1/2, -a], [1/2], z)""" - nargs = 2 - - @classmethod - def _expr_small(cls, a, z): - return sqrt(z)*((1 - sqrt(z))**(2*a) - (1 + sqrt(z))**(2*a))/2 - - @classmethod - def _expr_small_minus(cls, a, z): - return sqrt(z)*(1 + z)**a*sin(2*a*atan(sqrt(z))) - - @classmethod - def _expr_big(cls, a, z, n): - if n.is_even: - return sqrt(z)/2*((sqrt(z) - 1)**(2*a)*exp(2*pi*I*a*(n - 1)) - - (sqrt(z) + 1)**(2*a)*exp(2*pi*I*a*n)) - else: - n -= 1 - return sqrt(z)/2*((sqrt(z) - 1)**(2*a)*exp(2*pi*I*a*(n + 1)) - - (sqrt(z) + 1)**(2*a)*exp(2*pi*I*a*n)) - - def _expr_big_minus(cls, a, z, n): - if n.is_even: - return (1 + z)**a*exp(2*pi*I*n*a)*sqrt(z)*sin(2*a*atan(sqrt(z))) - else: - return (1 + z)**a*exp(2*pi*I*n*a)*sqrt(z) \ - *sin(2*a*atan(sqrt(z)) - 2*pi*a) - - -class HyperRep_log2(HyperRep): - """ Represent log(1/2 + sqrt(1 - z)/2) == -z/4*hyper([3/2, 1, 1], [2, 2], z) """ - - @classmethod - def _expr_small(cls, z): - return log(S(1)/2 + sqrt(1 - z)/2) - - @classmethod - def _expr_small_minus(cls, z): - return log(S(1)/2 + sqrt(1 + z)/2) - - @classmethod - def _expr_big(cls, z, n): - if n.is_even: - return (n - S(1)/2)*pi*I + log(sqrt(z)/2) + I*asin(1/sqrt(z)) - else: - return (n - S(1)/2)*pi*I + log(sqrt(z)/2) - I*asin(1/sqrt(z)) - - def _expr_big_minus(cls, z, n): - if n.is_even: - return pi*I*n + log(S(1)/2 + sqrt(1 + z)/2) - else: - return pi*I*n + log(sqrt(1 + z)/2 - S(1)/2) - - -class HyperRep_cosasin(HyperRep): - """ Represent hyper([a, -a], [1/2], z) == cos(2*a*asin(sqrt(z))). """ - # Note there are many alternative expressions, e.g. as powers of a sum of - # square roots. - nargs = 2 - - @classmethod - def _expr_small(cls, a, z): - return cos(2*a*asin(sqrt(z))) - - @classmethod - def _expr_small_minus(cls, a, z): - return cosh(2*a*asinh(sqrt(z))) - - @classmethod - def _expr_big(cls, a, z, n): - return cosh(2*a*acosh(sqrt(z)) + a*pi*I*(2*n - 1)) - - @classmethod - def _expr_big_minus(cls, a, z, n): - return cosh(2*a*asinh(sqrt(z)) + 2*a*pi*I*n) - - -class HyperRep_sinasin(HyperRep): - """ Represent 2*a*z*hyper([1 - a, 1 + a], [3/2], z) - == sqrt(z)/sqrt(1-z)*sin(2*a*asin(sqrt(z))) """ - nargs = 2 - - @classmethod - def _expr_small(cls, a, z): - return sqrt(z)/sqrt(1 - z)*sin(2*a*asin(sqrt(z))) - - @classmethod - def _expr_small_minus(cls, a, z): - return -sqrt(z)/sqrt(1 + z)*sinh(2*a*asinh(sqrt(z))) - - @classmethod - def _expr_big(cls, a, z, n): - return -1/sqrt(1 - 1/z)*sinh(2*a*acosh(sqrt(z)) + a*pi*I*(2*n - 1)) - - @classmethod - def _expr_big_minus(cls, a, z, n): - return -1/sqrt(1 + 1/z)*sinh(2*a*asinh(sqrt(z)) + 2*a*pi*I*n) -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/functions/special/polynomials.html b/dev-py3k/_modules/sympy/functions/special/polynomials.html deleted file mode 100644 index 1401b8a94cb..00000000000 --- a/dev-py3k/_modules/sympy/functions/special/polynomials.html +++ /dev/null @@ -1,1277 +0,0 @@ - - - - - - - - - - sympy.functions.special.polynomials — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.functions.special.polynomials

    -"""
    -This module mainly implements special orthogonal polynomials.
    -
    -See also functions.combinatorial.numbers which contains some
    -combinatorial polynomials.
    -
    -"""
    -
    -from sympy.core.basic import C
    -from sympy.core.singleton import S
    -from sympy.core import Rational
    -from sympy.core.function import Function, ArgumentIndexError
    -from sympy.functions.elementary.miscellaneous import sqrt
    -
    -from sympy.polys.orthopolys import (
    -    jacobi_poly,
    -    gegenbauer_poly,
    -    chebyshevt_poly,
    -    chebyshevu_poly,
    -    laguerre_poly,
    -    hermite_poly,
    -    legendre_poly
    -)
    -
    -_x = C.Dummy('x')
    -
    -
    -class OrthogonalPolynomial(Function):
    -    """Base class for orthogonal polynomials.
    -    """
    -    nargs = 2
    -
    -    @classmethod
    -    def _eval_at_order(cls, n, x):
    -        if n.is_integer and n >= 0:
    -            return cls._ortho_poly(int(n), _x).subs(_x, x)
    -
    -    def _eval_conjugate(self):
    -        return self.func(self.args[0], self.args[1].conjugate())
    -
    -#----------------------------------------------------------------------------
    -# Jacobi polynomials
    -#
    -
    -
    -
    [docs]class jacobi(OrthogonalPolynomial): - r""" - Jacobi polynomial :math:`P_n^{\left(\alpha, \beta\right)}(x)` - - jacobi(n, alpha, beta, x) gives the nth Jacobi polynomial - in x, :math:`P_n^{\left(\alpha, \beta\right)}(x)`. - - The Jacobi polynomials are orthogonal on :math:`[-1, 1]` with respect - to the weight :math:`\left(1-x\right)^\alpha \left(1+x\right)^\beta`. - - Examples - ======== - - >>> from sympy import jacobi, S, conjugate, diff - >>> from sympy.abc import n,a,b,x - - >>> jacobi(0, a, b, x) - 1 - >>> jacobi(1, a, b, x) - a/2 - b/2 + x*(a/2 + b/2 + 1) - >>> jacobi(2, a, b, x) # doctest:+SKIP - (a**2/8 - a*b/4 - a/8 + b**2/8 - b/8 + x**2*(a**2/8 + a*b/4 + 7*a/8 + - b**2/8 + 7*b/8 + 3/2) + x*(a**2/4 + 3*a/4 - b**2/4 - 3*b/4) - 1/2) - - >>> jacobi(n, a, b, x) - jacobi(n, a, b, x) - - >>> jacobi(n, a, a, x) - RisingFactorial(a + 1, n)*gegenbauer(n, - a + 1/2, x)/RisingFactorial(2*a + 1, n) - - >>> jacobi(n, 0, 0, x) - legendre(n, x) - - >>> jacobi(n, S(1)/2, S(1)/2, x) - RisingFactorial(3/2, n)*chebyshevu(n, x)/(n + 1)! - - >>> jacobi(n, -S(1)/2, -S(1)/2, x) - RisingFactorial(1/2, n)*chebyshevt(n, x)/n! - - >>> jacobi(n, a, b, -x) - (-1)**n*jacobi(n, b, a, x) - - >>> jacobi(n, a, b, 0) - 2**(-n)*gamma(a + n + 1)*hyper((-b - n, -n), (a + 1,), -1)/(n!*gamma(a + 1)) - >>> jacobi(n, a, b, 1) - RisingFactorial(a + 1, n)/n! - - >>> conjugate(jacobi(n, a, b, x)) - jacobi(n, conjugate(a), conjugate(b), conjugate(x)) - - >>> diff(jacobi(n,a,b,x), x) - (a/2 + b/2 + n/2 + 1/2)*jacobi(n - 1, a + 1, b + 1, x) - - See Also - ======== - - gegenbauer, - chebyshevt_root, chebyshevu, chebyshevu_root, - legendre, assoc_legendre, - hermite, - laguerre, assoc_laguerre, - sympy.polys.orthopolys.jacobi_poly, - sympy.polys.orthopolys.gegenbauer_poly - sympy.polys.orthopolys.chebyshevt_poly - sympy.polys.orthopolys.chebyshevu_poly - sympy.polys.orthopolys.hermite_poly - sympy.polys.orthopolys.legendre_poly - sympy.polys.orthopolys.laguerre_poly - - References - ========== - - .. [1] http://en.wikipedia.org/wiki/Jacobi_polynomials - .. [2] http://mathworld.wolfram.com/JacobiPolynomial.html - .. [3] http://functions.wolfram.com/Polynomials/JacobiP/ - """ - - nargs = 4 - - @classmethod - def eval(cls, n, a, b, x): - # Simplify to other polynomials - # P^{a, a}_n(x) - if a == b: - if a == -S.Half: - return C.RisingFactorial(S.Half, n) / C.factorial(n) * chebyshevt(n, x) - elif a == S.Zero: - return legendre(n, x) - elif a == S.Half: - return C.RisingFactorial(3*S.Half, n) / C.factorial(n + 1) * chebyshevu(n, x) - else: - return C.RisingFactorial(a + 1, n) / C.RisingFactorial(2*a + 1, n) * gegenbauer(n, a + S.Half, x) - elif b == -a: - # P^{a, -a}_n(x) - return C.gamma(n + a + 1) / C.gamma(n + 1) * (1 + x)**(a/2) / (1 - x)**(a/2) * assoc_legendre(n, -a, x) - elif a == -b: - # P^{-b, b}_n(x) - return C.gamma(n - b + 1) / C.gamma(n + 1) * (1 - x)**(b/2) / (1 + x)**(b/2) * assoc_legendre(n, b, x) - - if not n.is_Number: - # Symbolic result P^{a,b}_n(x) - # P^{a,b}_n(-x) ---> (-1)**n * P^{b,a}_n(-x) - if x.could_extract_minus_sign(): - return S.NegativeOne**n * jacobi(n, b, a, -x) - # We can evaluate for some special values of x - if x == S.Zero: - return (2**(-n) * C.gamma(a + n + 1) / (C.gamma(a + 1) * C.factorial(n)) * - C.hyper([-b - n, -n], [a + 1], -1)) - if x == S.One: - return C.RisingFactorial(a + 1, n) / C.factorial(n) - elif x == S.Infinity: - if n.is_positive: - # TODO: Make sure a+b+2*n \notin Z - return C.RisingFactorial(a + b + n + 1, n) * S.Infinity - else: - # n is a given fixed integer, evaluate into polynomial - return jacobi_poly(n, a, b, x) - - def fdiff(self, argindex=4): - if argindex == 1: - # Diff wrt n - raise ArgumentIndexError(self, argindex) - elif argindex == 2: - # Diff wrt a - n, a, b, x = self.args - k = C.Dummy("k") - f1 = 1 / (a + b + n + k + 1) - f2 = ((a + b + 2*k + 1) * C.RisingFactorial(b + k + 1, n - k) / - ((n - k) * C.RisingFactorial(a + b + k + 1, n - k))) - return C.Sum(f1 * (jacobi(n, a, b, x) + f2*jacobi(k, a, b, x)), (k, 0, n - 1)) - elif argindex == 3: - # Diff wrt b - n, a, b, x = self.args - k = C.Dummy("k") - f1 = 1 / (a + b + n + k + 1) - f2 = (-1)**(n - k) * ((a + b + 2*k + 1) * C.RisingFactorial(a + k + 1, n - k) / - ((n - k) * C.RisingFactorial(a + b + k + 1, n - k))) - return C.Sum(f1 * (jacobi(n, a, b, x) + f2*jacobi(k, a, b, x)), (k, 0, n - 1)) - elif argindex == 4: - # Diff wrt x - n, a, b, x = self.args - return S.Half * (a + b + n + 1) * jacobi(n - 1, a + 1, b + 1, x) - else: - raise ArgumentIndexError(self, argindex) - - def _eval_rewrite_as_polynomial(self, n, a, b, x): - # TODO: Make sure n \in N - k = C.Dummy("k") - kern = (C.RisingFactorial(-n, k) * C.RisingFactorial(a + b + n + 1, k) * C.RisingFactorial(a + k + 1, n - k) / - C.factorial(k) * ((1 - x)/2)**k) - return 1 / C.factorial(n) * C.Sum(kern, (k, 0, n)) - - def _eval_conjugate(self): - n, a, b, x = self.args - return self.func(n, a.conjugate(), b.conjugate(), x.conjugate()) - -#---------------------------------------------------------------------------- -# Gegenbauer polynomials -# - -
    -
    [docs]class gegenbauer(OrthogonalPolynomial): - r""" - Gegenbauer polynomial :math:`C_n^{\left(\alpha\right)}(x)` - - gegenbauer(n, alpha, x) gives the nth Gegenbauer polynomial - in x, :math:`C_n^{\left(\alpha\right)}(x)`. - - The Gegenbauer polynomials are orthogonal on :math:`[-1, 1]` with - respect to the weight :math:`\left(1-x^2\right)^{\alpha-\frac{1}{2}}`. - - Examples - ======== - - >>> from sympy import gegenbauer, conjugate, diff - >>> from sympy.abc import n,a,x - >>> gegenbauer(0, a, x) - 1 - >>> gegenbauer(1, a, x) - 2*a*x - >>> gegenbauer(2, a, x) - -a + x**2*(2*a**2 + 2*a) - >>> gegenbauer(3, a, x) - x**3*(4*a**3/3 + 4*a**2 + 8*a/3) + x*(-2*a**2 - 2*a) - - >>> gegenbauer(n, a, x) - gegenbauer(n, a, x) - >>> gegenbauer(n, a, -x) - (-1)**n*gegenbauer(n, a, x) - - >>> gegenbauer(n, a, 0) - 2**n*sqrt(pi)*gamma(a + n/2)/(gamma(a)*gamma(-n/2 + 1/2)*gamma(n + 1)) - >>> gegenbauer(n, a, 1) - gamma(2*a + n)/(gamma(2*a)*gamma(n + 1)) - - >>> conjugate(gegenbauer(n, a, x)) - gegenbauer(n, conjugate(a), conjugate(x)) - - >>> diff(gegenbauer(n, a, x), x) - 2*a*gegenbauer(n - 1, a + 1, x) - - See Also - ======== - - jacobi, - chebyshevt_root, chebyshevu, chebyshevu_root, - legendre, assoc_legendre, - hermite, - laguerre, assoc_laguerre, - sympy.polys.orthopolys.jacobi_poly - sympy.polys.orthopolys.gegenbauer_poly - sympy.polys.orthopolys.chebyshevt_poly - sympy.polys.orthopolys.chebyshevu_poly - sympy.polys.orthopolys.hermite_poly - sympy.polys.orthopolys.legendre_poly - sympy.polys.orthopolys.laguerre_poly - - References - ========== - - .. [1] http://en.wikipedia.org/wiki/Gegenbauer_polynomials - .. [2] http://mathworld.wolfram.com/GegenbauerPolynomial.html - .. [3] http://functions.wolfram.com/Polynomials/GegenbauerC3/ - """ - - nargs = 3 - - @classmethod - def eval(cls, n, a, x): - # For negative n the polynomials vanish - # See http://functions.wolfram.com/Polynomials/GegenbauerC3/03/01/03/0012/ - if n.is_negative: - return S.Zero - - # Some special values for fixed a - if a == S.Half: - return legendre(n, x) - elif a == S.One: - return chebyshevu(n, x) - elif a == S.NegativeOne: - return S.Zero - - if not n.is_Number: - # Handle this before the general sign extraction rule - if x == S.NegativeOne: - if C.re(a) > S.Half: - return S.ComplexInfinity - else: - # No sec function available yet - #return (C.cos(S.Pi*(a+n)) * C.sec(S.Pi*a) * C.gamma(2*a+n) / - # (C.gamma(2*a) * C.gamma(n+1))) - return cls - - # Symbolic result C^a_n(x) - # C^a_n(-x) ---> (-1)**n * C^a_n(x) - if x.could_extract_minus_sign(): - return S.NegativeOne**n * gegenbauer(n, a, -x) - # We can evaluate for some special values of x - if x == S.Zero: - return (2**n * sqrt(S.Pi) * C.gamma(a + S.Half*n) / - (C.gamma((1 - n)/2) * C.gamma(n + 1) * C.gamma(a)) ) - if x == S.One: - return C.gamma(2*a + n) / (C.gamma(2*a) * C.gamma(n + 1)) - elif x == S.Infinity: - if n.is_positive: - return C.RisingFactorial(a, n) * S.Infinity - else: - # n is a given fixed integer, evaluate into polynomial - return gegenbauer_poly(n, a, x) - - def fdiff(self, argindex=3): - if argindex == 1: - # Diff wrt n - raise ArgumentIndexError(self, argindex) - elif argindex == 2: - # Diff wrt a - n, a, x = self.args - k = C.Dummy("k") - factor1 = 2 * (1 + (-1)**(n - k)) * (k + a) / ((k + - n + 2*a) * (n - k)) - factor2 = 2*(k + 1) / ((k + 2*a) * (2*k + 2*a + 1)) + \ - 2 / (k + n + 2*a) - kern = factor1*gegenbauer(k, a, x) + factor2*gegenbauer(n, a, x) - return C.Sum(kern, (k, 0, n - 1)) - elif argindex == 3: - # Diff wrt x - n, a, x = self.args - return 2*a*gegenbauer(n - 1, a + 1, x) - else: - raise ArgumentIndexError(self, argindex) - - def _eval_rewrite_as_polynomial(self, n, a, x): - k = C.Dummy("k") - kern = ((-1)**k * C.RisingFactorial(a, n - k) * (2*x)**(n - 2*k) / - (C.factorial(k) * C.factorial(n - 2*k))) - return C.Sum(kern, (k, 0, C.floor(n/2))) - - def _eval_conjugate(self): - n, a, x = self.args - return self.func(n, a.conjugate(), x.conjugate()) - -#---------------------------------------------------------------------------- -# Chebyshev polynomials of first and second kind -# - -
    -
    [docs]class chebyshevt(OrthogonalPolynomial): - r""" - Chebyshev polynomial of the first kind, :math:`T_n(x)` - - chebyshevt(n, x) gives the nth Chebyshev polynomial (of the first - kind) in x, :math:`T_n(x)`. - - The Chebyshev polynomials of the first kind are orthogonal on - :math:`[-1, 1]` with respect to the weight :math:`\frac{1}{\sqrt{1-x^2}}`. - - Examples - ======== - - >>> from sympy import chebyshevt, chebyshevu, diff - >>> from sympy.abc import n,x - >>> chebyshevt(0, x) - 1 - >>> chebyshevt(1, x) - x - >>> chebyshevt(2, x) - 2*x**2 - 1 - - >>> chebyshevt(n, x) - chebyshevt(n, x) - >>> chebyshevt(n, -x) - (-1)**n*chebyshevt(n, x) - >>> chebyshevt(-n, x) - chebyshevt(n, x) - - >>> chebyshevt(n, 0) - cos(pi*n/2) - >>> chebyshevt(n, -1) - (-1)**n - - >>> diff(chebyshevt(n, x), x) - n*chebyshevu(n - 1, x) - - See Also - ======== - - jacobi, gegenbauer, - chebyshevt_root, chebyshevu, chebyshevu_root, - legendre, assoc_legendre, - hermite, - laguerre, assoc_laguerre, - sympy.polys.orthopolys.jacobi_poly - sympy.polys.orthopolys.gegenbauer_poly - sympy.polys.orthopolys.chebyshevt_poly - sympy.polys.orthopolys.chebyshevu_poly - sympy.polys.orthopolys.hermite_poly - sympy.polys.orthopolys.legendre_poly - sympy.polys.orthopolys.laguerre_poly - - References - ========== - - .. [1] http://en.wikipedia.org/wiki/Chebyshev_polynomial - .. [2] http://mathworld.wolfram.com/ChebyshevPolynomialoftheFirstKind.html - .. [3] http://mathworld.wolfram.com/ChebyshevPolynomialoftheSecondKind.html - .. [4] http://functions.wolfram.com/Polynomials/ChebyshevT/ - .. [5] http://functions.wolfram.com/Polynomials/ChebyshevU/ - """ - - _ortho_poly = staticmethod(chebyshevt_poly) - - @classmethod - def eval(cls, n, x): - if not n.is_Number: - # Symbolic result T_n(x) - # T_n(-x) ---> (-1)**n * T_n(x) - if x.could_extract_minus_sign(): - return S.NegativeOne**n * chebyshevt(n, -x) - # T_{-n}(x) ---> T_n(x) - if n.could_extract_minus_sign(): - return chebyshevt(-n, x) - # We can evaluate for some special values of x - if x == S.Zero: - return C.cos(S.Half * S.Pi * n) - if x == S.One: - return S.One - elif x == S.Infinity: - return S.Infinity - else: - # n is a given fixed integer, evaluate into polynomial - if n.is_negative: - # T_{-n}(x) == T_n(x) - return cls._eval_at_order(-n, x) - else: - return cls._eval_at_order(n, x) - - def fdiff(self, argindex=2): - if argindex == 1: - # Diff wrt n - raise ArgumentIndexError(self, argindex) - elif argindex == 2: - # Diff wrt x - n, x = self.args - return n * chebyshevu(n - 1, x) - else: - raise ArgumentIndexError(self, argindex) - - def _eval_rewrite_as_polynomial(self, n, x): - k = C.Dummy("k") - kern = C.binomial(n, 2*k) * (x**2 - 1)**k * x**(n - 2*k) - return C.Sum(kern, (k, 0, C.floor(n/2))) - -
    -
    [docs]class chebyshevu(OrthogonalPolynomial): - r""" - Chebyshev polynomial of the second kind, :math:`U_n(x)` - - chebyshevu(n, x) gives the nth Chebyshev polynomial of the second - kind in x, :math:`U_n(x)`. - - The Chebyshev polynomials of the second kind are orthogonal on - :math:`[-1, 1]` with respect to the weight :math:`\sqrt{1-x^2}`. - - Examples - ======== - - >>> from sympy import chebyshevt, chebyshevu, diff - >>> from sympy.abc import n,x - >>> chebyshevu(0, x) - 1 - >>> chebyshevu(1, x) - 2*x - >>> chebyshevu(2, x) - 4*x**2 - 1 - - >>> chebyshevu(n, x) - chebyshevu(n, x) - >>> chebyshevu(n, -x) - (-1)**n*chebyshevu(n, x) - >>> chebyshevu(-n, x) - -chebyshevu(n - 2, x) - - >>> chebyshevu(n, 0) - cos(pi*n/2) - >>> chebyshevu(n, 1) - n + 1 - - >>> diff(chebyshevu(n, x), x) - (-x*chebyshevu(n, x) + (n + 1)*chebyshevt(n + 1, x))/(x**2 - 1) - - See Also - ======== - - jacobi, gegenbauer, - chebyshevt, chebyshevt_root, chebyshevu_root, - legendre, assoc_legendre, - hermite, - laguerre, assoc_laguerre, - sympy.polys.orthopolys.jacobi_poly - sympy.polys.orthopolys.gegenbauer_poly - sympy.polys.orthopolys.chebyshevt_poly - sympy.polys.orthopolys.chebyshevu_poly - sympy.polys.orthopolys.hermite_poly - sympy.polys.orthopolys.legendre_poly - sympy.polys.orthopolys.laguerre_poly - - References - ========== - - .. [1] http://en.wikipedia.org/wiki/Chebyshev_polynomial - .. [2] http://mathworld.wolfram.com/ChebyshevPolynomialoftheFirstKind.html - .. [3] http://mathworld.wolfram.com/ChebyshevPolynomialoftheSecondKind.html - .. [4] http://functions.wolfram.com/Polynomials/ChebyshevT/ - .. [5] http://functions.wolfram.com/Polynomials/ChebyshevU/ - """ - - _ortho_poly = staticmethod(chebyshevu_poly) - - @classmethod - def eval(cls, n, x): - if not n.is_Number: - # Symbolic result U_n(x) - # U_n(-x) ---> (-1)**n * U_n(x) - if x.could_extract_minus_sign(): - return S.NegativeOne**n * chebyshevu(n, -x) - # U_{-n}(x) ---> -U_{n-2}(x) - if n.could_extract_minus_sign(): - if n == S.NegativeOne: - return S.Zero - else: - return -chebyshevu(-n - 2, x) - # We can evaluate for some special values of x - if x == S.Zero: - return C.cos(S.Half * S.Pi * n) - if x == S.One: - return S.One + n - elif x == S.Infinity: - return S.Infinity - else: - # n is a given fixed integer, evaluate into polynomial - if n.is_negative: - # U_{-n}(x) ---> -U_{n-2}(x) - if n == S.NegativeOne: - return S.Zero - else: - return -cls._eval_at_order(-n - 2, x) - else: - return cls._eval_at_order(n, x) - - def fdiff(self, argindex=2): - if argindex == 1: - # Diff wrt n - raise ArgumentIndexError(self, argindex) - elif argindex == 2: - # Diff wrt x - n, x = self.args - return ((n + 1) * chebyshevt(n + 1, x) - x * chebyshevu(n, x)) / (x**2 - 1) - else: - raise ArgumentIndexError(self, argindex) - - def _eval_rewrite_as_polynomial(self, n, x): - k = C.Dummy("k") - kern = S.NegativeOne**k * C.factorial( - n - k) * (2*x)**(n - 2*k) / (C.factorial(k) * C.factorial(n - 2*k)) - return C.Sum(kern, (k, 0, C.floor(n/2))) - -
    -
    [docs]class chebyshevt_root(Function): - r""" - chebyshev_root(n, k) returns the kth root (indexed from zero) of - the nth Chebyshev polynomial of the first kind; that is, if - 0 <= k < n, chebyshevt(n, chebyshevt_root(n, k)) == 0. - - Examples - ======== - - >>> from sympy import chebyshevt, chebyshevt_root - >>> chebyshevt_root(3, 2) - -sqrt(3)/2 - >>> chebyshevt(3, chebyshevt_root(3, 2)) - 0 - - See Also - ======== - - jacobi, gegenbauer, - chebyshevt, chebyshevu, chebyshevu_root, - legendre, assoc_legendre, - hermite, - laguerre, assoc_laguerre, - sympy.polys.orthopolys.jacobi_poly - sympy.polys.orthopolys.gegenbauer_poly - sympy.polys.orthopolys.chebyshevt_poly - sympy.polys.orthopolys.chebyshevu_poly - sympy.polys.orthopolys.hermite_poly - sympy.polys.orthopolys.legendre_poly - sympy.polys.orthopolys.laguerre_poly - """ - - nargs = 2 - - @classmethod - def eval(cls, n, k): - if not 0 <= k < n: - raise ValueError("must have 0 <= k < n") - return C.cos(S.Pi*(2*k + 1)/(2*n)) - -
    -
    [docs]class chebyshevu_root(Function): - r""" - chebyshevu_root(n, k) returns the kth root (indexed from zero) of the - nth Chebyshev polynomial of the second kind; that is, if 0 <= k < n, - chebyshevu(n, chebyshevu_root(n, k)) == 0. - - Examples - ======== - - >>> from sympy import chebyshevu, chebyshevu_root - >>> chebyshevu_root(3, 2) - -sqrt(2)/2 - >>> chebyshevu(3, chebyshevu_root(3, 2)) - 0 - - See Also - ======== - - chebyshevt, chebyshevt_root, chebyshevu, - legendre, assoc_legendre, - hermite, - laguerre, assoc_laguerre, - sympy.polys.orthopolys.jacobi_poly - sympy.polys.orthopolys.gegenbauer_poly - sympy.polys.orthopolys.chebyshevt_poly - sympy.polys.orthopolys.chebyshevu_poly - sympy.polys.orthopolys.hermite_poly - sympy.polys.orthopolys.legendre_poly - sympy.polys.orthopolys.laguerre_poly - """ - - nargs = 2 - - @classmethod - def eval(cls, n, k): - if not 0 <= k < n: - raise ValueError("must have 0 <= k < n") - return C.cos(S.Pi*(k + 1)/(n + 1)) - -#---------------------------------------------------------------------------- -# Legendre polynomials and Associated Legendre polynomials -# - -
    -
    [docs]class legendre(OrthogonalPolynomial): - r""" - legendre(n, x) gives the nth Legendre polynomial of x, :math:`P_n(x)` - - The Legendre polynomials are orthogonal on [-1, 1] with respect to - the constant weight 1. They satisfy :math:`P_n(1) = 1` for all n; further, - :math:`P_n` is odd for odd n and even for even n. - - Examples - ======== - - >>> from sympy import legendre, diff - >>> from sympy.abc import x, n - >>> legendre(0, x) - 1 - >>> legendre(1, x) - x - >>> legendre(2, x) - 3*x**2/2 - 1/2 - >>> legendre(n, x) - legendre(n, x) - >>> diff(legendre(n,x), x) - n*(x*legendre(n, x) - legendre(n - 1, x))/(x**2 - 1) - - See Also - ======== - - jacobi, gegenbauer, - chebyshevt, chebyshevt_root, chebyshevu, chebyshevu_root, - assoc_legendre, - hermite, - laguerre, assoc_laguerre, - sympy.polys.orthopolys.jacobi_poly - sympy.polys.orthopolys.gegenbauer_poly - sympy.polys.orthopolys.chebyshevt_poly - sympy.polys.orthopolys.chebyshevu_poly - sympy.polys.orthopolys.hermite_poly - sympy.polys.orthopolys.legendre_poly - sympy.polys.orthopolys.laguerre_poly - - References - ========== - - .. [1] http://en.wikipedia.org/wiki/Legendre_polynomial - .. [2] http://mathworld.wolfram.com/LegendrePolynomial.html - .. [3] http://functions.wolfram.com/Polynomials/LegendreP/ - .. [4] http://functions.wolfram.com/Polynomials/LegendreP2/ - """ - - _ortho_poly = staticmethod(legendre_poly) - - @classmethod - def eval(cls, n, x): - if not n.is_Number: - # Symbolic result L_n(x) - # L_n(-x) ---> (-1)**n * L_n(x) - if x.could_extract_minus_sign(): - return S.NegativeOne**n * legendre(n, -x) - # L_{-n}(x) ---> L_{n-1}(x) - if n.could_extract_minus_sign(): - return legendre(-n - S.One, x) - # We can evaluate for some special values of x - if x == S.Zero: - return sqrt(S.Pi)/(C.gamma(S.Half - n/2)*C.gamma(S.One + n/2)) - elif x == S.One: - return S.One - elif x == S.Infinity: - return S.Infinity - else: - # n is a given fixed integer, evaluate into polynomial - if n.is_negative: - raise ValueError( - "The index n must be nonnegative integer (got %r)" % n) - else: - return cls._eval_at_order(n, x) - - def fdiff(self, argindex=2): - if argindex == 1: - # Diff wrt n - raise ArgumentIndexError(self, argindex) - elif argindex == 2: - # Diff wrt x - # Find better formula, this is unsuitable for x = 1 - n, x = self.args - return n/(x**2 - 1)*(x*legendre(n, x) - legendre(n - 1, x)) - else: - raise ArgumentIndexError(self, argindex) - - def _eval_rewrite_as_polynomial(self, n, x): - k = C.Dummy("k") - kern = (-1)**k*C.binomial(n, k)**2*((1 + x)/2)**(n - k)*((1 - x)/2)**k - return C.Sum(kern, (k, 0, n)) - -
    -
    [docs]class assoc_legendre(Function): - r""" - assoc_legendre(n,m, x) gives :math:`P_n^m(x)`, where n and m are - the degree and order or an expression which is related to the nth - order Legendre polynomial, :math:`P_n(x)` in the following manner: - - .. math:: - P_n^m(x) = (-1)^m (1 - x^2)^{\frac{m}{2}} - \frac{\mathrm{d}^m P_n(x)}{\mathrm{d} x^m} - - Associated Legendre polynomial are orthogonal on [-1, 1] with: - - - weight = 1 for the same m, and different n. - - weight = 1/(1-x**2) for the same n, and different m. - - Examples - ======== - - >>> from sympy import assoc_legendre - >>> from sympy.abc import x, m, n - >>> assoc_legendre(0,0, x) - 1 - >>> assoc_legendre(1,0, x) - x - >>> assoc_legendre(1,1, x) - -sqrt(-x**2 + 1) - >>> assoc_legendre(n,m,x) - assoc_legendre(n, m, x) - - See Also - ======== - - jacobi, gegenbauer, - chebyshevt, chebyshevt_root, chebyshevu, chebyshevu_root, - legendre, - hermite, - laguerre, assoc_laguerre, - sympy.polys.orthopolys.jacobi_poly - sympy.polys.orthopolys.gegenbauer_poly - sympy.polys.orthopolys.chebyshevt_poly - sympy.polys.orthopolys.chebyshevu_poly - sympy.polys.orthopolys.hermite_poly - sympy.polys.orthopolys.legendre_poly - sympy.polys.orthopolys.laguerre_poly - - References - ========== - - .. [1] http://en.wikipedia.org/wiki/Associated_Legendre_polynomials - .. [2] http://mathworld.wolfram.com/LegendrePolynomial.html - .. [3] http://functions.wolfram.com/Polynomials/LegendreP/ - .. [4] http://functions.wolfram.com/Polynomials/LegendreP2/ - """ - - nargs = 3 - - @classmethod - def _eval_at_order(cls, n, m): - P = legendre_poly(n, _x, polys=True).diff((_x, m)) - return (-1)**m * (1 - _x**2)**Rational(m, 2) * P.as_expr() - - @classmethod - def eval(cls, n, m, x): - if m.could_extract_minus_sign(): - # P^{-m}_n ---> F * P^m_n - return S.NegativeOne**(-m) * (C.factorial(m + n)/C.factorial(n - m)) * assoc_legendre(n, -m, x) - if m == 0: - # P^0_n ---> L_n - return legendre(n, x) - if x == 0: - return 2**m*sqrt(S.Pi) / (C.gamma((1 - m - n)/2)*C.gamma(1 - (m - n)/2)) - if n.is_Number and m.is_Number and n.is_integer and m.is_integer: - if n.is_negative: - raise ValueError("%s : 1st index must be nonnegative integer (got %r)" % (cls, n)) - if abs(m) > n: - raise ValueError("%s : abs('2nd index') must be <= '1st index' (got %r, %r)" % (cls, n, m)) - return cls._eval_at_order(int(n), abs(int(m))).subs(_x, x) - - def fdiff(self, argindex=3): - if argindex == 1: - # Diff wrt n - raise ArgumentIndexError(self, argindex) - elif argindex == 2: - # Diff wrt m - raise ArgumentIndexError(self, argindex) - elif argindex == 3: - # Diff wrt x - # Find better formula, this is unsuitable for x = 1 - n, m, x = self.args - return 1/(x**2 - 1)*(x*n*assoc_legendre(n, m, x) - (m + n)*assoc_legendre(n - 1, m, x)) - else: - raise ArgumentIndexError(self, argindex) - - def _eval_rewrite_as_polynomial(self, n, m, x): - k = C.Dummy("k") - kern = C.factorial(2*n - 2*k)/(2**n*C.factorial(n - k)*C.factorial( - k)*C.factorial(n - 2*k - m))*(-1)**k*x**(n - m - 2*k) - return (1 - x**2)**(m/2) * C.Sum(kern, (k, 0, C.floor((n - m)*S.Half))) - -#---------------------------------------------------------------------------- -# Hermite polynomials -# - -
    -
    [docs]class hermite(OrthogonalPolynomial): - r""" - hermite(n, x) gives the nth Hermite polynomial in x, :math:`H_n(x)` - - The Hermite polynomials are orthogonal on :math:`(-\infty, \infty)` - with respect to the weight :math:`\exp\left(-\frac{x^2}{2}\right)`. - - Examples - ======== - - >>> from sympy import hermite, diff - >>> from sympy.abc import x, n - >>> hermite(0, x) - 1 - >>> hermite(1, x) - 2*x - >>> hermite(2, x) - 4*x**2 - 2 - >>> hermite(n, x) - hermite(n, x) - >>> diff(hermite(n,x), x) - 2*n*hermite(n - 1, x) - >>> diff(hermite(n,x), x) - 2*n*hermite(n - 1, x) - >>> hermite(n, -x) - (-1)**n*hermite(n, x) - - See Also - ======== - - jacobi, gegenbauer, - chebyshevt, chebyshevt_root, chebyshevu, chebyshevu_root, - legendre, assoc_legendre, - laguerre, assoc_laguerre, - sympy.polys.orthopolys.jacobi_poly - sympy.polys.orthopolys.gegenbauer_poly - sympy.polys.orthopolys.chebyshevt_poly - sympy.polys.orthopolys.chebyshevu_poly - sympy.polys.orthopolys.hermite_poly - sympy.polys.orthopolys.legendre_poly - sympy.polys.orthopolys.laguerre_poly - - References - ========== - - .. [1] http://en.wikipedia.org/wiki/Hermite_polynomial - .. [2] http://mathworld.wolfram.com/HermitePolynomial.html - .. [3] http://functions.wolfram.com/Polynomials/HermiteH/ - """ - - _ortho_poly = staticmethod(hermite_poly) - - @classmethod - def eval(cls, n, x): - if not n.is_Number: - # Symbolic result H_n(x) - # H_n(-x) ---> (-1)**n * H_n(x) - if x.could_extract_minus_sign(): - return S.NegativeOne**n * hermite(n, -x) - # We can evaluate for some special values of x - if x == S.Zero: - return 2**n * sqrt(S.Pi) / C.gamma((S.One - n)/2) - elif x == S.Infinity: - return S.Infinity - else: - # n is a given fixed integer, evaluate into polynomial - if n.is_negative: - raise ValueError( - "The index n must be nonnegative integer (got %r)" % n) - else: - return cls._eval_at_order(n, x) - - def fdiff(self, argindex=2): - if argindex == 1: - # Diff wrt n - raise ArgumentIndexError(self, argindex) - elif argindex == 2: - # Diff wrt x - n, x = self.args - return 2*n*hermite(n - 1, x) - else: - raise ArgumentIndexError(self, argindex) - - def _eval_rewrite_as_polynomial(self, n, x): - k = C.Dummy("k") - kern = (-1)**k / (C.factorial(k)*C.factorial(n - 2*k)) * (2*x)**(n - 2*k) - return C.factorial(n)*C.Sum(kern, (k, 0, C.floor(n/2))) - -#---------------------------------------------------------------------------- -# Laguerre polynomials -# - -
    -
    [docs]class laguerre(OrthogonalPolynomial): - r""" - Returns the nth Laguerre polynomial in x, :math:`L_n(x)`. - - Parameters - ========== - - n : int - Degree of Laguerre polynomial. Must be ``n >= 0``. - - Examples - ======== - - >>> from sympy import laguerre, diff - >>> from sympy.abc import x, n - >>> laguerre(0, x) - 1 - >>> laguerre(1, x) - -x + 1 - >>> laguerre(2, x) - x**2/2 - 2*x + 1 - >>> laguerre(3, x) - -x**3/6 + 3*x**2/2 - 3*x + 1 - - >>> laguerre(n, x) - laguerre(n, x) - - >>> diff(laguerre(n, x), x) - -assoc_laguerre(n - 1, 1, x) - - See Also - ======== - - jacobi, gegenbauer, - chebyshevt, chebyshevt_root, chebyshevu, chebyshevu_root, - legendre, assoc_legendre, - hermite, - assoc_laguerre, - sympy.polys.orthopolys.jacobi_poly - sympy.polys.orthopolys.gegenbauer_poly - sympy.polys.orthopolys.chebyshevt_poly - sympy.polys.orthopolys.chebyshevu_poly - sympy.polys.orthopolys.hermite_poly - sympy.polys.orthopolys.legendre_poly - sympy.polys.orthopolys.laguerre_poly - - References - ========== - - .. [1] http://en.wikipedia.org/wiki/Laguerre_polynomial - .. [2] http://mathworld.wolfram.com/LaguerrePolynomial.html - .. [3] http://functions.wolfram.com/Polynomials/LaguerreL/ - .. [4] http://functions.wolfram.com/Polynomials/LaguerreL3/ - """ - - @classmethod - def eval(cls, n, x): - if not n.is_Number: - # Symbolic result L_n(x) - # L_{n}(-x) ---> exp(-x) * L_{-n-1}(x) - # L_{-n}(x) ---> exp(x) * L_{n-1}(-x) - if n.could_extract_minus_sign(): - return C.exp(x) * laguerre(n - 1, -x) - # We can evaluate for some special values of x - if x == S.Zero: - return S.One - elif x == S.NegativeInfinity: - return S.Infinity - elif x == S.Infinity: - return S.NegativeOne**n * S.Infinity - else: - # n is a given fixed integer, evaluate into polynomial - if n.is_negative: - raise ValueError( - "The index n must be nonnegative integer (got %r)" % n) - else: - return laguerre_poly(n, x, 0) - - def fdiff(self, argindex=2): - if argindex == 1: - # Diff wrt n - raise ArgumentIndexError(self, argindex) - elif argindex == 2: - # Diff wrt x - n, x = self.args - return -assoc_laguerre(n - 1, 1, x) - else: - raise ArgumentIndexError(self, argindex) - - def _eval_rewrite_as_polynomial(self, n, x): - # TODO: Should make sure n is in N_0 - k = C.Dummy("k") - kern = C.RisingFactorial(-n, k) / C.factorial(k)**2 * x**k - return C.Sum(kern, (k, 0, n)) - -
    -
    [docs]class assoc_laguerre(OrthogonalPolynomial): - r""" - Returns the nth generalized Laguerre polynomial in x, :math:`L_n(x)`. - - Parameters - ========== - - n : int - Degree of Laguerre polynomial. Must be ``n >= 0``. - - alpha : Expr - Arbitrary expression. For ``alpha=0`` regular Laguerre - polynomials will be generated. - - Examples - ======== - - >>> from sympy import laguerre, assoc_laguerre, diff - >>> from sympy.abc import x, n, a - >>> assoc_laguerre(0, a, x) - 1 - >>> assoc_laguerre(1, a, x) - a - x + 1 - >>> assoc_laguerre(2, a, x) - a**2/2 + 3*a/2 + x**2/2 + x*(-a - 2) + 1 - >>> assoc_laguerre(3, a, x) - a**3/6 + a**2 + 11*a/6 - x**3/6 + x**2*(a/2 + 3/2) + - x*(-a**2/2 - 5*a/2 - 3) + 1 - - >>> assoc_laguerre(n, a, 0) - binomial(a + n, a) - - >>> assoc_laguerre(n, a, x) - assoc_laguerre(n, a, x) - - >>> assoc_laguerre(n, 0, x) - laguerre(n, x) - - >>> diff(assoc_laguerre(n, a, x), x) - -assoc_laguerre(n - 1, a + 1, x) - - >>> diff(assoc_laguerre(n, a, x), a) - Sum(assoc_laguerre(_k, a, x)/(-a + n), (_k, 0, n - 1)) - - See Also - ======== - - jacobi, gegenbauer, - chebyshevt, chebyshevt_root, chebyshevu, chebyshevu_root, - legendre, assoc_legendre, - hermite, - laguerre, - sympy.polys.orthopolys.jacobi_poly - sympy.polys.orthopolys.gegenbauer_poly - sympy.polys.orthopolys.chebyshevt_poly - sympy.polys.orthopolys.chebyshevu_poly - sympy.polys.orthopolys.hermite_poly - sympy.polys.orthopolys.legendre_poly - sympy.polys.orthopolys.laguerre_poly - - References - ========== - - .. [1] http://en.wikipedia.org/wiki/Laguerre_polynomial#Assoc_laguerre_polynomials - .. [2] http://mathworld.wolfram.com/AssociatedLaguerrePolynomial.html - .. [3] http://functions.wolfram.com/Polynomials/LaguerreL/ - .. [4] http://functions.wolfram.com/Polynomials/LaguerreL3/ - """ - - nargs = 3 - - @classmethod - def eval(cls, n, alpha, x): - # L_{n}^{0}(x) ---> L_{n}(x) - if alpha == S.Zero: - return laguerre(n, x) - - if not n.is_Number: - # We can evaluate for some special values of x - if x == S.Zero: - return C.binomial(n + alpha, alpha) - elif x == S.Infinity and n > S.Zero: - return S.NegativeOne**n * S.Infinity - elif x == S.NegativeInfinity and n > S.Zero: - return S.Infinity - else: - # n is a given fixed integer, evaluate into polynomial - if n.is_negative: - raise ValueError( - "The index n must be nonnegative integer (got %r)" % n) - else: - return laguerre_poly(n, x, alpha) - - def fdiff(self, argindex=3): - if argindex == 1: - # Diff wrt n - raise ArgumentIndexError(self, argindex) - elif argindex == 2: - # Diff wrt alpha - n, alpha, x = self.args - k = C.Dummy("k") - return C.Sum(assoc_laguerre(k, alpha, x) / (n - alpha), (k, 0, n - 1)) - elif argindex == 3: - # Diff wrt x - n, alpha, x = self.args - return -assoc_laguerre(n - 1, alpha + 1, x) - else: - raise ArgumentIndexError(self, argindex) - - def _eval_rewrite_as_polynomial(self, n, x): - # TODO: Should make sure n is in N_0 - k = C.Dummy("k") - kern = C.RisingFactorial( - -n, k) / (C.gamma(k + alpha + 1) * C.factorial(k)) * x**k - return C.gamma(n + alpha + 1) / C.factorial(n) * C.Sum(kern, (k, 0, n))
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/functions/special/spherical_harmonics.html b/dev-py3k/_modules/sympy/functions/special/spherical_harmonics.html deleted file mode 100644 index 46ccd2fad66..00000000000 --- a/dev-py3k/_modules/sympy/functions/special/spherical_harmonics.html +++ /dev/null @@ -1,188 +0,0 @@ - - - - - - - - - - sympy.functions.special.spherical_harmonics — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.functions.special.spherical_harmonics

    -from sympy import C, pi, I
    -from sympy.core import Dummy, sympify
    -from sympy.functions import legendre, assoc_legendre
    -from sympy.functions.elementary.miscellaneous import sqrt
    -
    -Pl = legendre
    -Plm = assoc_legendre
    -
    -_x = Dummy("x")
    -
    -
    -
    [docs]def Plmcos(l, m, th): - """ - Plm(cos(th)). - """ - l = sympify(l) - m = sympify(m) - sin = C.sin - cos = C.cos - P = Plm(l, m, _x).subs(_x, cos(th)) - # assume th in (0,pi) => sin(th) is nonnegative - _sinth = Dummy("_sinth", nonnegative=True) - P = P.subs(1 - cos(th)**2, _sinth**2).subs(_sinth, sin(th)) - return P - -
    -
    [docs]def Ylm(l, m, theta, phi): - """ - Spherical harmonics Ylm. - - Examples - ======== - - >>> from sympy import symbols, Ylm - >>> theta, phi = symbols("theta phi") - >>> Ylm(0, 0, theta, phi) - 1/(2*sqrt(pi)) - >>> Ylm(1, -1, theta, phi) - sqrt(6)*exp(-I*phi)*sin(theta)/(4*sqrt(pi)) - >>> Ylm(1, 0, theta, phi) - sqrt(3)*cos(theta)/(2*sqrt(pi)) - - """ - l, m, theta, phi = [sympify(x) for x in (l, m, theta, phi)] - factorial = C.factorial - return sqrt((2*l + 1)/(4*pi) * factorial(l - m)/factorial(l + m)) * \ - Plmcos(l, m, theta) * C.exp(I*m*phi) - -
    -
    [docs]def Ylm_c(l, m, theta, phi): - """Conjugate spherical harmonics.""" - return (-1)**m * Ylm(l, -m, theta, phi) - -
    -
    [docs]def Zlm(l, m, th, ph): - """ - Real spherical harmonics. - """ - from sympy import simplify - if m > 0: - zz = C.NegativeOne()**m * \ - (Ylm(l, m, th, ph) + Ylm_c(l, m, th, ph))/sqrt(2) - elif m == 0: - return Ylm(l, m, th, ph) - else: - zz = C.NegativeOne()**m * \ - (Ylm(l, -m, th, ph) - Ylm_c(l, -m, th, ph))/(I*sqrt(2)) - - zz = zz.expand(complex=True) - zz = simplify(zz) - return zz
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/functions/special/tensor_functions.html b/dev-py3k/_modules/sympy/functions/special/tensor_functions.html deleted file mode 100644 index 7f4ef668be5..00000000000 --- a/dev-py3k/_modules/sympy/functions/special/tensor_functions.html +++ /dev/null @@ -1,547 +0,0 @@ - - - - - - - - - - sympy.functions.special.tensor_functions — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.functions.special.tensor_functions

    -from sympy.core.function import Function, C
    -from sympy.core import S, Integer
    -from sympy.core.mul import prod
    -from sympy.utilities.iterables import has_dups
    -
    -###############################################################################
    -###################### Kronecker Delta, Levi-Civita etc. ######################
    -###############################################################################
    -
    -
    -
    [docs]def Eijk(*args, **kwargs): - """ - Represent the Levi-Civita symbol. - - This is just compatibility wrapper to LeviCivita(). - - See Also - ======== - - LeviCivita - - """ - return LeviCivita(*args, **kwargs) - -
    -
    [docs]def eval_levicivita(*args): - """Evaluate Levi-Civita symbol.""" - from sympy import factorial - n = len(args) - return prod( - prod(args[j] - args[i] for j in range(i + 1, n)) - / factorial(i) for i in range(n)) - # converting factorial(i) to int is slightly faster - -
    -
    [docs]class LeviCivita(Function): - """Represent the Levi-Civita symbol. - - For even permutations of indices it returns 1, for odd permutations -1, and - for everything else (a repeated index) it returns 0. - - Thus it represents an alternating pseudotensor. - - Examples - ======== - - >>> from sympy import LeviCivita - >>> from sympy.abc import i, j, k - >>> LeviCivita(1, 2, 3) - 1 - >>> LeviCivita(1, 3, 2) - -1 - >>> LeviCivita(1, 2, 2) - 0 - >>> LeviCivita(i, j, k) - LeviCivita(i, j, k) - >>> LeviCivita(i, j, i) - 0 - - See Also - ======== - - Eijk - - """ - - is_integer = True - - @classmethod - def eval(cls, *args): - if all(isinstance(a, (int, Integer)) for a in args): - return eval_levicivita(*args) - if has_dups(args): - return S.Zero - - def doit(self): - return eval_levicivita(*self.args) - -
    -
    [docs]class KroneckerDelta(Function): - """The discrete, or Kronecker, delta function. - - A function that takes in two integers i and j. It returns 0 if i and j are - not equal or it returns 1 if i and j are equal. - - Parameters - ========== - - i : Number, Symbol - The first index of the delta function. - j : Number, Symbol - The second index of the delta function. - - Examples - ======== - - A simple example with integer indices:: - - >>> from sympy.functions.special.tensor_functions import KroneckerDelta - >>> KroneckerDelta(1, 2) - 0 - >>> KroneckerDelta(3, 3) - 1 - - Symbolic indices:: - - >>> from sympy.abc import i, j, k - >>> KroneckerDelta(i, j) - KroneckerDelta(i, j) - >>> KroneckerDelta(i, i) - 1 - >>> KroneckerDelta(i, i + 1) - 0 - >>> KroneckerDelta(i, i + 1 + k) - KroneckerDelta(i, i + k + 1) - - See Also - ======== - - eval - sympy.functions.special.delta_functions.DiracDelta - - References - ========== - - http://en.wikipedia.org/wiki/Kronecker_delta - """ - - nargs = 2 - - is_integer = True - - @classmethod -
    [docs] def eval(cls, i, j): - """ - Evaluates the discrete delta function. - - Examples - ======== - - >>> from sympy.functions.special.tensor_functions import KroneckerDelta - >>> from sympy.abc import i, j, k - - >>> KroneckerDelta(i, j) - KroneckerDelta(i, j) - >>> KroneckerDelta(i, i) - 1 - >>> KroneckerDelta(i, i + 1) - 0 - >>> KroneckerDelta(i, i + 1 + k) - KroneckerDelta(i, i + k + 1) - - # indirect doctest - - """ - if (i > j) is True: - return cls(j, i) - - diff = C.Abs(i - j) - if diff == 0: - return S.One - elif diff.is_number: - return S.Zero - elif i != 0 and diff.is_nonzero: - return KroneckerDelta(0, diff.args[0]) - - if i.assumptions0.get("below_fermi") and \ - j.assumptions0.get("above_fermi"): - return S.Zero - if j.assumptions0.get("below_fermi") and \ - i.assumptions0.get("above_fermi"): - return S.Zero -
    - @property -
    [docs] def is_above_fermi(self): - """ - True if Delta can be non-zero above fermi - - Examples - ======== - - >>> from sympy.functions.special.tensor_functions import KroneckerDelta - >>> from sympy import Symbol - >>> a = Symbol('a', above_fermi=True) - >>> i = Symbol('i', below_fermi=True) - >>> p = Symbol('p') - >>> q = Symbol('q') - >>> KroneckerDelta(p, a).is_above_fermi - True - >>> KroneckerDelta(p, i).is_above_fermi - False - >>> KroneckerDelta(p, q).is_above_fermi - True - - See Also - ======== - - is_below_fermi, is_only_below_fermi, is_only_above_fermi - - - """ - if self.args[0].assumptions0.get("below_fermi"): - return False - if self.args[1].assumptions0.get("below_fermi"): - return False - return True -
    - @property -
    [docs] def is_below_fermi(self): - """ - True if Delta can be non-zero below fermi - - Examples - ======== - - >>> from sympy.functions.special.tensor_functions import KroneckerDelta - >>> from sympy import Symbol - >>> a = Symbol('a', above_fermi=True) - >>> i = Symbol('i', below_fermi=True) - >>> p = Symbol('p') - >>> q = Symbol('q') - >>> KroneckerDelta(p, a).is_below_fermi - False - >>> KroneckerDelta(p, i).is_below_fermi - True - >>> KroneckerDelta(p, q).is_below_fermi - True - - See Also - ======== - - is_above_fermi, is_only_above_fermi, is_only_below_fermi - - """ - if self.args[0].assumptions0.get("above_fermi"): - return False - if self.args[1].assumptions0.get("above_fermi"): - return False - return True -
    - @property -
    [docs] def is_only_above_fermi(self): - """ - True if Delta is restricted to above fermi - - Examples - ======== - - >>> from sympy.functions.special.tensor_functions import KroneckerDelta - >>> from sympy import Symbol - >>> a = Symbol('a', above_fermi=True) - >>> i = Symbol('i', below_fermi=True) - >>> p = Symbol('p') - >>> q = Symbol('q') - >>> KroneckerDelta(p, a).is_only_above_fermi - True - >>> KroneckerDelta(p, q).is_only_above_fermi - False - >>> KroneckerDelta(p, i).is_only_above_fermi - False - - See Also - ======== - - is_above_fermi, is_below_fermi, is_only_below_fermi - - - """ - return ( self.args[0].assumptions0.get("above_fermi") - or - self.args[1].assumptions0.get("above_fermi") - ) or False -
    - @property -
    [docs] def is_only_below_fermi(self): - """ - True if Delta is restricted to below fermi - - Examples - ======== - - >>> from sympy.functions.special.tensor_functions import KroneckerDelta - >>> from sympy import Symbol - >>> a = Symbol('a', above_fermi=True) - >>> i = Symbol('i', below_fermi=True) - >>> p = Symbol('p') - >>> q = Symbol('q') - >>> KroneckerDelta(p, i).is_only_below_fermi - True - >>> KroneckerDelta(p, q).is_only_below_fermi - False - >>> KroneckerDelta(p, a).is_only_below_fermi - False - - See Also - ======== - - is_above_fermi, is_below_fermi, is_only_above_fermi - - - """ - return ( self.args[0].assumptions0.get("below_fermi") - or - self.args[1].assumptions0.get("below_fermi") - ) or False -
    - @property -
    [docs] def indices_contain_equal_information(self): - """ - Returns True if indices are either both above or below fermi. - - Examples - ======== - - >>> from sympy.functions.special.tensor_functions import KroneckerDelta - >>> from sympy import Symbol - >>> a = Symbol('a', above_fermi=True) - >>> i = Symbol('i', below_fermi=True) - >>> p = Symbol('p') - >>> q = Symbol('q') - >>> KroneckerDelta(p, q).indices_contain_equal_information - True - >>> KroneckerDelta(p, q+1).indices_contain_equal_information - True - >>> KroneckerDelta(i, p).indices_contain_equal_information - False - - """ - if (self.args[0].assumptions0.get("below_fermi") and - self.args[1].assumptions0.get("below_fermi")): - return True - if (self.args[0].assumptions0.get("above_fermi") - and self.args[1].assumptions0.get("above_fermi")): - return True - - # if both indices are general we are True, else false - return self.is_below_fermi and self.is_above_fermi -
    - @property -
    [docs] def preferred_index(self): - """ - Returns the index which is preferred to keep in the final expression. - - The preferred index is the index with more information regarding fermi - level. If indices contain same information, 'a' is preferred before - 'b'. - - Examples - ======== - - >>> from sympy.functions.special.tensor_functions import KroneckerDelta - >>> from sympy import Symbol - >>> a = Symbol('a', above_fermi=True) - >>> i = Symbol('i', below_fermi=True) - >>> j = Symbol('j', below_fermi=True) - >>> p = Symbol('p') - >>> KroneckerDelta(p, i).preferred_index - i - >>> KroneckerDelta(p, a).preferred_index - a - >>> KroneckerDelta(i, j).preferred_index - i - - See Also - ======== - - killable_index - - """ - if self._get_preferred_index(): - return self.args[1] - else: - return self.args[0] -
    - @property -
    [docs] def killable_index(self): - """ - Returns the index which is preferred to substitute in the final - expression. - - The index to substitute is the index with less information regarding - fermi level. If indices contain same information, 'a' is preferred - before 'b'. - - Examples - ======== - - >>> from sympy.functions.special.tensor_functions import KroneckerDelta - >>> from sympy import Symbol - >>> a = Symbol('a', above_fermi=True) - >>> i = Symbol('i', below_fermi=True) - >>> j = Symbol('j', below_fermi=True) - >>> p = Symbol('p') - >>> KroneckerDelta(p, i).killable_index - p - >>> KroneckerDelta(p, a).killable_index - p - >>> KroneckerDelta(i, j).killable_index - j - - See Also - ======== - - preferred_index - - """ - if self._get_preferred_index(): - return self.args[0] - else: - return self.args[1] -
    - def _get_preferred_index(self): - """ - Returns the index which is preferred to keep in the final expression. - - The preferred index is the index with more information regarding fermi - level. If indices contain same information, index 0 is returned. - """ - if not self.is_above_fermi: - if self.args[0].assumptions0.get("below_fermi"): - return 0 - else: - return 1 - elif not self.is_below_fermi: - if self.args[0].assumptions0.get("above_fermi"): - return 0 - else: - return 1 - else: - return 0
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/functions/special/zeta_functions.html b/dev-py3k/_modules/sympy/functions/special/zeta_functions.html deleted file mode 100644 index 18d261f1403..00000000000 --- a/dev-py3k/_modules/sympy/functions/special/zeta_functions.html +++ /dev/null @@ -1,635 +0,0 @@ - - - - - - - - - - sympy.functions.special.zeta_functions — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.functions.special.zeta_functions

    -""" Riemann zeta and related function. """
    -from sympy.core import Function, S, C, sympify, pi
    -from sympy.core.function import ArgumentIndexError
    -
    -###############################################################################
    -###################### LERCH TRANSCENDENT #####################################
    -###############################################################################
    -
    -
    -
    [docs]class lerchphi(Function): - r""" - Lerch transcendent (Lerch phi function). - - For :math:`Re(a) > 0`, :math:`|z| < 1` and :math:`s \in \mathbb{C}`, the - Lerch transcendent is defined as - - .. math :: \Phi(z, s, a) = \sum_{n=0}^\infty \frac{z^n}{(n + a)^s}, - - where the standard branch of the argument is used for :math:`n + a`, - and by analytic continuation for other values of the parameters. - - A commonly used related function is the Lerch zeta function, defined by - - .. math:: L(q, s, a) = \Phi(e^{2\pi i q}, s, a). - - **Analytic Continuation and Branching Behavior** - - It can be shown that - - .. math:: \Phi(z, s, a) = z\Phi(z, s, a+1) + a^{-s}. - - This provides the analytic continuation to :math:`Re(a) \le 0`. - - Assume now :math:`Re(a) > 0`. The integral representation - - .. math:: \Phi_0(z, s, a) = \int_0^\infty \frac{t^{s-1} e^{-at}}{1 - ze^{-t}} - \frac{\mathrm{d}t}{\Gamma(s)} - - provides an analytic continuation to :math:`\mathbb{C} - [1, \infty)`. - Finally, for :math:`x \in (1, \infty)` we find - - .. math:: \lim_{\epsilon \to 0^+} \Phi_0(x + i\epsilon, s, a) - -\lim_{\epsilon \to 0^+} \Phi_0(x - i\epsilon, s, a) - = \frac{2\pi i \log^{s-1}{x}}{x^a \Gamma(s)}, - - using the standard branch for both :math:`\log{x}` and - :math:`\log{\log{x}}` (a branch of :math:`\log{\log{x}}` is needed to - evaluate :math:`\log{x}^{s-1}`). - This concludes the analytic continuation. The Lerch transcendent is thus - branched at :math:`z \in \{0, 1, \infty\}` and - :math:`a \in \mathbb{Z}_{\le 0}`. For fixed :math:`z, a` outside these - branch points, it is an entire function of :math:`s`. - - See Also - ======== - - polylog, zeta - - References - ========== - - - Bateman, H.; Erdelyi, A. (1953), Higher Transcendental Functions, - Vol. I, New York: McGraw-Hill. Section 1.11. - - http://dlmf.nist.gov/25.14 - - http://en.wikipedia.org/wiki/Lerch_transcendent - - Examples - ======== - - The Lerch transcendent is a fairly general function, for this reason it does - not automatically evaluate to simpler functions. Use expand_func() to - achieve this. - - If :math:`z=1`, the Lerch transcendent reduces to the Hurwitz zeta function: - - >>> from sympy import lerchphi, expand_func - >>> from sympy.abc import z, s, a - >>> expand_func(lerchphi(1, s, a)) - zeta(s, a) - - More generally, if :math:`z` is a root of unity, the Lerch transcendent - reduces to a sum of Hurwitz zeta functions: - - >>> expand_func(lerchphi(-1, s, a)) - 2**(-s)*zeta(s, a/2) - 2**(-s)*zeta(s, a/2 + 1/2) - - If :math:`a=1`, the Lerch transcendent reduces to the polylogarithm: - - >>> expand_func(lerchphi(z, s, 1)) - polylog(s, z)/z - - More generally, if :math:`a` is rational, the Lerch transcendent reduces - to a sum of polylogarithms: - - >>> from sympy import S - >>> expand_func(lerchphi(z, s, S(1)/2)) - 2**(s - 1)*(polylog(s, sqrt(z))/sqrt(z) - - polylog(s, sqrt(z)*exp_polar(I*pi))/sqrt(z)) - >>> expand_func(lerchphi(z, s, S(3)/2)) - -2**s/z + 2**(s - 1)*(polylog(s, sqrt(z))/sqrt(z) - - polylog(s, sqrt(z)*exp_polar(I*pi))/sqrt(z))/z - - The derivatives with respect to :math:`z` and :math:`a` can be computed in - closed form: - - >>> lerchphi(z, s, a).diff(z) - (-a*lerchphi(z, s, a) + lerchphi(z, s - 1, a))/z - >>> lerchphi(z, s, a).diff(a) - -s*lerchphi(z, s + 1, a) - """ - - nargs = 3 - - def _eval_expand_func(self, **hints): - from sympy import exp, I, floor, Add, Poly, Dummy, exp_polar, unpolarify - z, s, a = self.args - if z == 1: - return zeta(s, a) - if s.is_Integer and s <= 0: - t = Dummy('t') - p = Poly((t + a)**(-s), t) - start = 1/(1 - t) - res = S(0) - for c in reversed(p.all_coeffs()): - res += c*start - start = t*start.diff(t) - return res.subs(t, z) - - if a.is_Rational: - # See section 18 of - # Kelly B. Roach. Hypergeometric Function Representations. - # In: Proceedings of the 1997 International Symposium on Symbolic and - # Algebraic Computation, pages 205-211, New York, 1997. ACM. - # TODO should something be polarified here? - add = S(0) - mul = S(1) - # First reduce a to the interaval (0, 1] - if a > 1: - n = floor(a) - if n == a: - n -= 1 - a -= n - mul = z**(-n) - add = Add(*[-z**(k - n)/(a + k)**s for k in range(n)]) - elif a <= 0: - n = floor(-a) + 1 - a += n - mul = z**n - add = Add(*[z**(n - 1 - k)/(a - k - 1)**s for k in range(n)]) - - m, n = S([a.p, a.q]) - zet = exp_polar(2*pi*I/n) - root = z**(1/n) - return add + mul*n**(s - 1)*Add( - *[polylog(s, zet**k*root)._eval_expand_func(**hints) - / (unpolarify(zet)**k*root)**m for k in range(n)]) - - # TODO use minpoly instead of ad-hoc methods when issue 2789 is fixed - if z.func is exp and (z.args[0]/(pi*I)).is_Rational or z in [-1, I, -I]: - # TODO reference? - if z == -1: - p, q = S([1, 2]) - elif z == I: - p, q = S([1, 4]) - elif z == -I: - p, q = S([-1, 4]) - else: - arg = z.args[0]/(2*pi*I) - p, q = S([arg.p, arg.q]) - return Add(*[exp(2*pi*I*k*p/q)/q**s*zeta(s, (k + a)/q) - for k in range(q)]) - - return lerchphi(z, s, a) - - def fdiff(self, argindex=1): - z, s, a = self.args - if argindex == 3: - return -s*lerchphi(z, s + 1, a) - elif argindex == 1: - return (lerchphi(z, s - 1, a) - a*lerchphi(z, s, a))/z - else: - raise ArgumentIndexError - - def _eval_rewrite_helper(self, z, s, a, target): - res = self._eval_expand_func() - if res.has(target): - return res - else: - return self - - def _eval_rewrite_as_zeta(self, z, s, a): - return self._eval_rewrite_helper(z, s, a, zeta) - - def _eval_rewrite_as_polylog(self, z, s, a): - return self._eval_rewrite_helper(z, s, a, polylog) - -############################################################################### -###################### POLYLOGARITHM ########################################## -############################################################################### - -
    -
    [docs]class polylog(Function): - r""" - Polylogarithm function. - - For :math:`|z| < 1` and :math:`s \in \mathbb{C}`, the polylogarithm is - defined by - - .. math:: \operatorname{Li}_s(z) = \sum_{n=1}^\infty \frac{z^n}{n^s}, - - where the standard branch of the argument is used for :math:`n`. It admits - an analytic continuation which is branched at :math:`z=1` (notably not on the - sheet of initial definition), :math:`z=0` and :math:`z=\infty`. - - The name polylogarithm comes from the fact that for :math:`s=1`, the - polylogarithm is related to the ordinary logarithm (see examples), and that - - .. math:: \operatorname{Li}_{s+1}(z) = - \int_0^z \frac{\operatorname{Li}_s(t)}{t} \mathrm{d}t. - - The polylogarithm is a special case of the Lerch transcendent: - - .. math:: \operatorname{Li}_{s}(z) = z \Phi(z, s, 1) - - See Also - ======== - - zeta, lerchphi - - Examples - ======== - - For :math:`z \in \{0, 1, -1\}`, the polylogarithm is automatically expressed - using other functions: - - >>> from sympy import polylog - >>> from sympy.abc import s - >>> polylog(s, 0) - 0 - >>> polylog(s, 1) - zeta(s) - >>> polylog(s, -1) - dirichlet_eta(s) - - If :math:`s` is a negative integer, :math:`0` or :math:`1`, the - polylogarithm can be expressed using elementary functions. This can be - done using expand_func(): - - >>> from sympy import expand_func - >>> from sympy.abc import z - >>> expand_func(polylog(1, z)) - -log(z*exp_polar(-I*pi) + 1) - >>> expand_func(polylog(0, z)) - z/(-z + 1) - - The derivative with respect to :math:`z` can be computed in closed form: - - >>> polylog(s, z).diff(z) - polylog(s - 1, z)/z - - The polylogarithm can be expressed in terms of the lerch transcendent: - - >>> from sympy import lerchphi - >>> polylog(s, z).rewrite(lerchphi) - z*lerchphi(z, s, 1) - """ - - nargs = 2 - - @classmethod - def eval(cls, s, z): - if z == 1: - return zeta(s) - elif z == -1: - return dirichlet_eta(s) - elif z == 0: - return 0 - - def fdiff(self, argindex=1): - s, z = self.args - if argindex == 2: - return polylog(s - 1, z)/z - raise ArgumentIndexError - - def _eval_rewrite_as_lerchphi(self, s, z): - return z*lerchphi(z, s, 1) - - def _eval_expand_func(self, **hints): - from sympy import log, expand_mul, Dummy, exp_polar, I - s, z = self.args - if s == 1: - return -log(1 + exp_polar(-I*pi)*z) - if s.is_Integer and s <= 0: - u = Dummy('u') - start = u/(1 - u) - for _ in range(-s): - start = u*start.diff(u) - return expand_mul(start).subs(u, z) - return polylog(s, z) - -############################################################################### -###################### HURWITZ GENERALIZED ZETA FUNCTION ###################### -############################################################################### - -
    -
    [docs]class zeta(Function): - r""" - Hurwitz zeta function (or Riemann zeta function). - - For :math:`Re(a) > 0` and :math:`Re(s) > 1`, this function is defined as - - .. math:: \zeta(s, a) = \sum_{n=0}^\infty \frac{1}{(n + a)^s}, - - where the standard choice of argument for :math:`n + a` is used. For fixed - :math:`a` with :math:`Re(a) > 0` the Hurwitz zeta function admits a - meromorphic continuation to all of :math:`\mathbb{C}`, it is an unbranched - function with a simple pole at :math:`s = 1`. - - Analytic continuation to other :math:`a` is possible under some circumstances, - but this is not typically done. - - The Hurwitz zeta function is a special case of the Lerch transcendent: - - .. math:: \zeta(s, a) = \Phi(1, s, a). - - This formula defines an analytic continuation for all possible values of - :math:`s` and :math:`a` (also :math:`Re(a) < 0`), see the documentation of - :class:`lerchphi` for a description of the branching behavior. - - If no value is passed for :math:`a`, by this function assumes a default value - of :math:`a = 1`, yielding the Riemann zeta function. - - See Also - ======== - - dirichlet_eta, lerchphi, polylog - - References - ========== - - - http://dlmf.nist.gov/25.11 - - http://en.wikipedia.org/wiki/Hurwitz_zeta_function - - Examples - ======== - - For :math:`a = 1` the Hurwitz zeta function reduces to the famous Riemann - zeta function: - - .. math:: \zeta(s, 1) = \zeta(s) = \sum_{n=1}^\infty \frac{1}{n^s}. - - >>> from sympy import zeta - >>> from sympy.abc import s - >>> zeta(s, 1) - zeta(s) - - The Riemann zeta function can also be expressed using the Dirichlet eta - function: - - >>> from sympy import dirichlet_eta - >>> zeta(s).rewrite(dirichlet_eta) - dirichlet_eta(s)/(-2**(-s + 1) + 1) - - The Riemann zeta function at positive even integer and negative odd integer - values is related to the Bernoulli numbers: - - >>> zeta(2) - pi**2/6 - >>> zeta(4) - pi**4/90 - >>> zeta(-1) - -1/12 - - The specific formulae are: - - .. math:: \zeta(2n) = (-1)^{n+1} \frac{B_{2n} (2\pi)^{2n}}{2(2n)!} - .. math:: \zeta(-n) = -\frac{B_{n+1}}{n+1} - - At negative even integers the Riemann zeta function is zero: - - >>> zeta(-4) - 0 - - No closed-form expressions are known at positive odd integers, but - numerical evaluation is possible: - - >>> zeta(3).n() - 1.20205690315959 - - The derivative of :math:`\zeta(s, a)` with respect to :math:`a` is easily - computed: - - >>> from sympy.abc import a - >>> zeta(s, a).diff(a) - -s*zeta(s + 1, a) - - However the derivative with respect to :math:`s` has no useful closed form - expression: - - >>> zeta(s, a).diff(s) - Derivative(zeta(s, a), s) - - The Hurwitz zeta function can be expressed in terms of the Lerch transcendent, - :class:`sympy.functions.special.lerchphi`: - - >>> from sympy import lerchphi - >>> zeta(s, a).rewrite(lerchphi) - lerchphi(1, s, a) - - """ - - nargs = (1, 2) - - @classmethod - def eval(cls, z, a_=None): - if a_ is None: - z, a = list(map(sympify, (z, 1))) - else: - z, a = list(map(sympify, (z, a_))) - - if a.is_Number: - if a is S.NaN: - return S.NaN - elif a is S.One and a_ is not None: - return cls(z) - # TODO Should a == 0 return S.NaN as well? - - if z.is_Number: - if z is S.NaN: - return S.NaN - elif z is S.Infinity: - return S.One - elif z is S.Zero: - if a.is_negative: - return S.Half - a - 1 - else: - return S.Half - a - elif z is S.One: - return S.ComplexInfinity - elif z.is_Integer: - if a.is_Integer: - if z.is_negative: - zeta = (-1)**z * C.bernoulli(-z + 1)/(-z + 1) - elif z.is_even: - B, F = C.bernoulli(z), C.factorial(z) - zeta = 2**(z - 1) * abs(B) * pi**z / F - else: - return - - if a.is_negative: - return zeta + C.harmonic(abs(a), z) - else: - return zeta - C.harmonic(a - 1, z) - - def _eval_rewrite_as_dirichlet_eta(self, s, a=1): - if a != 1: - return self - s = self.args[0] - return dirichlet_eta(s)/(1 - 2**(1 - s)) - - def _eval_rewrite_as_lerchphi(self, s, a=1): - return lerchphi(1, s, a) - - def fdiff(self, argindex=1): - if len(self.args) == 2: - s, a = self.args - else: - s, a = self.args + (1,) - if argindex == 2: - return -s*zeta(s + 1, a) - else: - raise ArgumentIndexError - -
    -
    [docs]class dirichlet_eta(Function): - r""" - Dirichlet eta function. - - For :math:`Re(s) > 0`, this function is defined as - - .. math:: \eta(s) = \sum_{n=1}^\infty \frac{(-1)^n}{n^s}. - - It admits a unique analytic continuation to all of :math:`\mathbb{C}`. - It is an entire, unbranched function. - - See Also - ======== - - zeta - - References - ========== - - - http://en.wikipedia.org/wiki/Dirichlet_eta_function - - Examples - ======== - - The Dirichlet eta function is closely related to the Riemann zeta function: - - >>> from sympy import dirichlet_eta, zeta - >>> from sympy.abc import s - >>> dirichlet_eta(s).rewrite(zeta) - (-2**(-s + 1) + 1)*zeta(s) - - """ - nargs = 1 - - @classmethod - def eval(cls, s): - if s == 1: - return C.log(2) - z = zeta(s) - if not z.has(zeta): - return (1 - 2**(1 - s))*z - - def _eval_rewrite_as_zeta(self, s): - return (1 - 2**(1 - s)) * zeta(s)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/galgebra/GA.html b/dev-py3k/_modules/sympy/galgebra/GA.html deleted file mode 100644 index 2042cdd6f8e..00000000000 --- a/dev-py3k/_modules/sympy/galgebra/GA.html +++ /dev/null @@ -1,2695 +0,0 @@ - - - - - - - - - - sympy.galgebra.GA — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.galgebra.GA

    -#!/usr/bin/python
    -
    -"""
    -The module symbolicGA implements symbolic Geometric Algebra in python.
    -The relevant references for this module are:
    -
    -    1. "Geometric Algebra for Physicists" by C. Doran and A. Lazenby,
    -       Cambridge University Press, 2003.
    -
    -    2. "Geometric Algebra for Computer Science" by Leo Dorst,
    -       Daniel Fontijne, and Stephen Mann, Morgan Kaufmann Publishers, 2007.
    -
    -    3. SymPy Tutorial, http://docs.sympy.org/
    -"""
    -import sys
    -import numpy
    -import sympy
    -import re as regrep
    -import sympy.galgebra.latex_ex
    -from sympy.core.decorators import deprecated
    -
    -NUMPAT = regrep.compile( '([\-0-9])|([\-0-9]/[0-9])')
    -"""Re pattern for rational number"""
    -
    -ZERO = sympy.Rational(0)
    -ONE = sympy.Rational(1)
    -TWO = sympy.Rational(2)
    -HALF = sympy.Rational(1, 2)
    -
    -from sympy.core import Pow as pow_type
    -from sympy import Abs as abs_type
    -from sympy.core import Mul as mul_type
    -from sympy.core import Add as add_type
    -
    -
    -@sympy.vectorize(0)
    -def substitute_array(array, *args):
    -    return(array.subs(*args))
    -
    -
    -
    [docs]def is_quasi_unit_numpy_array(array): - """ - Determine if a array is square and diagonal with - entries of +1 or -1. - """ - shape = numpy.shape(array) - if len(shape) == 2 and (shape[0] == shape[1]): - n = shape[0] - ix = 0 - while ix < n: - iy = 0 - while iy < ix: - if array[ix][iy] != ZERO: - return(False) - iy += 1 - if sympy.Abs(array[ix][ix]) != ONE: - return(False) - ix += 1 - return(True) - else: - return(False) - -
    -@deprecated(value="This function is no longer needed.", issue=3379, - deprecated_since_version="0.7.2") -def set_main(main_program): - pass - - -def plist(lst): - if type(lst) == list: - for x in lst: - plist(x) - else: - sys.stderr.write(lst + '\n') - return - - -
    [docs]def numeric(num_str): - """ - Returns rational numbers compatible with symbols. - Input is a string representing a fraction or integer - or a simple integer. - """ - if type(num_str) == int: - a = num_str - b = 1 - else: - tmp = num_str.split('/') - if len(tmp) == 1: - a = int(tmp[0]) - b = 1 - else: - a = int(tmp[0]) - b = int(tmp[1]) - return(sympy.Rational(a, b)) - -
    -
    [docs]def collect(expr, lst): - """ - Wrapper for sympy.collect. - """ - lst = MV.scalar_to_symbol(lst) - return(sympy.collect(expr, lst)) - -
    -def sqrt(expr): - return(sympy.sqrt(expr)) - - -
    [docs]def isint(a): - """ - Test for integer. - """ - return(type(a) == int) - -
    -
    [docs]def make_null_array(n): - """ - Return list of n empty lists. - """ - a = [] - for i in range(n): - a.append([]) - return(a) - -
    -
    [docs]def test_int_flgs(lst): - """ - Test if all elements in list are 0. - """ - for i in lst: - if i: - return(1) - return(0) - -
    -
    [docs]def comb(N, P): - """ - Calculates the combinations of the integers [0,N-1] taken P at a time. - The output is a list of lists of integers where the inner lists are - the different combinations. Each combination is sorted in ascending - order. - """ - def rloop(n, p, combs, comb): - if p: - for i in range(n): - newcomb = comb + [i] - np = p - 1 - rloop(i, np, combs, newcomb) - else: - combs.append(comb) - combs = [] - rloop(N, P, combs, []) - for comb in combs: - comb.sort() - return(combs) - -
    -
    [docs]def diagpq(p, q=0): - """ - Return string equivalent metric tensor for signature (p, q). - """ - n = p + q - D = [] - rn = list(range(n)) - for i in rn: - for j in rn: - if i == j: - if i < p: - D.append('1 ') - else: - D.append('-1 ') - else: - D.append('0 ') - return ','.join(D) - -
    -
    [docs]def make_scalars(symnamelst): - """ - make_scalars takes a string of symbol names separated by - blanks and converts them to MV scalars and returns a list - of the symbols. - """ - symlst = sympy.symbols(symnamelst) - scalar_lst = [] - for s in symlst: - tmp = MV(s, 'scalar') - scalar_lst.append(tmp) - return(scalar_lst) - -
    -@deprecated(useinstead="sympy.symbols()", issue=3379, - deprecated_since_version="0.7.2") -def make_symbols(symnamelst): - return sympy.symbols(symnamelst) - - -
    [docs]def israt(numstr): - """ - Test if string represents a rational number. - """ - global NUMPAT - if NUMPAT.match(numstr): - return(1) - return(0) - -
    -
    [docs]def dualsort(lst1, lst2): - """ - Inplace dual sort of lst1 and lst2 keyed on sorted lst1. - """ - _indices = list(range(len(lst1))) - _indices.sort(key=lst2.__getitem__) - lst1[:] = list(map(lst1.__getitem__, _indices)) - lst2[:] = list(map(lst2.__getitem__, _indices)) - return - -
    -
    [docs]def cp(A, B): - """ - Calculates the commutator product (A*B-B*A)/2 for - the objects A and B. - """ - return(HALF*(A*B - B*A)) - -
    -
    [docs]def reduce_base(k, base): - """ - If base is a list of sorted integers [i_1,...,i_R] then reduce_base - sorts the list [k,i_1,...,i_R] and calculates whether an odd or even - number of permutations is required to sort the list. The sorted list - is returned and +1 for even permutations or -1 for odd permutations. - """ - if k in base: - return(0, base) - grade = len(base) - if grade == 1: - if k < base[0]: - return(1, [k, base[0]]) - else: - return(-1, [base[0], k]) - ilo = 0 - ihi = grade - 1 - if k < base[0]: - return(1, [k] + base) - if k > base[ihi]: - if grade % 2 == 0: - return(1, base + [k]) - else: - return(-1, base + [k]) - imid = ihi + ilo - if grade == 2: - return(-1, [base[0], k, base[1]]) - while True: - if ihi - ilo == 1: - break - if base[imid] > k: - ihi = imid - else: - ilo = imid - imid = (ilo + ihi)/2 - if ilo % 2 == 1: - return(1, base[:ihi] + [k] + base[ihi:]) - else: - return(-1, base[:ihi] + [k] + base[ihi:]) - -
    -
    [docs]def sub_base(k, base): - """ - If base is a list of sorted integers [i_1,...,i_R] then sub_base returns - a list with the k^th element removed. Note that k=0 removes the first - element. There is no test to see if k is in the range of the list. - """ - n = len(base) - if n == 1: - return([]) - if n == 2: - if k == base[0]: - return([base[1]]) - else: - return([base[0]]) - return(base[:k] + base[k + 1:]) - -
    -
    [docs]def magnitude(vector): - """ - Calculate magnitude of vector containing trig expressions - and simplify. This is a hack because of way he sign of - magsq is determined and because of the way that absolute - values are removed. - """ - magsq = sympy.expand((vector | vector)()) - magsq = sympy.trigsimp(magsq, deep=True, recursive=True) - #print magsq - magsq_str = sympy.galgebra.latex_ex.LatexPrinter()._print(magsq) - if magsq_str[0] == '-': - magsq = -magsq - mag = unabs(sqrt(magsq)) - #print mag - return(mag) - -
    -
    [docs]def LaTeX_lst(lst, title=''): - """ - Output a list in LaTeX format. - """ - if title != '': - sympy.galgebra.latex_ex.LaTeX(title) - for x in lst: - sympy.galgebra.latex_ex.LaTeX(x) - return - -
    -
    [docs]def unabs(x): - """ - Remove absolute values from expressions so a = sqrt(a**2). - This is a hack. - """ - if type(x) == mul_type: - y = unabs(x.args[0]) - for yi in x.args[1:]: - y *= unabs(yi) - return(y) - if type(x) == pow_type: - if x.args[1] == HALF and type(x.args[0]) == add_type: - return(x) - y = 1/unabs(x.args[0]) - return(y) - if len(x.args) == 0: - return(x) - if type(x) == abs_type: - return(x.args[0]) - return(x) - -
    -def function_lst(fstr, xtuple): - sys.stderr.write(fstr + '\n') - fct_lst = [] - for xstr in fstr.split(): - f = sympy.Function(xstr)(*xtuple) - fct_lst.append(f) - return(fct_lst) - - -
    [docs]def vector_fct(Fstr, x): - """ - Create a list of functions of arguments x. One function is - created for each variable in x. Fstr is a string that is - the base name of each function while each function in the - list is given the name Fstr+'__'+str(x[ix]) so that if - Fstr = 'f' and str(x[1]) = 'theta' then the LaTeX output - of the second element in the output list would be 'f^{\\theta}'. - """ - nx = len(x) - Fvec = [] - for ix in range(nx): - ftmp = sympy.Function(Fstr + '__' + sympy.galgebra.latex_ex.LatexPrinter.str_basic(x[ix]))(*tuple(x)) - Fvec.append(ftmp) - return(Fvec) - -
    -def print_lst(lst): - for x in lst: - print(x) - return - - -
    [docs]def normalize(elst, nname_lst): - """ - Normalize a list of vectors and rename the normalized vectors. 'elist' is the list - (or array) of vectors to be normalized and nname_lst is a list of the names for the - normalized vectors. The function returns the numpy arrays enlst and mags containing - the normalized vectors (enlst) and the magnitudes of the original vectors (mags). - """ - i = 0 - mags = numpy.array(MV.n*[ZERO], dtype=numpy.object) - enlst = numpy.array(MV.n*[ZERO], dtype=numpy.object) - for (e, nname) in zip(elst, nname_lst): - emag = magnitude(e) - emaginv = 1/emag - mags[i] = emag - enorm = emaginv*e - enorm.name = nname - enlst[i] = enorm - i += 1 - return(enlst, mags) - -
    -def build_base(base_index, base_vectors, reverse=False): - base = base_vectors[base_index[0]] - if len(base_index) > 1: - for i in base_index[1:]: - base = base ^ base_vectors[i] - if reverse: - base = base.rev() - return(base) - - -class MV(object): - - is_setup = False - basislabel_lst = 0 - curvilinear_flg = False - coords = None - - @staticmethod - def pad_zeros(value, n): - """ - Pad list with zeros to length n. If length is > n - truncate list. Return padded list. - """ - nvalue = len(value) - if nvalue < n: - value = value + (n - nvalue)*[ZERO] - if nvalue > n: - value = value[:n] - return(value) - - @staticmethod - def define_basis(basis): - """ - Calculates all the MV static variables needed for - basis operations. See reference 5 section 2. - """ - MV.vbasis = basis - MV.vsyms = sympy.symbols(MV.vbasis) - MV.n = len(MV.vbasis) - MV.nrg = list(range(MV.n)) - MV.n1 = MV.n + 1 - MV.n1rg = list(range(MV.n1)) - MV.npow = 2**MV.n - MV.index = list(range(MV.n)) - MV.gabasis = [[]] - MV.basis = (MV.n + 1)*[0] - MV.basislabel = (MV.n + 1)*[0] - MV.basis[0] = [] - MV.basislabel[0] = '1' - MV.basislabel_lst = [['1']] - MV.nbasis = numpy.array((MV.n + 1)*[1], dtype=numpy.object) - for igrade in range(1, MV.n + 1): - tmp = comb(MV.n, igrade) - MV.gabasis += [tmp] - ntmp = len(tmp) - MV.nbasis[igrade] = ntmp - MV.basis[igrade] = tmp - gradelabels = [] - gradelabel_lst = [] - for i in range(ntmp): - tmp_lst = [] - bstr = '' - for j in tmp[i]: - bstr += MV.vbasis[j] - tmp_lst.append(MV.vbasis[j]) - gradelabel_lst.append(tmp_lst) - gradelabels.append(bstr) - MV.basislabel_lst.append(gradelabel_lst) - MV.basislabel[igrade] = gradelabels - MV.basis_map = [{'':0}] - igrade = 1 - while igrade <= MV.n: - tmpdict = {} - bases = MV.gabasis[igrade] - ibases = 0 - for base in bases: - tmpdict[str(base)] = ibases - ibases += 1 - MV.basis_map.append(tmpdict) - igrade += 1 - - if MV.debug: - print('basis strings =', MV.vbasis) - print('basis symbols =', MV.vsyms) - print('basis labels =', MV.basislabel) - print('basis =', MV.basis) - print('grades =', MV.nbasis) - print('index =', MV.index) - return - - @staticmethod - def define_metric(metric): - """ - Calculates all the MV static variables needed for - metric operations. See reference 5 section 2. - """ - if MV.metric_str: - MV.g = [] - MV.metric = numpy.array(MV.n*[MV.n*[ZERO]], dtype=numpy.object) - if metric == '': - metric = numpy.array(MV.n*[MV.n*['#']], dtype=numpy.object) - for i in MV.index: - for j in MV.index: - gij = metric[i][j] - if israt(gij): - MV.metric[i][j] = numeric(gij) - else: - if gij == '#': - if i == j: - gij = '(' + MV.vbasis[j] + '**2)' - else: - gij = '(' + MV.vbasis[min( - i, j)] + '.' + MV.vbasis[max(i, j)] + ')' - tmp = sympy.Symbol(gij) - MV.metric[i][j] = tmp - if i <= j: - MV.g.append(tmp) - else: - MV.metric = metric - MV.g = [] - for row in metric: - g_row = [] - for col in metric: - g_row.append(col) - MV.g.append(g_row) - if MV.debug: - print('metric =', MV.metric) - return - - @staticmethod - def define_reciprocal_frame(): - """ - Calculates unscaled reciprocal vectors (MV.brecp) and scale - factor (MV.Esq). The ith scaled reciprocal vector is - (1/MV.Esq)*MV.brecp[i]. The pseudoscalar for the set of - basis vectors is MV.E. - """ - if MV.tables_flg: - MV.E = MV.bvec[0] - MV.brecp = [] - for i in range(1, MV.n): - MV.E = MV.E ^ MV.bvec[i] - for i in range(MV.n): - tmp = ONE - if i % 2 != 0: - tmp = -ONE - for j in range(MV.n): - if i != j: - tmp = tmp ^ MV.bvec[j] - tmp = tmp*MV.E - MV.brecp.append(tmp) - MV.Esq = (MV.E*MV.E)() - MV.Esq_inv = ONE/MV.Esq - for i in range(MV.n): - MV.brecp[i] = MV.brecp[i]*MV.Esq_inv - return - - @staticmethod - def reduce_basis_loop(blst): - """ - Makes one pass through basis product representation for - reduction of representation to normal form. - See reference 5 section 3. - """ - nblst = len(blst) - if nblst <= 1: - return(1) - jstep = 1 - while jstep < nblst: - istep = jstep - 1 - if blst[istep] == blst[jstep]: - i = blst[istep] - if len(blst) > 2: - blst = blst[:istep] + blst[jstep + 1:] - else: - blst = [] - if len(blst) <= 1 or jstep == nblst - 1: - blst_flg = 0 - else: - blst_flg = 1 - return(MV.metric[i][i], blst, blst_flg) - if blst[istep] > blst[jstep]: - blst1 = blst[:istep] + blst[jstep + 1:] - a1 = TWO*MV.metric[blst[jstep]][blst[istep]] - blst = blst[:istep] + [blst[jstep]] + [blst[istep]] + blst[jstep + 1:] - if len(blst1) <= 1: - blst1_flg = 0 - else: - blst1_flg = 1 - return(a1, blst1, blst1_flg, blst) - jstep += 1 - return(1) - - @staticmethod - def reduce_basis(blst): - """ - Repetitively applies reduce_basis_loop to basis - product representation until normal form is - realized. See reference 5 section 3. - """ - if blst == []: - blst_coef = [] - blst_expand = [] - for i in MV.n1rg: - blst_coef.append([]) - blst_expand.append([]) - blst_expand[0].append([]) - blst_coef[0].append(ONE) - return(blst_coef, blst_expand) - blst_expand = [blst] - blst_coef = [ONE] - blst_flg = [1] - while test_int_flgs(blst_flg): - for i in range(len(blst_flg)): - if blst_flg[i]: - tmp = MV.reduce_basis_loop(blst_expand[i]) - if tmp == 1: - blst_flg[i] = 0 - else: - if len(tmp) == 3: - blst_coef[i] = tmp[0]*blst_coef[i] - blst_expand[i] = tmp[1] - blst_flg[i] = tmp[2] - else: - blst_coef[i] = -blst_coef[i] - blst_flg[i] = 1 - blst_expand[i] = tmp[3] - blst_coef.append(-blst_coef[i]*tmp[0]) - blst_expand.append(tmp[1]) - blst_flg.append(tmp[2]) - (blst_coef, blst_expand) = MV.combine_common_factors( - blst_coef, blst_expand) - return(blst_coef, blst_expand) - - @staticmethod - def combine_common_factors(blst_coef, blst_expand): - new_blst_coef = [] - new_blst_expand = [] - for i in range(MV.n1): - new_blst_coef.append([]) - new_blst_expand.append([]) - nfac = len(blst_coef) - for ifac in range(nfac): - blen = len(blst_expand[ifac]) - new_blst_coef[blen].append(blst_coef[ifac]) - new_blst_expand[blen].append(blst_expand[ifac]) - for i in range(MV.n1): - if len(new_blst_coef[i]) > 1: - MV.contract(new_blst_coef[i], new_blst_expand[i]) - return(new_blst_coef, new_blst_expand) - - @staticmethod - def contract(coefs, bases): - dualsort(coefs, bases) - n = len(bases) - 1 - i = 0 - while i < n: - j = i + 1 - if bases[i] == bases[j]: - coefs[i] += coefs[j] - bases.pop(j) - coefs.pop(j) - n -= 1 - else: - i += 1 - n = len(coefs) - i = 0 - while i < n: - if coefs[i] == ZERO: - coefs.pop(i) - bases.pop(i) - n -= 1 - else: - i += 1 - return - - @staticmethod - def convert(coefs, bases): - mv = MV() - mv.bladeflg = 0 - for igrade in MV.n1rg: - coef = coefs[igrade] - base = bases[igrade] - if len(coef) > 0: - nbases = MV.nbasis[igrade] - mv.mv[igrade] = numpy.array(nbases*[ZERO], dtype=numpy.object) - nbaserg = list(range(len(base))) - for ibase in nbaserg: - if igrade > 0: - k = MV.basis[igrade].index(base[ibase]) - mv.mv[igrade][k] = coef[ibase] - else: - mv.mv[igrade] = numpy.array( - [coef[0]], dtype=numpy.object) - return(mv) - - @staticmethod - def set_str_format(str_mode=0): - MV.str_mode = str_mode - return - - @staticmethod - def str_rep(mv): - """ - Converts internal representation of a multivector to a string - for outputting. If lst_mode = 1, str_rep outputs a list of - strings where each string contains one multivector coefficient - concatenated with the corresponding base or blade symbol. - - MV.str_mode Effect - 0 Print entire multivector on one line (default) - 1 Print each grade on a single line - 2 Print each base on a single line - """ - - if MV.bladeprint: - mv.convert_to_blades() - labels = MV.bladelabel - else: - if not mv.bladeflg: - labels = MV.basislabel - else: - labels = MV.bladelabel - mv.compact() - if isinstance(mv.mv[0], int): - value = '' - else: - value = (mv.mv[0][0]).__str__() - value = value.replace(' ', '') - dummy = sympy.Dummy('dummy') - for igrade in MV.n1rg[1:]: - if isinstance(mv.mv[igrade], numpy.ndarray): - j = 0 - for x in mv.mv[igrade]: - if x != ZERO: - xstr = (x*dummy).__str__() - xstr = xstr.replace(' ', '') - if xstr[0] != '-' and len(value) > 0: - xstr = '+' + xstr - if xstr.find('_dummy') < 2 and xstr[-5:] != '_dummy': - xstr = xstr.replace( - '_dummy*', '') + '*' + labels[igrade][j] - else: - xstr = xstr.replace('_dummy', labels[igrade][j]) - if MV.str_mode == 2: - xstr += '\n' - value += xstr - j += 1 - if MV.str_mode == 1: - value += '\n' - if value == '': - value = '0' - #value = value.replace(' ','') - value = value.replace('dummy', '1') - return(value) - - @staticmethod - def xstr_rep(mv, lst_mode=0): - """ - Converts internal representation of a multivector to a string - for outputing. If lst_mode = 1, str_rep outputs a list of - strings where each string contains one multivector coefficient - concatenated with the corresponding base or blade symbol. - """ - if lst_mode: - outlst = [] - if MV.bladeprint: - mv.convert_to_blades() - labels = MV.bladelabel - else: - if not mv.bladeflg: - labels = MV.basislabel - else: - labels = MV.bladelabel - value = '' - for igrade in MV.n1rg: - tmp = [] - if isinstance(mv.mv[igrade], numpy.ndarray): - j = 0 - for x in mv.mv[igrade]: - if x != ZERO: - xstr = x.__str__() - if xstr == '+1' or xstr == '1' or xstr == '-1': - if xstr == '+1' or xstr == '1': - xstr = '+' - else: - xstr = '-' - else: - if xstr[0] != '+': - xstr = '+(' + xstr + ')' - else: - xstr = '+(' + xstr[1:] + ')' - value += xstr + labels[igrade][j] - if MV.str_mode and not lst_mode: - value += value + '\n' - if lst_mode: - tmp.append(value) - j += 1 - if lst_mode: - if len(tmp) > 0: - outlst.append(tmp) - value = '' - if not lst_mode: - if len(value) > 1 and value[0] == '+': - value = value[1:] - if len(value) == 0: - value = '0' - else: - value = outlst - return(value) - - @staticmethod - def setup(basis, metric='', rframe=False, coords=None, debug=False, offset=0): - """ - MV.setup initializes the MV class by calculating the static - multivector tables required for geometric algebra operations - on multivectors. See reference 5 section 2 for details on - basis and metric arguments. - """ - MV.is_setup = True - MV.metric_str = False - MV.debug = debug - MV.bladeprint = 0 - MV.tables_flg = 0 - MV.str_mode = 0 - MV.basisroot = '' - MV.index_offset = offset - if coords is None: - MV.coords = None - else: - MV.coords = tuple(coords) - rframe = True - if type(basis) == str: - basislst = basis.split() - if len(basislst) == 1: - MV.basisroot = basislst[0] - basislst = [] - for coord in coords: - basislst.append(MV.basisroot + '_' + str(coord)) - MV.define_basis(basislst) - if type(metric) == str: - MV.metric_str = True - if len(metric) > 0: - if metric[0] == '[' and metric[-1] == ']': - tmps = metric[1:-1].split(',') - N = len(tmps) - metric = [] - itmp = 0 - for tmp in tmps: - xtmp = N*['0'] - xtmp[itmp] = tmp - itmp += 1 - metric.append(xtmp) - else: - tmps = metric.split(',') - metric = [] - for tmp in tmps: - xlst = tmp.split() - xtmp = [] - for x in xlst: - xtmp.append(x) - metric.append(xtmp) - - MV.define_metric(metric) - MV.multiplication_table() - MV.blade_table() - MV.inverse_blade_table() - MV.tables_flg = 1 - isym = 0 - MV.bvec = [] - for name in MV.vbasis: - bvar = MV(value=isym, mvtype='basisvector', mvname=name) - bvar.bladeflg = 1 - MV.bvec.append(bvar) - isym += 1 - if rframe: - MV.define_reciprocal_frame() - MV.I = MV(ONE, 'pseudo', 'I') - MV.ZERO = MV() - Isq = (MV.I*MV.I)() - MV.Iinv = (1/Isq)*MV.I - return MV.bvec - - @staticmethod - def set_coords(coords): - MV.coords = coords - return - - @staticmethod - def scalar_fct(fct_name): - """ - Create multivector scalar function with name fct_name (string) and - independent variables coords (list of variable). Default variables are - those associated with each dimension of vector space. - """ - phi = sympy.Function(fct_name)(*MV.coords) - Phi = MV(phi, 'scalar') - Phi.name = fct_name - return(Phi) - - @staticmethod - def vector_fct(fct_name, vars=''): - """ - Create multivector vector function with name fct_name (string) and - independent variables coords (list of variable). Default variables are - those associated with each dimension of vector space. - """ - if isinstance(vars, str): - Acoefs = vector_fct(fct_name, MV.coords) - else: - Acoefs = numpy.array(MV.n*[ZERO], dtype=numpy.object) - x = MV.coords - if isinstance(vars, sympy.core.symbol.Symbol): - for icoef in MV.nrg: - Acoefs[icoef] = sympy.Function(fct_name + '__' + - sympy.galgebra.latex_ex.LatexPrinter.str_basic(x[icoef]))(vars) - else: - for icoef in MV.nrg: - Acoefs[icoef] = sympy.Function(fct_name + '__' + - sympy.galgebra.latex_ex.LatexPrinter.str_basic(x[icoef]))(*tuple(vars)) - A = MV(Acoefs, 'vector', fct_name) - return(A) - - @staticmethod - def rebase(x, coords, base_name='', debug=False, debug_level=0): - """ - Define curvilinear coordinates for previously defined vector (multivector) space (MV.setup has been run) - with position vector, x, that is a vector function of the independent coordinates, coords (list of - sympy variables equal in length to dimension of vector space), and calculate: - - 1. Frame (basis) vectors - 2. Normalized frame (basis) vectors. - 3. Metric tensor - 4. Reciprocal frame vectors - 5. Reciprocal metric tensor - 6. Connection multivectors - - The basis vectors are named with the base_name (string) and a subscript derived from the name of each - coordinate. So that if the base name is 'e' and the coordinated are [r,theta,z] the variable names - of the frame vectors would be e_r, e_theta, and e_z. For LaTeX output the names of the frame vectors - would be e_{r}, e_{\theta}, and e_{z}. Everything needed to compute the geometric, outer, and inner - derivatives of multivector functions in curvilinear coordinates is calculated. - - If debug is True all the quantities in the above list are output in LaTeX format. - - Currently rebase works with cylindrical and spherical coordinates in any dimension. The limitation is the - ability to automatically simplify complex sympy expressions generated while calculating the quantities in - the above list. This is why the debug option is included. The debug_level can equal 0,1,2, or 3 and - determines how far in the list to calculate (input 0 to do the entire list) while debugging. - """ - #Form root names for basis, reciprocal basis, normalized basis, and normalized reciprocal basis - - if base_name == '': - base_name = MV.basisroot + 'prm' - - LaTeX_base = sympy.galgebra.latex_ex.LatexPrinter.extended_symbol( - base_name) - bm = '\\bm{' + LaTeX_base + '}' - bmhat = '\\hat{' + bm + '}' - bstr = bmhat + '_{[i_{1},\dots, i_{R}]}' - base_name += 'bm' - base_name_hat = base_name + 'hat' - - base_name_lst = [] - nbase_name_lst = [] - rbase_name_lst = [] - rnbase_name_lst = [] - coords_lst = [] - - for coord in coords: - coord_str = sympy.galgebra.latex_ex.LatexPrinter.str_basic(coord) - coords_lst.append(coord_str) - base_name_lst.append(base_name + '_' + coord_str) - rbase_name_lst.append(base_name + '__' + coord_str) - nbase_name_lst.append(base_name_hat + '_' + coord_str) - rnbase_name_lst.append(base_name_hat + '__' + coord_str) - - if not (MV.n == len(coords) == len(base_name_lst)): - print('rebaseMV inputs not congruent:') - print('MV.n =', MV.n) - print('coords =', coords) - print('bases =', base_name) - sys.exit(1) - - if isinstance(x, MV): - - #Calculate basis vectors from derivatives of position vector x - - bases = numpy.array(MV.n*[ZERO], dtype=numpy.object) - i = 0 - for coord in coords: - ei = x.diff(coords[i]) - ei.set_name(base_name_lst[i]) - bases[i] = ei - i += 1 - - #Calculate normalizee basis vectors and basis vector magnitudes - - if debug: - print('Coordinate Generating Vector') - print(x) - print('Basis Vectors') - for base in bases: - print(base) - - else: - - #Input basis vectors as N vector fields - - bases = x - - for (base, name) in zip(bases, base_name_lst): - base.set_name(name) - - if debug: - print('Basis Vectors') - for base in bases: - print(base) - if debug_level == 1: - return - - if debug_level == 1: - return - - #Calculate normalized basis vectors and magnitudes of - #unormalized basis vectors - - (nbases, mags) = normalize(bases, nbase_name_lst) - - if debug: - print('Magnitudes') - print('\\abs{' + LaTeX_base + '_{i}} = ', mags) - print('Normalized Basis Vectors') - for nbase in nbases: - print(nbase) - - g = numpy.array(MV.n*[MV.n*[ZERO]], dtype=numpy.object) - - for irow in MV.nrg: - for icol in MV.nrg: - magsq = sympy.expand((nbases[irow] | nbases[icol])()) - g[irow][icol] = sympy.simplify( - sympy.trigsimp(magsq, deep=True, recursive=True)) - - if debug: - print('Metric $\\hat{g}_{ij} = \\hat{' + LaTeX_base + \ - '}_{i}\\cdot \\hat{' + LaTeX_base + '}_{j}$') - print(r'\hat{g}_{ij} =', sympy.galgebra.latex_ex.LaTeX(g)) - - if debug_level == 2: - return - - #Calculate reciprocal normalized basis vectors - - rnbases = [] - - if is_quasi_unit_numpy_array(g): - ibasis = 0 - while ibasis < MV.n: - base = g[ibasis][ibasis]*nbases[ibasis] - base.set_name(rnbase_name_lst[ibasis]) - base.simplify() - base.trigsimp() - rnbases.append(base) - ibasis += 1 - else: - rnbases = reciprocal_frame(nbases, rnbase_name_lst) - ibase = 0 - for base in rnbases: - base.simplify() - base.trigsimp() - rnbases[ibase] = base - ibase += 1 - - if debug: - if debug_level != 0: - sympy.galgebra.latex_ex.MV_format(1) - print('Reciprocal Normalized Basis Vectors') - for rnbase in rnbases: - print(rnbase) - - if debug_level == 3: - return - - #Calculate components of inverse vectors - - Acoef = [] - - for ibasis in MV.nrg: - evec = numpy.array(MV.n*[ZERO], dtype=numpy.object) - for jbasis in MV.nrg: - evec[jbasis] = (MV.bvec[ibasis] | rnbases[jbasis])() - Acoef.append(evec) - - #Calculat metric tensors - - gr = numpy.array(MV.n*[MV.n*[ZERO]], dtype=numpy.object) - - for irow in MV.nrg: - for icol in MV.nrg: - magsq = sympy.expand((rnbases[irow] | rnbases[icol])()) - gr[irow][icol] = sympy.simplify( - sympy.trigsimp(magsq, deep=True, recursive=True)) - - if debug: - print('Metric $\\hat{g}^{ij} = \\hat{' + LaTeX_base + \ - '}^{i}\\cdot \\hat{' + LaTeX_base + '}^{j}$') - print(r'\hat{g}^{ij} =', sympy.galgebra.latex_ex.LaTeX(gr)) - - if debug_level == 4: - return - - #Calculate bases and reciprocal bases for curvilinear mulitvectors - - MV_bases = [[ONE]] - MV_rbases = [[ONE]] - igrade = 1 - while igrade <= MV.n: - base_index = MV.gabasis[igrade] - grade_bases = [] - rgrade_bases = [] - for index in base_index: - base = build_base(index, nbases) - base.simplify() - base.trigsimp() - rbase = build_base(index, rnbases, True) - rbase.simplify() - rbase.trigsimp() - grade_bases.append(base) - rgrade_bases.append(rbase) - igrade += 1 - MV_bases.append(grade_bases) - MV_rbases.append(rgrade_bases) - - #Calculate connection multivectors for geometric derivative - - MV_connect = [[ZERO]] - igrade = 1 - while igrade <= MV.n: - grade_connect = [] - ibase = 0 - for base in MV_bases[igrade]: - sum = MV() - itheta = 0 - for (theta, etheta) in zip(coords, rnbases): - psum = (1/mags[itheta])*etheta*base.diff(theta) - psum.trigsimp() - sum += psum - itheta += 1 - sum.simplify() - sum.trigsimp() - grade_connect.append(sum) - ibase += 1 - MV_connect.append(grade_connect) - igrade += 1 - - if debug: - print('Curvilinear Bases: $' + bstr + ' = ' + bmhat + \ - '_{i_{1}}\\W\\dots\\W' + bmhat + '_{i_{R}}$') - igrade = 1 - for grade in MV_bases[1:]: - ibase = 0 - for base in grade: - index = MV.gabasis[igrade][ibase] - sub_str = '' - for i in index: - sub_str += sympy.galgebra.latex_ex.LatexPrinter.extended_symbol(coords_lst[i]) - base_str = bmhat + '_{[' + sub_str + ']} = ' - print(base_str, base) - ibase += 1 - igrade += 1 - - if debug_level == 5: - return - - #Calculate representation of connection multivectors in curvilinear system - - MV_Connect = [[ZERO]] - igrade = 1 - while igrade <= MV.n: - grade_connect = [] - ibase = 0 - nbase = len(MV_bases[igrade]) - - if igrade < MV.n: - ibase = 0 - p1base = len(MV_bases[igrade + 1]) - m1base = len(MV_bases[igrade - 1]) - while ibase < nbase: - Cm1 = numpy.array(m1base*[ZERO], dtype=numpy.object) - Cp1 = numpy.array(p1base*[ZERO], dtype=numpy.object) - C = MV_connect[igrade][ibase] - if igrade == 1: - X = C(0) - else: - X = MV() - jbase = 0 - while jbase < m1base: - Cm1[jbase] = sympy.trigsimp((MV.inner_product(MV_rbases[igrade - 1][jbase], C))(), deep=True, recursive=True) - jbase += 1 - jbase = 0 - while jbase < p1base: - Cp1[jbase] = sympy.trigsimp((MV.inner_product(MV_rbases[igrade + 1][jbase], C))(), deep=True, recursive=True) - jbase += 1 - X += MV( - (igrade - 1, Cm1), 'grade') + MV((igrade + 1, Cp1), 'grade') - X.simplify() - X.trigsimp() - grade_connect.append(X) - ibase += 1 - else: - ibase = 0 - m1base = len(MV_bases[igrade - 1]) - while ibase < nbase: - Cm1 = numpy.array(m1base*[ZERO], dtype=numpy.object) - C = MV_connect[igrade][ibase] - jbase = 0 - while jbase < m1base: - Cm1[jbase] = sympy.trigsimp((MV.inner_product(MV_rbases[igrade - 1][jbase], C))(), deep=True, recursive=True) - jbase += 1 - X = MV() - X.mv[MV.n - 1] = Cm1 - X.simplify() - X.trigsimp() - grade_connect.append(X) - ibase += 1 - - MV_Connect.append(grade_connect) - igrade += 1 - - base_str = '' - for coord in coords: - base_str += base_name + '_' + \ - sympy.galgebra.latex_ex.LatexPrinter.str_basic(coord) + ' ' - base_str = base_str[:-1] - - old_names = MV.vbasis - - MV.setup(base_str, g, True, coords) - - MV.curvilinear_flg = True - MV.Connect = MV_Connect - sympy.galgebra.latex_ex.LatexPrinter.latex_bases() - MV.Rframe = numpy.array(MV.n*[ZERO], dtype=numpy.object) - ibasis = 0 - while ibasis < MV.n: - base = MV() - jbasis = 0 - while jbasis < MV.n: - base.add_in_place(gr[ibasis][jbasis]*MV.bvec[jbasis]) - jbasis += 1 - base.scalar_mul_inplace(1/mags[ibasis]) - MV.Rframe[ibasis] = base - ibasis += 1 - - MV.org_basis = [] - for ibasis in MV.nrg: - evec = MV(Acoef[ibasis], 'vector', old_names[ibasis]) - MV.org_basis.append(evec) - - if MV.coords[0] == sympy.Symbol('t'): - MV.dedt = [] - # I can't fix this no name error because I have no clue what the - # original writer was trying to do. - for coef in dedt_coef: - MV.dedt.append(MV(coef, 'vector')) - else: - MV.dedt = None - - if debug: - print('Representation of Original Basis Vectors') - for evec in MV.org_basis: - print(evec) - - print('Renormalized Reciprocal Vectors ' + '$\\bfrac{' + bmhat + \ - '^{k}}{\\abs{\\bm{' + LaTeX_base + '}_{k}}}$') - - ibasis = 0 - while ibasis < MV.n: - c_str = sympy.galgebra.latex_ex.LatexPrinter.extended_symbol( - coords_lst[ibasis]) - print('\\bfrac{\\bm{\\hat{' + LaTeX_base + '}}^{' + c_str + \ - '}}{\\abs{\\bm{' + LaTeX_base + '}_{' + c_str + '}}} =', \ - MV.Rframe[ibasis]) - ibasis += 1 - - title_str = 'Connection Multivectors: $C\\lbrc' + bstr + \ - '\\rbrc = ' + '\\bfrac{' + bmhat + '^{k}}{\\abs{' + bmhat + \ - '_{k}}}\\pdiff{' + bstr + '}{\\theta^{k}}$' - - print(title_str) - igrade = 1 - for grade in MV.Connect[1:]: - ibase = 0 - for base in grade: - index = MV.gabasis[igrade][ibase] - sub_str = '' - for i in index: - sub_str += sympy.galgebra.latex_ex.LatexPrinter.extended_symbol(coords_lst[i]) - - base_str = 'C\\lbrc\\hat{' + \ - LaTeX_base + '}_{[' + sub_str + ']}\\rbrc = ' - print(base_str, base) - ibase += 1 - igrade += 1 - return - - @staticmethod - def print_blades(): - """ - Set multivector output to blade representation. - """ - MV.bladeprint = 1 - return - - @staticmethod - def print_bases(): - """ - Set multivector output to base representation. - """ - MV.bladeprint = 0 - return - - @staticmethod - def multiplication_table(): - """ - Calculate geometric product base multiplication table. - See reference 5 section 3 for details. - """ - MV.mtable = [] - for igrade in MV.n1rg: - MV.mtable.append([]) - for ibase in range(MV.nbasis[igrade]): - MV.mtable[igrade].append([]) - if igrade == 0: - base1 = [] - else: - base1 = MV.basis[igrade][ibase] - for jgrade in MV.n1rg: - MV.mtable[igrade][ibase].append([]) - for jbase in range(MV.nbasis[jgrade]): - if jgrade == 0: - base2 = [] - else: - base2 = MV.basis[jgrade][jbase] - base = base1 + base2 - (coefs, bases) = MV.reduce_basis(base) - product = MV.convert(coefs, bases) - product.name = '(' + MV.basislabel[igrade][ibase] + \ - ')(' + MV.basislabel[jgrade][jbase] + ')' - MV.mtable[igrade][ibase][jgrade].append(product) - if MV.debug: - print('Multiplication Table:') - for level1 in MV.mtable: - for level2 in level1: - for level3 in level2: - for mv in level3: - mv.printmv() - return - - @staticmethod - def geometric_product(mv1, mv2): - """ - MV.geometric_product(mv1,mv2) calculates the geometric - product the multivectors mv1 and mv2 (mv1*mv2). See - reference 5 section 3. - """ - product = MV() - if isinstance(mv1, MV) and isinstance(mv2, MV): - bladeflg1 = mv1.bladeflg - bladeflg2 = mv2.bladeflg - if bladeflg1: - mv1.convert_from_blades() - if bladeflg2: - mv2.convert_from_blades() - for igrade in MV.n1rg: - gradei = mv1.mv[igrade] - if isinstance(gradei, numpy.ndarray): - for ibase in range(MV.nbasis[igrade]): - xi = gradei[ibase] - if xi != ZERO: - for jgrade in MV.n1rg: - gradej = mv2.mv[jgrade] - if isinstance(gradej, numpy.ndarray): - for jbase in range(MV.nbasis[jgrade]): - xj = gradej[jbase] - if xj != ZERO: - xixj = MV.mtable[igrade][ibase][jgrade][jbase].scalar_mul(xi*xj) - product.add_in_place(xixj) - product.bladeflg = 0 - if bladeflg1: - mv1.convert_to_blades() - if bladeflg2: - mv1.convert_to_blades() - if bladeflg1 and bladeflg2: - product.convert_to_blades() - else: - if isinstance(mv1, MV): - product = mv1.scalar_mul(mv2) - else: - product = mv2.scalar_mul(mv1) - return(product) - - @staticmethod - def wedge(igrade1, blade1, vector2, name=''): - """ - Calculate the outer product of a multivector blade - and a vector. See reference 5 section 5 for details. - """ - w12 = blade1*vector2 - w21 = vector2*blade1 - if igrade1 % 2 != 0: - w = w12 - w21 - else: - w = w12 + w21 - w.name = name - return(w*HALF) - - @staticmethod - def blade_table(): - """ - Calculate basis blades in terms of bases. See reference 5 - section 5 for details. Used to convert from blade to base - representation. - """ - MV.btable = [] - MV.bladelabel = [] - basis_str = MV.basislabel[1] - for igrade in MV.n1rg: - MV.bladelabel.append([]) - if igrade == 0: - MV.bladelabel[0].append('1') - tmp = [MV(value=ONE, mvtype='scalar', mvname='1')] - if igrade == 1: - tmp = [] - for ibase in range(MV.nbasis[1]): - MV.bladelabel[1].append(basis_str[ibase]) - tmp.append(MV(value=ibase, - mvtype='basisvector', mvname=basis_str[ibase])) - if igrade >= 2: - tmp = [] - basis = MV.basis[igrade] - for blade in basis: - name = '' - for i in blade: - name += basis_str[i] + '^' - name = name[:-1] - MV.bladelabel[igrade].append(name) - lblade = MV.basis[igrade - 1].index(blade[:-1]) - rblade = blade[-1] - igrade1 = igrade - 1 - blade1 = MV.btable[igrade1][lblade] - vector2 = MV.btable[1][rblade] - b1Wv2 = MV.wedge(igrade1, blade1, vector2, name) - tmp.append(b1Wv2) - MV.btable.append(tmp) - if MV.debug: - print('Blade Tabel:') - for grade in MV.btable: - for mv in grade: - print(mv) - print('Blade Labels:') - print(MV.bladelabel) - return - - @staticmethod - def inverse_blade_table(): - """ - Calculate bases in terms of basis blades. See reference 5 - section 5 for details. Used to convert from base to blade - representation. - """ - MV.ibtable = [] - for igrade in MV.n1rg: - if igrade == 0: - tmp = [MV(value=ONE, mvtype='scalar', mvname='1')] - if igrade == 1: - tmp = [] - for ibase in range(MV.nbasis[1]): - tmp.append(MV(value=ibase, mvtype='basisvector')) - if igrade >= 2: - tmp = [] - iblade = 0 - for blade in MV.btable[igrade]: - invblade = MV() - for i in range(igrade - 1): - invblade.mv[i] = -blade.mv[i] - invblade.mv[igrade] = +blade.mv[igrade] - invblade.bladeflg = 1 - if igrade >= 4: - jgrade = igrade - 2 - while jgrade > 1: - for ibase in range(MV.nbasis[jgrade]): - invblade.substitute_base( - jgrade, ibase, MV.ibtable[jgrade][ibase]) - jgrade -= 2 - invblade.name = MV.basislabel[igrade][iblade] - iblade += 1 - tmp.append(invblade) - MV.ibtable.append(tmp) - if MV.debug: - print('Inverse Blade Tabel:') - for grade in MV.ibtable: - for mv in grade: - mv.printmv() - return - - @staticmethod - def outer_product(mv1, mv2): - """ - MV.outer_product(mv1,mv2) calculates the outer (exterior,wedge) - product of the multivectors mv1 and mv2 (mv1^mv2). See - reference 5 section 6. - """ - if type(mv1) == type(MV) and type(mv2) == type(MV): - if mv1.is_scalar() and mv2.is_scalar(): - return(mv1*mv2) - - if isinstance(mv1, MV) and isinstance(mv2, MV): - product = MV() - product.bladeflg = 1 - mv1.convert_to_blades() - mv2.convert_to_blades() - for igrade1 in MV.n1rg: - if isinstance(mv1.mv[igrade1], numpy.ndarray): - pg1 = mv1.project(igrade1) - for igrade2 in MV.n1rg: - igrade = igrade1 + igrade2 - if igrade <= MV.n: - if isinstance(mv2.mv[igrade2], numpy.ndarray): - pg2 = mv2.project(igrade2) - pg1pg2 = pg1*pg2 - product.add_in_place(pg1pg2.project(igrade)) - else: - if isinstance(mv1, MV): - product = mv1.scalar_mul(mv2) - if isinstance(mv2, MV): - product = mv2.scalar_mul(mv1) - return(product) - - @staticmethod - def inner_product(mv1, mv2, mode='s'): - """ - MV.inner_product(mv1,mv2) calculates the inner - - mode = 's' - symmetric (Doran & Lasenby) - mode = 'l' - left contraction (Dorst) - mode = 'r' - right contraction (Dorst) - """ - if type(mv1) == type(MV) and type(mv2) == type(MV): - if mv1.is_scalar() and mv2.is_scalar(): - return(mv1*mv2) - - if isinstance(mv1, MV) and isinstance(mv2, MV): - product = MV() - product.bladeflg = 1 - mv1.convert_to_blades() - mv2.convert_to_blades() - for igrade1 in range(MV.n1): - if isinstance(mv1.mv[igrade1], numpy.ndarray): - pg1 = mv1.project(igrade1) - for igrade2 in range(MV.n1): - igrade = igrade1 - igrade2 - if mode == 's': - igrade = igrade.__abs__() - else: - if mode == 'l': - igrade = -igrade - if igrade >= 0: - if isinstance(mv2.mv[igrade2], numpy.ndarray): - pg2 = mv2.project(igrade2) - pg1pg2 = pg1*pg2 - product.add_in_place(pg1pg2.project(igrade)) - return(product) - else: - if mode == 's': - if isinstance(mv1, MV): - product = mv1.scalar_mul(mv2) - if isinstance(mv2, MV): - product = mv2.scalar_mul(mv1) - else: - product = None - return(product) - - @staticmethod - def addition(mv1, mv2): - """ - MV.addition(mv1,mv2) calculates the sum - of the multivectors mv1 and mv2 (mv1+mv2). - """ - sum = MV() - if isinstance(mv1, MV) and isinstance(mv2, MV): - if mv1.bladeflg or mv2.bladeflg: - mv1.convert_to_blades() - mv2.convert_to_blades() - sum.bladeflg = 1 - for i in MV.n1rg: - if isinstance(mv1.mv[i], numpy.ndarray) and isinstance(mv2.mv[i], numpy.ndarray): - sum.mv[i] = mv1.mv[i] + mv2.mv[i] - else: - if isinstance(mv1.mv[i], numpy.ndarray) and not isinstance(mv2.mv[i], numpy.ndarray): - sum.mv[i] = +mv1.mv[i] - else: - if isinstance(mv2.mv[i], numpy.ndarray) and not isinstance(mv1.mv[i], numpy.ndarray): - sum.mv[i] = +mv2.mv[i] - return(sum) - else: - if isinstance(mv1, MV): - return(mv1 + MV(mv2, 'scalar')) - else: - return(MV(mv1, 'scalar') + mv2) - - @staticmethod - def subtraction(mv1, mv2): - """ - MV.subtraction(mv1,mv2) calculates the difference - of the multivectors mv1 and mv2 (mv1-mv2). - """ - diff = MV() - if isinstance(mv1, MV) and isinstance(mv2, MV): - if mv1.bladeflg or mv2.bladeflg: - mv1.convert_to_blades() - mv2.convert_to_blades() - diff.bladeflg = 1 - for i in MV.n1rg: - if isinstance(mv1.mv[i], numpy.ndarray) and isinstance(mv2.mv[i], numpy.ndarray): - diff.mv[i] = mv1.mv[i] - mv2.mv[i] - else: - if isinstance(mv1.mv[i], numpy.ndarray) and not isinstance(mv2.mv[i], numpy.ndarray): - diff.mv[i] = +mv1.mv[i] - else: - if not isinstance(mv1.mv[i], numpy.ndarray) and isinstance(mv2.mv[i], numpy.ndarray): - diff.mv[i] = -mv2.mv[i] - return(diff) - else: - if isinstance(mv1, MV): - return(mv1 - MV(mv2, 'scalar')) - else: - return(MV(mv1, 'scalar') - mv2) - - @staticmethod - def vdiff(vec, x): - dvec = numpy.array(len(vec)*[ZERO]) - ivec = 0 - for veci in vec: - dvec[ivec] = sympy.diff(veci, x) - ivec += 1 - return(dvec) - - @staticmethod - def scalar_to_symbol(scalar): - if isinstance(scalar, MV): - return(scalar.mv[0][0]) - if type(scalar) == list: - sym = [] - for x in scalar: - sym.append(MV.scalar_to_symbol(x)) - return(sym) - return(scalar) - - def __init__(self, value='', mvtype='', mvname='', fct=False, vars=None): - """ - Initialization of multivector X. Inputs are as follows - - mvtype value result - - default default Zero multivector - 'basisvector' int i ith basis vector - 'basisbivector' int i ith basis bivector - 'scalar' symbol x scalar of value x - string s - 'grade' [int i, symbol array A] X.grade(i) = A - [int i, string s] - 'vector' symbol array A X.grade(1) = A - string s - 'grade2' symbol array A X.grade(2) = A - string s - 'pseudo' symbol x X.grade(n) = x - string s - 'spinor' string s spinor with coefficients - s__indices and name sbm - - mvname is name of multivector. - If fct is 'True' and MV.coords is defined in MV.setup then a - multivector field of MV.coords is instantiated. - """ - - self.name = mvname - self.mv = MV.n1*[0] - self.bladeflg = 0 # 1 for blade expansion - self.puregrade = 1 - if mvtype == 'basisvector': - self.mv[1] = numpy.array(MV.nbasis[1]*[ZERO], dtype=numpy.object) - self.mv[1][value] = ONE - if mvtype == 'basisbivector': - self.mv[2] = numpy.array(MV.nbasis[2]*[ZERO], dtype=numpy.object) - self.mv[2][value] = ONE - if mvtype == 'scalar': - if isinstance(value, str): - value = sympy.Symbol(value) - if isinstance(value, int): - value = sympy.Rational(value) - self.mv[0] = numpy.array([value], dtype=numpy.object) - if mvtype == 'pseudo': - if isinstance(value, str): - value = sympy.Symbol(value) - self.mv[MV.n] = numpy.array([value], dtype=numpy.object) - if mvtype == 'vector': - if isinstance(value, str): # Most general vector - symbol_str = '' - for ibase in MV.nrg: - if MV.coords is None: - symbol = value + '__' + str(ibase + MV.index_offset) - symbol_str += symbol + ' ' - else: - symbol = value + '__' + (MV.coords[ibase]).name - symbol_str += symbol + ' ' - symbol_lst = sympy.symbols(symbol_str) - self.mv[1] = numpy.array(symbol_lst, dtype=numpy.object) - self.name = value - else: - value = MV.pad_zeros(value, MV.nbasis[1]) - self.mv[1] = numpy.array(value, dtype=numpy.object) - if mvtype == 'grade2': - if isinstance(value, str): # Most general grade-2 multivector - if value != '': - symbol_str = '' - for base in MV.basis[2]: - symbol = value + MV.construct_index(base) - symbol_str += symbol + ' ' - symbol_lst = sympy.symbols(symbol_str) - self.mv[2] = numpy.array(symbol_lst, dtype=numpy.object) - else: - value = MV.pad_zeros(value, MV.nbasis[2]) - self.mv[2] = numpy.array(value, dtype=numpy.object) - if mvtype == 'grade': - igrade = value[0] - coefs = value[1] - if isinstance(coefs, str): # Most general pure grade multivector - base_symbol = coefs - coefs = [] - bases = MV.basis[igrade] - if igrade == 0: - self.mv[0] = numpy.array( - [sympy.Symbol(base_symbol)], dtype=numpy.object) - else: - for base in bases: - coef = base_symbol + MV.construct_index(base) - coef = sympy.Symbol(coef) - coefs.append(coef) - self.mv[igrade] = numpy.array(coefs, dtype=numpy.object) - else: - self.mv[igrade] = coefs - if mvtype == 'base': - self.mv[value[0]] = numpy.array( - MV.nbasis[value[0]]*[ZERO], dtype=numpy.object) - self.mv[value[0]][value[1]] = ONE - if mvtype == 'spinor': - if isinstance(value, str): # Most general spinor - for grade in MV.n1rg: - if grade % 2 == 0: - symbol_str = '' - if grade != 0: - symbol_lst = [] - for base in MV.basis[grade]: - symbol = sympy.Symbol( - value + MV.construct_index(base)) - symbol_lst.append(symbol) - self.mv[grade] = numpy.array( - symbol_lst, dtype=numpy.object) - else: - self.mv[0] = numpy.array( - [sympy.Symbol(value)], dtype=numpy.object) - self.name = value + 'bm' - if isinstance(value, str) and mvtype == '': # Most general multivector - if value != '': - for grade in MV.n1rg: - symbol_str = '' - if grade != 0: - symbol_lst = [] - for base in MV.basis[grade]: - symbol = sympy.Symbol( - value + MV.construct_index(base)) - symbol_lst.append(symbol) - self.mv[grade] = numpy.array( - symbol_lst, dtype=numpy.object) - else: - self.mv[0] = numpy.array( - [sympy.Symbol(value)], dtype=numpy.object) - self.name = value + 'bm' - if fct: - if vars is not None: - vars = tuple(vars) - for grade in MV.n1rg: - if not isinstance(self.mv[grade], int): - if grade == 0: - coef = sympy.galgebra.latex_ex.LatexPrinter.str_basic( - self.mv[0][0]) - if vars is None and MV.coords is not None: - self.mv[0] = numpy.array([sympy.Function( - coef)(*MV.coords)], dtype=numpy.object) - else: - self.mv[0] = numpy.array([sympy.Function( - coef)(*vars)], dtype=numpy.object) - else: - for base in range(MV.nbasis[grade]): - coef = sympy.galgebra.latex_ex.LatexPrinter.str_basic(self.mv[grade][base]) - if vars is None and MV.coords is not None: - self.mv[grade][ - base] = sympy.Function(coef)(*MV.coords) - else: - self.mv[ - grade][base] = sympy.Function(coef)(*vars) - - @staticmethod - def construct_index(base): - index_str = '' - if len(base) == 0: - return('') - if MV.coords is None: - for ix in base: - index_str += str(ix + MV.index_offset) - else: - for ix in base: - index_str += (MV.coords[ix]).name - return('__' + index_str) - - def set_name(self, namestr): - self.name = namestr - return - - def max_grade(self): - """ - X.max_grade() is maximum grade of non-zero grades of X. - """ - for i in range(MV.n, -1, -1): - if isinstance(self.mv[i], numpy.ndarray): - return(i) - return(-1) - - @staticmethod - def coord(xname, offset=0): - xi_str = '' - for i in MV.nrg: - xi_str += xname + str(i + offset) + ' ' - xi = sympy.symbols(xi_str) - x = MV(xi, 'vector') - return(x) - - def x(self, i): - if isint(self.mv[1]): - return(ZERO) - return(self.mv[1][i]) - - def set_coef(self, grade, base, value): - if isinstance(self.mv[grade], int): - self.mv[grade] = numpy.array( - MV.nbasis[grade]*[ZERO], dtype=numpy.object) - self.mv[grade][base] = value - return - - @staticmethod - def named(mvname, value='', mvtype=''): - name = mvname - tmp = MV(value=value, mvtype=mvtype, mvname=name) - setattr(sys.modules[__name__], name, tmp) - return - - @staticmethod - def printnm(tpl): - for a in tpl: - print(a.name, ' =', a.mv) - return - - def __str__(self): - return(MV.str_rep(self)) - - def printmv(self, name=''): - title = '' - if name: - title += name + ' = ' - else: - if self.name: - title += self.name + ' = ' - print(title + MV.str_rep(self)) - return - - def set_value(self, igrade, ibase, value): - if isinstance(self.mv[igrade], numpy.ndarray): - self.mv[igrade][ibase] = value - else: - self.mv[igrade] = numpy.array( - MV.nbasis[igrade]*[ZERO], dtype=numpy.object) - self.mv[igrade][ibase] = value - return - - def add_in_place(self, mv): - """ - X.add_in_place(mv) increments multivector X by multivector - mv. - """ - if self.bladeflg or mv.bladeflg: - self.convert_to_blades() - mv.convert_to_blades() - for i in MV.n1rg: - if isinstance(self.mv[i], numpy.ndarray) and isinstance(mv.mv[i], numpy.ndarray): - self.mv[i] += mv.mv[i] - else: - if not isinstance(self.mv[i], numpy.ndarray) and isinstance(mv.mv[i], numpy.ndarray): - self.mv[i] = +mv.mv[i] - return - - def sub_in_place(self, mv): - """ - X.sub_in_place(mv) decrements multivector X by multivector - mv. - """ - if self.bladeflg or mv.bladeflg: - self.convert_to_blades() - mv.convert_to_blades() - for i in MV.n1rg: - if isinstance(self.mv[i], numpy.ndarray) and isinstance(mv.mv[i], numpy.ndarray): - self.mv[i] -= mv.mv[i] - else: - if not isinstance(self.mv[i], numpy.ndarray) and isinstance(mv.mv[i], numpy.ndarray): - self.mv[i] = +mv.mv[i] - return - - def __pos__(self): - p = MV() - p.bladeflg = self.bladeflg - for i in MV.n1rg: - if isinstance(self.mv[i], numpy.ndarray): - p.mv[i] = +self.mv[i] - return(p) - - def __neg__(self): - n = MV() - n.bladeflg = self.bladeflg - for i in MV.n1rg: - if isinstance(self.mv[i], numpy.ndarray): - n.mv[i] = -self.mv[i] - return(n) - - def __add_ab__(self, mv): - self.add_in_place(mv) - return - - def __sub_ab__(self, mv): - self.sub_in_place(mv) - return - - def __add__(self, mv): - """See MV.addition(self,mv)""" - return(MV.addition(self, mv)) - - def __radd__(self, mv): - """See MV.addition(mv,self)""" - return(MV.addition(mv, self)) - - def __sub__(self, mv): - """See MV.subtraction(self,mv)""" - return(MV.subtraction(self, mv)) - - def __rsub__(self, mv): - """See MV.subtraction(mv,self)""" - return(MV.subtraction(mv, self)) - - def __xor__(self, mv): - """See MV.outer_product(self,mv)""" - return(MV.outer_product(self, mv)) - - def __pow__(self, mv): - """See MV.outer_product(self,mv)""" - return(MV.outer_product(self, mv)) - - def __rxor__(self, mv): - """See MV.outer_product(mv,self)""" - return(MV.outer_product(mv, self)) - - def __or__(self, mv): - """See MV.inner_product(self,mv)""" - return(MV.inner_product(self, mv)) - - def __ror__(self, mv): - """See MV.inner_product(mv,self)""" - return(MV.inner_product(mv, self)) - - def __lt__(self, mv): - """See MV.inner_product(self,mv)""" - return(MV.inner_product(self, mv, 'l')) - - def __lshift__(self, mv): - """See MV.inner_product(self,mv)""" - return(MV.inner_product(self, mv, 'l')) - - def __rlshift__(self, mv): - """See MV.inner_product(self,mv)""" - return(MV.inner_product(mv, self, 'l')) - - def lc(self, mv): - return(MV.inner_product(self, mv, 'l')) - - def __gt__(self, mv): - """See MV.inner_product(self,mv)""" - return(MV.inner_product(self, mv, 'r')) - - def __rshift__(self, mv): - """See MV.inner_product(self,mv)""" - return(MV.inner_product(self, mv, 'r')) - - def __rrshift__(self, mv): - """See MV.inner_product(self,mv)""" - return(MV.inner_product(mv, self, 'r')) - - def rc(self, mv): - return(MV.inner_product(self, mv, 'r')) - - def scalar_mul(self, c): - """ - Y = X.scalar_mul(c), multiply multivector X by scalar c and return - result. - """ - mv = MV() - mv.bladeflg = self.bladeflg - mv.puregrade = self.puregrade - for i in MV.n1rg: - if isinstance(self.mv[i], numpy.ndarray): - #print self.mv[i] - #print c,type(c) - mv.mv[i] = self.mv[i]*c - return(mv) - - def scalar_mul_inplace(self, c): - """ - X.scalar_mul_inplace(c), multiply multivector X by scalar c and save - result in X. - """ - for i in MV.n1rg: - if isinstance(self.mv[i], numpy.ndarray): - self.mv[i] = self.mv[i]*c - return - - def __mul__(self, mv): - """See MV.geometric_product(self,mv)""" - return(MV.geometric_product(self, mv)) - - def __rmul__(self, mv): - """See MV.geometric_product(mv,self)""" - return(MV.geometric_product(mv, self)) - - def __div__(self, scalar): - div = MV() - div.bladeflg = self.bladeflg - for i in MV.n1rg: - if isinstance(self.mv[i], numpy.ndarray): - div.mv[i] = self.mv[i]/scalar - return(div) - - def __div_ab__(self, scalar): - for i in MV.n1rg: - if isinstance(self.mv[i], numpy.ndarray): - self.mv[i] /= scalar - return - - def __call__(self, igrade=0, ibase=0): - """ - X(i,j) returns symbol in ith grade and jth base or blade of - multivector X. - """ - if not isinstance(self.mv[igrade], numpy.ndarray): - return(ZERO) - return(self.mv[igrade][ibase]) - - @staticmethod - def equal(mv1, mv2): - mv1.compact() - if isinstance(mv2, MV): - mv2.compact() - pure_grade = mv1.is_pure() - if not isinstance(mv2, MV) and pure_grade != 0: - return(False) - if not isinstance(mv2, MV) and pure_grade == 0: - if isinstance(mv1.mv[0], int): - return(mv2 == 0) - else: - return(mv1.mv[0][0] == mv2) - for (mvi, mvj) in zip(mv1.mv, mv2.mv): - if isint(mvi) ^ isint(mvj): - return(False) - if isinstance(mvi, numpy.ndarray) and isinstance(mvj, numpy.ndarray): - for (x, y) in zip(mvi, mvj): - if x != y: - return(False) - return(True) - - def __eq__(self, mv): - return(MV.equal(self, mv)) - - def copy(self, sub=0): - """ - Y = X.copy(), make a deep copy of multivector X in multivector - Y so that Y can be modified without affecting X. - """ - cpy = MV() - cpy.name = self.name - cpy.bladeflg = self.bladeflg - cpy.puregrade = self.puregrade - for i in MV.n1rg: - if sub: - if isinstance(self.mv[i], numpy.ndarray): - cpy.mv[i] = -self.mv[i] - else: - if isinstance(self.mv[i], numpy.ndarray): - cpy.mv[i] = +self.mv[i] - return(cpy) - - def substitute_base(self, igrade, base, mv): - if not isinstance(self.mv[igrade], numpy.ndarray): - return - if isinstance(base, numpy.ndarray): - ibase = MV.basis[igrade].index(base) - else: - ibase = base - coef = self.mv[igrade][ibase] - if coef == ZERO: - return - self.mv[igrade][ibase] = ZERO - self.add_in_place(mv*coef) - return - - def convert_to_blades(self): - """ - X.convert_to_blades(), inplace convert base representation - to blade representation. See reference 5 section 5. - """ - if self.bladeflg: - return - self.bladeflg = 1 - for igrade in range(2, MV.n1): - if isinstance(self.mv[igrade], numpy.ndarray): - for ibase in range(MV.nbasis[igrade]): - coef = self.mv[igrade][ibase] - if (not coef == ZERO): - self.mv[igrade][ibase] = ZERO - self.add_in_place(MV.ibtable[igrade][ibase]*coef) - return - - def convert_from_blades(self): - """ - X.convert_from_blades(), inplace convert blade representation - to base representation. See reference 5 section 5. - """ - if not self.bladeflg: - return - self.bladeflg = 0 - for igrade in range(2, MV.n1): - if isinstance(self.mv[igrade], numpy.ndarray): - for ibase in range(MV.nbasis[igrade]): - coef = self.mv[igrade][ibase] - if (not coef == ZERO): - self.mv[igrade][ibase] = ZERO - self.add_in_place(MV.btable[igrade][ibase]*coef) - return - - def project(self, r): - """ - Grade projection operator. For multivector X, X.project(r) - returns multivector of grade r components of X if r is an - integer. If r is a multivector X.project(r) returns a - multivector consisting of the grade of X for which r has non- - zero grades. For example if X is a general multivector and - r is a general spinor then X.project(r) will return the even - grades of X. - """ - if isinstance(r, int): - grade_r = MV() - if r > MV.n: - return(grade_r) - self.convert_to_blades() - if not isinstance(self.mv[r], numpy.ndarray): - return(grade_r) - grade_r.bladeflg = 1 - grade_r.puregrade = 1 - grade_r.mv[r] = +self.mv[r] - return(grade_r) - if isinstance(r, MV): - self.convert_to_blades() - r.convert_to_blades() - proj = MV() - for i in MV.n1rg: - if not isinstance(r.mv[i], int): - proj.mv[i] = self.mv[i] - proj.bladeflg = self.bladeflg - return(proj) - return(None) - - def even(self): - """ - Even grade projection operator. For multivector X, X.even() - returns multivector of even grade components of X. - """ - egrades = MV() - self.convert_to_blades() - egrades.bladeflg = self.bladeflg - egrades.puregrade = self.puregrade - for igrade in range(0, MV.n1, 2): - egrades.mv[igrade] = +self.mv[igrade] - return(egrades) - - def odd(self): - """ - Odd grade projection operator. For multivector X, X.odd() - returns multivector of odd grade components of X. - """ - ogrades = MV() - self.convert_to_blades() - ogrades.bladeflg = self.bladeflg - ogrades.puregrade = self.puregrade - for igrade in range(1, MV.n1, 2): - ogrades.mv[igrade] = +self.mv[igrade] - return(ogrades) - - def rev(self): - """ - Revision operator. For multivector X, X.rev() - returns reversed multivector of X. - """ - revmv = MV() - self.convert_to_blades() - revmv.bladeflg = self.bladeflg - revmv.puregrade = self.puregrade - for igrade in MV.n1rg: - if isinstance(self.mv[igrade], numpy.ndarray): - if igrade < 2 or (not (((igrade*(igrade - 1))/2) % 2)): - revmv.mv[igrade] = +self.mv[igrade] - else: - revmv.mv[igrade] = -self.mv[igrade] - return(revmv) - - def cse(self, grade): - cse_lst = [] - if isinstance(self.mv[grade], numpy.ndarray): - for ibase in range(MV.nbasis[grade]): - if self.mv[grade][ibase] != ZERO: - cse_lst.append(sympy.cse(self.mv[grade][ibase])) - return(cse_lst) - - def div(self, grade, divisor): - div_lst = [] - if isinstance(self.mv[grade], numpy.ndarray): - for ibase in range(MV.nbasis[grade]): - if self.mv[grade][ibase] != ZERO: - div_lst.append( - self.mv[grade][ibase].as_coefficient(divisor)) - return(div_lst) - - # don't know which one is correctly named - - def div(self): - return self.grad_int() - - def collect(self, lst): - """ - Applies sympy collect function to each component - of multivector. - """ - for igrade in MV.n1rg: - if isinstance(self.mv[igrade], numpy.ndarray): - for ibase in range(MV.nbasis[igrade]): - if self.mv[igrade][ibase] != ZERO: - self.mv[igrade][ibase] = \ - sympy.collect(self.mv[igrade][ibase], lst) - return - - def sqrfree(self, lst): - """ - Applies sympy sqrfree function to each component - of multivector. - """ - for igrade in MV.n1rg: - if isinstance(self.mv[igrade], numpy.ndarray): - for ibase in range(MV.nbasis[igrade]): - if self.mv[igrade][ibase] != ZERO: - self.mv[igrade][ibase] = \ - sympy.sqrfree(self.mv[igrade][ibase], lst) - return - - def flatten(self): - flst = [] - for igrade in MV.n1rg: - if isinstance(self.mv[igrade], int): - flst += MV.nbasis[igrade]*[ZERO] - else: - for coef in self.mv[igrade]: - flst.append(coef) - return(flst) - - def subs(self, *args): - X = MV() - X.bladeflg = self.bladeflg - X.puregrade = self.puregrade - for igrade in MV.n1rg: - if isinstance(self.mv[igrade], numpy.ndarray): - X.mv[igrade] = numpy.array( - substitute_array(self.mv[igrade], *args)) - return(X) - - def sub_mv(self, mv1, mv2): - mv1_flat = mv1.flatten() - mv2_flat = mv2.flatten() - self.sub_scalar(mv1_flat, mv2_flat) - return - - def sub_scalar(self, expr1, expr2): - if (isinstance(expr1, list) and isinstance(expr2, list)) or \ - (isinstance(expr1, tuple) and isinstance(expr2, tuple)): - for (var1, var2) in zip(expr1, expr2): - self.sub_scalar(var1, var2) - else: - for igrade in MV.n1rg: - if not isinstance(self.mv[igrade], int): - for ibase in range(MV.nbasis[igrade]): - if expr1 != ZERO: - self.mv[igrade][ibase] = self.mv[ - igrade][ibase].subs(expr1, expr2) - return - - def simplify(self): - """ - Applies sympy simplify function - to each component of multivector. - """ - for igrade in MV.n1rg: - if isinstance(self.mv[igrade], numpy.ndarray): - for ibase in range(MV.nbasis[igrade]): - if self.mv[igrade][ibase] != ZERO: - self.mv[igrade][ibase] = \ - sympy.simplify(self.mv[igrade][ibase]) - return - - def trigsimp(self): - """ - Applies sympy trigsimp function - to each component of multivector - using the recursive option. - """ - for igrade in MV.n1rg: - if isinstance(self.mv[igrade], numpy.ndarray): - for ibase in range(MV.nbasis[igrade]): - if self.mv[igrade][ibase] != ZERO: - self.mv[igrade][ibase] = \ - sympy.trigsimp(self.mv[igrade] - [ibase], deep=True, recursive=True) - return - - def cancel(self): - """ - Applies sympy cancel function - to each component of multivector. - """ - for igrade in MV.n1rg: - if isinstance(self.mv[igrade], numpy.ndarray): - for ibase in range(MV.nbasis[igrade]): - if self.mv[igrade][ibase] != ZERO: - self.mv[igrade][ibase] = \ - sympy.cancel(self.mv[igrade][ibase]) - return - - def expand(self): - """ - Applies sympy expand function to each component - of multivector. - """ - for igrade in MV.n1rg: - if isinstance(self.mv[igrade], numpy.ndarray): - for ibase in range(MV.nbasis[igrade]): - if self.mv[igrade][ibase] != ZERO: - self.mv[igrade][ibase] = \ - self.mv[igrade][ibase].expand() - return - - def is_pure(self): - igrade = -1 - ngrade = 0 - self.compact() - for i in MV.n1rg: - if isinstance(self.mv[i], numpy.ndarray): - for base in self.mv[i]: - if base != 0: - igrade = i - ngrade += 1 - break - if ngrade > 1: - return(-1) - if igrade == -1: - return(0) - return(igrade) - - def is_scalar(self): - if self.is_pure() == 0: - return(True) - return(False) - - def compact(self): - """ - Convert zero numpy arrays to single integer zero place holder - in grade list for instantiated multivector. For example if - numpy array of grade one components is a zero array then replace - with single integer equal to zero. - """ - for i in MV.n1rg: - if not isint(self.mv[i]): - zero_flg = True - for x in self.mv[i]: - if x != 0: - zero_flg = False - break - if zero_flg: - self.mv[i] = 0 - return - - def diff(self, x): - """ - Calculate partial derivative of multivector with respect to - argument x. - """ - D = MV() - igrade = 0 - for grade in self.mv: - if not isinstance(grade, int): - D.mv[igrade] = MV.vdiff(grade, x) - igrade += 1 - return(D) - - def ddt(self): - if MV.coords[0] != sympy.Symbol('t'): - return(MV()) - dxdt = self.diff(MV.coords[0]) - for ibase in MV.nrg: - dxdt += self.mv[1][ibase]*MV.dedt[ibase] - #dxdt.simplify() - #dxdt.trigsimp() - return(dxdt) - - def grad(self): - """ - Calculate geometric (grad) derivative of multivector function - """ - D = [] - dD = MV() - for theta in MV.coords: - D.append(self.diff(theta)) - if MV.curvilinear_flg: - recp = MV.Rframe - else: - recp = MV.brecp - for (rbase, iD) in zip(recp, D): - dD.add_in_place(rbase*iD) - if MV.curvilinear_flg: # Add Connection - igrade = 1 - while igrade <= MV.n: - coefs = self.mv[igrade] - if type(coefs) != int: - for (coef, connect) in zip(coefs, MV.Connect[igrade]): - dD.add_in_place(coef*connect) - igrade += 1 - return(dD) - - def grad_ext(self): - """ - Calculate outer (exterior,curl) derivative of multivector function. - """ - D = [] - dD = MV() - for ix in MV.coords: - D.append(self.diff(ix)) - if MV.curvilinear_flg: - recp = MV.Rframe - else: - recp = MV.brecp - for (irbase, iD) in zip(recp, D): - dD.add_in_place(irbase ^ iD) - if MV.curvilinear_flg: # Add Connection - igrade = 1 - while igrade <= MV.n: - coefs = self.mv[igrade] - if type(coefs) != int: - for (coef, connect) in zip(coefs, MV.Connect[igrade]): - if igrade < MV.n: - dD.add_in_place(coef*connect.project(igrade + 1)) - igrade += 1 - return(dD) - - def curl(self): - return(self.grad_ext()) - - def grad_int(self): - """ - Calculate inner (interior,div) derivative of multivector function. - """ - D = [] - dD = MV() - for ix in MV.coords: - D.append(self.diff(ix)) - if MV.curvilinear_flg: - recp = MV.Rframe - else: - recp = MV.brecp - for (irbase, iD) in zip(recp, D): - dD.add_in_place(irbase | iD) - if MV.curvilinear_flg: # Add Connection - igrade = 1 - while igrade <= MV.n: - coefs = self.mv[igrade] - if type(coefs) != int: - for (coef, connect) in zip(coefs, MV.Connect[igrade]): - dD.add_in_place(coef*connect.project(igrade - 1)) - igrade += 1 - return(dD) - - def mag2(self): - """ - Calculate scalar component of square of multivector. - """ - return((self | self)()) - - def Name(self): - """ - Get LaTeX name of multivector. - """ - return(sympy.galgebra.latex_ex.LatexPrinter.extended_symbol(self.name)) - - -
    [docs]def set_names(var_lst, var_str): - """ - Set the names of a list of multivectors (var_lst) for a space delimited - string (var_str) containing the names. - """ - var_str_lst = var_str.split() - if len(var_lst) == len(var_str_lst): - for (var, var_name) in zip(var_lst, var_str_lst): - var.set_name(var_name) - return - sys.stderr.write('Error in set_names. Lists incongruent!\n') - sys.exit() - return - -
    -
    [docs]def reciprocal_frame(vlst, names=''): - """ - Calculate reciprocal frame of list (vlst) of vectors. If desired name each - vector in list of reciprocal vectors with names in space delimited string - (names). - """ - E = vlst[0] - recp = [] - if type(names) != str: - name_lst = names - else: - if names != '': - name_lst = names.split() - for i in range(1, MV.n): - E = E ^ vlst[i] - for i in range(MV.n): - tmp = ONE - if i % 2 != 0: - tmp = -ONE - for j in range(MV.n): - if i != j: - tmp = tmp ^ vlst[j] - tmp = tmp*E - recp.append(tmp) - Esq = sympy.trigsimp(E.mag2(), deep=True, recursive=True) - print(Esq) - print(sympy.simplify(Esq)) - Esq_inv = ONE/Esq - i = 0 - for i in range(MV.n): - recp[i].trigsimp() - recp[i] = recp[i]*Esq_inv - if names != '': - recp[i].set_name(name_lst[i]) - i += 1 - return(recp) - -
    -def S(value): - return(MV(value, 'scalar')) -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/galgebra/latex_ex.html b/dev-py3k/_modules/sympy/galgebra/latex_ex.html deleted file mode 100644 index 3101fecb352..00000000000 --- a/dev-py3k/_modules/sympy/galgebra/latex_ex.html +++ /dev/null @@ -1,1410 +0,0 @@ - - - - - - - - - - sympy.galgebra.latex_ex — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.galgebra.latex_ex

    -#latex_ex.py
    -
    -
    -
    -import sys
    -#if sys.version.find('Stackless') >= 0:
    -#    sys.path.append('/usr/lib/python2.5/site-packages')
    -
    -import os
    -import types
    -import io
    -
    -from sympy.core import S, C, Basic, Symbol
    -from sympy.core.function import _coeff_isneg
    -from sympy.printing.printer import Printer
    -from sympy.simplify import fraction
    -import re as regrep
    -
    -import sympy.galgebra.GA
    -#import sympy.galgebra.OGA
    -import numpy
    -
    -from sympy.core.compatibility import cmp_to_key
    -from sympy.utilities import default_sort_key
    -
    -from sympy.printing.latex import accepted_latex_functions
    -
    -
    -def debug(txt):
    -    sys.stderr.write(txt + '\n')
    -    return
    -
    -
    -
    [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 - -
    -def len_cmp(str1, str2): - return(len(str2) - len(str1)) - - -def process_equals(xstr): - eq1 = xstr.find('=') - eq2 = xstr.rfind('=') - if eq1 == eq2: - return(xstr) - xstr = xstr[:eq1] + xstr[eq2:] - return(xstr) - - -
    [docs]class LatexPrinter(Printer): - r""" - A printer class which converts an expression into its LaTeX equivalent. - This class extends the LatexPrinter class currently in sympy in the - following ways: - - 1. Variable and function names can now encode multiple Greek symbols, - number, Greek, and roman super and subscripts and accents plus bold - math in an alphanumeric ASCII string consisting of ``[A-Za-z0-9_]`` - symbols - - a) Accents and bold math are implemented in reverse notation. For - example if you wished the LaTeX output to be ``\bm{\hat{\sigma}}`` - you would give the variable the name sigmahatbm. - - b) Subscripts are denoted by a single underscore and superscripts - by a double underscore so that ``A_{\rho\beta}^{25}`` would be - input as A_rhobeta__25. - - 2. Some standard function names have been improved such as asin is now - denoted by sin^{-1} and log by ln. - - 3. Several LaTeX formats for multivectors are available: - - a) Print multivector on one line - - b) Print each grade of multivector on one line - - c) Print each base of multivector on one line - - 4. A LaTeX output for numpy arrays containing sympy expressions is - implemented for up to a three dimensional array. - - 5. LaTeX formatting for raw LaTeX, eqnarray, and array is available - in simple output strings. - - a) The delimiter for raw LaTeX input is '%'. The raw input starts - on the line where '%' is first encountered and continues until - the next line where '%' is encountered. It does not matter where - '%' is in the line. - - b) The delimiter for eqnarray input is '@'. The rules are the same - as for raw input except that '=' in the first line is replaced - be '&=&' and '\begin{eqnarray*}' is added before the first line - and '\end{eqnarray*}' to after the last line in the group of - lines. - - c) The delimiter for array input is '#'. The rules are the same - as for raw input except that '\begin{equation*}' is added before - the first line and '\end{equation*}' to after the last line in - the group of lines. - - 6. Additional formats for partial derivatives: - - a) Same as sympy latex module - - b) Use subscript notation with partial symbol to indicate which - variable the differentiation is with respect to. Symbol is of - form \partial_{differentiation variable} - """ - - #printmethod ='_latex_ex' - sym_fmt = 0 - fct_fmt = 0 - pdiff_fmt = 0 - mv_fmt = 0 - str_fmt = 1 - LaTeX_flg = False - - mode = ('_', '^') - - fmt_dict = {'sym': 0, 'fct': 0, 'pdiff': 0, 'mv': 0, 'str': 1} - - fct_dict = {'sin': 'sin', 'cos': 'cos', 'tan': 'tan', 'cot': 'cot', - 'asin': 'Sin^{-1}', 'acos': 'Cos^{-1}', - 'atan': 'Tan^{-1}', 'acot': 'Cot^{-1}', - 'sinh': 'sinh', 'cosh': 'cosh', 'tanh': 'tanh', 'coth': 'coth', - 'asinh': 'Sinh^{-1}', 'acosh': 'Cosh^{-1}', - 'atanh': 'Tanh^{-1}', 'acoth': 'Coth^{-1}', - 'sqrt': 'sqrt', 'exp': 'exp', 'log': 'ln'} - - fct_dict_keys = list(fct_dict.keys()) - - greek_keys = sorted( - ('alpha', 'beta', 'gamma', 'delta', 'varepsilon', 'epsilon', 'zeta', - 'vartheta', 'theta', 'iota', 'kappa', 'lambda', 'mu', 'nu', 'xi', - 'varpi', 'pi', 'rho', 'varrho', 'varsigma', 'sigma', 'tau', 'upsilon', - 'varphi', 'phi', 'chi', 'psi', 'omega', 'Gamma', 'Delta', 'Theta', - 'Lambda', 'Xi', 'Pi', 'Sigma', 'Upsilon', 'Phi', 'Psi', 'Omega', 'partial', - 'nabla', 'eta'), key=cmp_to_key(len_cmp)) - - accent_keys = sorted( - ('hat', 'check', 'dot', 'breve', 'acute', 'ddot', 'grave', 'tilde', - 'mathring', 'bar', 'vec', 'bm', 'prm', 'abs'), key=cmp_to_key(len_cmp)) - - greek_cnt = 0 - greek_dict = {} - accent_cnt = 0 - accent_dict = {} - - preamble = '\\documentclass[10pt,letter,fleqn]{report}\n' \ - '\\pagestyle{empty}\n' \ - '\\usepackage[latin1]{inputenc}\n' \ - '\\usepackage[dvips,landscape,top=1cm,nohead,nofoot]{geometry}\n' \ - '\\usepackage{amsmath}\n' \ - '\\usepackage{bm}\n' \ - '\\usepackage{amsfonts}\n' \ - '\\usepackage{amssymb}\n' \ - '\\setlength{\\parindent}{0pt}\n' \ - '\\newcommand{\\bfrac}[2]{\\displaystyle\\frac{#1}{#2}}\n' \ - '\\newcommand{\\lp}{\\left (}\n' \ - '\\newcommand{\\rp}{\\right )}\n' \ - '\\newcommand{\\half}{\\frac{1}{2}}\n' \ - '\\newcommand{\\llt}{\\left <}\n' \ - '\\newcommand{\\rgt}{\\right >}\n' \ - '\\newcommand{\\abs}[1]{\\left |{#1}\\right | }\n' \ - '\\newcommand{\\pdiff}[2]{\\bfrac{\\partial {#1}}{\\partial {#2}}}\n' \ - '\\newcommand{\\lbrc}{\\left \\{}\n' \ - '\\newcommand{\\rbrc}{\\right \\}}\n' \ - '\\newcommand{\\W}{\\wedge}\n' \ - "\\newcommand{\\prm}[1]{{#1}'}\n" \ - '\\newcommand{\\ddt}[1]{\\bfrac{d{#1}}{dt}}\n' \ - '\\newcommand{\\R}{\\dagger}\n' \ - '\\begin{document}\n' - postscript = '\\end{document}\n' - - @staticmethod -
    [docs] def latex_bases(): - """ - Generate LaTeX strings for multivector bases - """ - if isinstance(sympy.galgebra.GA.MV.basislabel_lst, int): - sys.stderr.write( - 'MV.setup() must be executed before LatexPrinter.format()!\n') - sys.exit(1) - LatexPrinter.latexbasis_lst = [['']] - for grades in sympy.galgebra.GA.MV.basislabel_lst[1:]: - grades_lst = [] - for grade in grades: - grade_lst = [] - for base in grade: - latex_base = LatexPrinter.extended_symbol(base) - grade_lst.append(latex_base) - grades_lst.append(grade_lst) - LatexPrinter.latexbasis_lst.append(grades_lst) - return -
    - @staticmethod - def build_base(igrade, iblade, bld_flg): - if igrade == 0: - return('') - base_lst = LatexPrinter.latexbasis_lst[igrade][iblade] - if len(base_lst) == 1: - return(base_lst[0]) - base_str = '' - for base in base_lst[:-1]: - if bld_flg: - base_str += base + '\\W ' - else: - base_str += base - base_str += base_lst[-1] - return(base_str) - - @staticmethod - def format(sym=0, fct=0, pdiff=0, mv=0): - LatexPrinter.LaTeX_flg = True - LatexPrinter.fmt_dict['sym'] = sym - LatexPrinter.fmt_dict['fct'] = fct - LatexPrinter.fmt_dict['pdiff'] = pdiff - LatexPrinter.fmt_dict['mv'] = mv - LatexPrinter.fmt_dict['str'] = 1 - if sympy.galgebra.GA.MV.is_setup: - LatexPrinter.latex_bases() - LatexPrinter.redirect() - return - - @staticmethod - def str_basic(in_str): - if not LatexPrinter.LaTeX_flg: - return(str(in_str)) - Basic.__str__ = LatexPrinter.Basic__str__ - out_str = str(in_str) - Basic.__str__ = LaTeX - return(out_str) - - @staticmethod - def redirect(): - LatexPrinter.Basic__str__ = Basic.__str__ - LatexPrinter.MV__str__ = sympy.galgebra.GA.MV.__str__ - LatexPrinter.stdout = sys.stdout - sys.stdout = io.StringIO() - Basic.__str__ = LaTeX - sympy.galgebra.GA.MV.__str__ = LaTeX - return - - @staticmethod - def restore(): - LatexPrinter_stdout = sys.stdout - LatexPrinter_Basic__str__ = Basic.__str__ - LatexPrinter_MV__str__ = sympy.galgebra.GA.MV.__str__ - - sys.stdout = LatexPrinter.stdout - Basic.__str__ = LatexPrinter.Basic__str__ - sympy.galgebra.GA.MV.__str__ = LatexPrinter.MV__str__ - - LatexPrinter.stdout = LatexPrinter_stdout - LatexPrinter.Basic__str__ = LatexPrinter_Basic__str__ - LatexPrinter.MV__str__ = LatexPrinter_MV__str__ - return - - @staticmethod - def format_str(fmt='0 0 0 0'): - fmt_lst = fmt.split() - if '=' not in fmt: - LatexPrinter.fmt_dict['sym'] = int(fmt_lst[0]) - LatexPrinter.fmt_dict['fct'] = int(fmt_lst[1]) - LatexPrinter.fmt_dict['pdiff'] = int(fmt_lst[2]) - LatexPrinter.fmt_dict['mv'] = int(fmt_lst[3]) - else: - for fmt in fmt_lst: - x = fmt.split('=') - LatexPrinter.fmt_dict[x[0]] = int(x[1]) - - if LatexPrinter.LaTeX_flg is False: - if sympy.galgebra.GA.MV.is_setup: - LatexPrinter.latex_bases() - LatexPrinter.redirect() - LatexPrinter.LaTeX_flg = True - return - - @staticmethod - def append_body(xstr): - if LatexPrinter.body_flg: - LatexPrinter.body += xstr - return('') - else: - return(xstr[:-1]) - - @staticmethod - def tokenize_greek(name_str): - for sym in LatexPrinter.greek_keys: - isym = name_str.find(sym) - if isym > -1: - keystr = '@' + str(LatexPrinter.greek_cnt) - LatexPrinter.greek_cnt += 1 - LatexPrinter.greek_dict[keystr] = sym - name_str = name_str.replace(sym, keystr) - return(name_str) - - @staticmethod - def tokenize_accents(name_str): - for sym in LatexPrinter.accent_keys: - if name_str.find(sym) > -1: - keystr = '#' + str(LatexPrinter.accent_cnt) + '#' - LatexPrinter.accent_cnt += 1 - LatexPrinter.accent_dict[keystr] = '\\' + sym - name_str = name_str.replace(sym, keystr) - return(name_str) - - @staticmethod - def replace_greek_tokens(name_str): - if name_str.find('@') == -1: - return(name_str) - for token in list(LatexPrinter.greek_dict.keys()): - name_str = name_str.replace( - token, '{\\' + LatexPrinter.greek_dict[token] + '}') - LatexPrinter.greek_cnt = 0 - LatexPrinter.greek_dict = {} - return(name_str) - - @staticmethod - def replace_accent_tokens(name_str): - tmp_lst = name_str.split('#') - name_str = tmp_lst[0] - if len(tmp_lst) == 1: - return(name_str) - for x in tmp_lst[1:]: - if x != '': - name_str = '{}' + LatexPrinter.accent_dict[ - '#' + x + '#'] + '{' + name_str + '}' - LatexPrinter.accent_cnt = 0 - LatexPrinter.accent_dict = {} - return(name_str) - - @staticmethod - def extended_symbol(name_str): - name_str = LatexPrinter.tokenize_greek(name_str) - tmp_lst = name_str.split('_') - subsup_str = '' - sym_str = tmp_lst[0] - sym_str = LatexPrinter.tokenize_accents(sym_str) - sym_str = LatexPrinter.replace_accent_tokens(sym_str) - if len(tmp_lst) > 1: - imode = 0 - for x in tmp_lst[1:]: - if x == '': - imode = (imode + 1) % 2 - else: - subsup_str += LatexPrinter.mode[imode] + '{' + x + '}' - #subsup_str += LatexPrinter.mode[imode]+x+' ' - imode = (imode + 1) % 2 - name_str = sym_str + subsup_str - name_str = LatexPrinter.replace_greek_tokens(name_str) - return(name_str) - - def coefficient(self, coef, first_flg): - if isinstance(coef, C.AssocOp) and isinstance(-coef, C.AssocOp): - coef_str = r"\lp %s\rp " % self._print(coef) - else: - coef_str = self._print(coef) - if first_flg: - first_flg = False - if coef_str[0] == '+': - coef_str = coef_str[1:] - else: - if coef_str[0] != '-': - if coef_str[0] != '+': - coef_str = '+' + coef_str - if coef_str in ('1', '+1', '-1'): - if coef_str == '1': - coef_str = '' - else: - coef_str = coef_str[0] - return(coef_str, first_flg) - - def __init__(self, inline=True): - Printer.__init__(self) - self._inline = inline - - def doprint(self, expr): - tex = Printer.doprint(self, expr) - xstr = '' - - if self._inline: - if LatexPrinter.fmt_dict['fct'] == 1: - xstr = r"%s" % tex - else: - xstr = r"$%s$" % tex - else: - xstr = r"\begin{equation*}%s\end{equation*}" % tex - return(xstr) - - def _needs_brackets(self, expr): - return not ((expr.is_Integer and expr.is_nonnegative) or expr.is_Atom) - - def _do_exponent(self, expr, exp): - if exp is not None: - return r"\left(%s\right)^{%s}" % (expr, exp) - else: - return expr - - def _print_Add(self, expr): - tex = str(self._print(expr.args[0])) - - for term in expr.args[1:]: - if _coeff_isneg(term): - tex += r" %s" % self._print(term) - else: - tex += r" + %s" % self._print(term) - - return tex - - def _print_Mul(self, expr): - coeff, tail = expr.as_coeff_Mul() - - if coeff.is_negative: - coeff = -coeff - tex = "- " - else: - tex = "" - - numer, denom = fraction(tail) - - def convert(terms): - product = [] - - if not terms.is_Mul: - return str(self._print(terms)) - else: - for term in terms.args: - pretty = self._print(term) - - if term.is_Add: - product.append(r"\left(%s\right)" % pretty) - else: - product.append(str(pretty)) - - return r" ".join(product) - - if denom is S.One: - if coeff is not S.One: - tex += str(self._print(coeff)) + " " - - if numer.is_Add: - tex += r"\left(%s\right)" % convert(numer) - else: - tex += r"%s" % convert(numer) - else: - if numer is S.One: - if coeff.is_Integer: - numer *= coeff.p - elif coeff.is_Rational: - if coeff.p != 1: - numer *= coeff.p - - denom *= coeff.q - elif coeff is not S.One: - tex += str(self._print(coeff)) + " " - else: - if coeff.is_Rational and coeff.p == 1: - denom *= coeff.q - elif coeff is not S.One: - tex += str(self._print(coeff)) + " " - - tex += r"\frac{%s}{%s}" % \ - (convert(numer), convert(denom)) - - return tex - - def _print_Pow(self, expr): - if expr.exp.is_Rational and expr.exp.q == 2: - base, exp = self._print(expr.base), abs(expr.exp.p) - - if exp == 1: - tex = r"\sqrt{%s}" % base - else: - tex = r"\sqrt[%s]{%s}" % (exp, base) - - if expr.exp.is_negative: - return r"\frac{1}{%s}" % tex - else: - return tex - else: - if expr.base.is_Function: - return self._print(expr.base, self._print(expr.exp)) - else: - if expr.exp == S.NegativeOne: - #solves issue 1030 - #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: - tex = r"{%s}^{%s}" - - return tex % (self._print(expr.base), - self._print(expr.exp)) - - def _print_Derivative(self, expr): - dim = len(expr.variables) - - if dim == 1: - if LatexPrinter.fmt_dict['pdiff'] == 1: - 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: - multiplicity.append((current, i)) - - if LatexPrinter.fmt_dict['pdiff'] == 1: - 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)) - 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_Integral(self, expr): - tex, symbols = "", [] - - for symbol, limits in reversed(expr.limits): - tex += r"\int" - - if limits is not None: - if not self._inline: - tex += r"\limits" - - tex += "_{%s}^{%s}" % (self._print(limits[0]), - self._print(limits[1])) - - symbols.insert(0, "d%s" % self._print(symbol)) - - return r"%s %s\,%s" % (tex, - str(self._print(expr.function)), " ".join(symbols)) - - def _print_Limit(self, expr): - tex = r"\lim_{%s \to %s}" % (self._print(expr.var), - self._print(expr.varlim)) - - 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_Function(self, expr, exp=None): - func = expr.func.__name__ - - if hasattr(self, '_print_' + func): - return getattr(self, '_print_' + func)(expr, exp) - else: - args = [ str(self._print(arg)) for arg in expr.args ] - - if LatexPrinter.fmt_dict['fct'] == 1: - if func in LatexPrinter.fct_dict_keys: - if exp is not None: - if func in accepted_latex_functions: - name = r"\%s^{%s}" % ( - LatexPrinter.fct_dict[func], exp) - else: - name = r"\operatorname{%s}^{%s}" % ( - LatexPrinter.fct_dict[func], exp) - else: - if LatexPrinter.fct_dict[func] in accepted_latex_functions: - name = r"\%s" % LatexPrinter.fct_dict[func] - else: - name = r"\operatorname{%s}" % LatexPrinter.fct_dict[func] - name += r"\left(%s\right)" % ",".join(args) - return name - else: - func = self.print_Symbol_name(func) - if exp is not None: - name = r"{%s}^{%s}" % (func, exp) - else: - name = r"{%s}" % func - return name - else: - if exp is not None: - if func in accepted_latex_functions: - name = r"\%s^{%s}" % (func, exp) - else: - name = r"\operatorname{%s}^{%s}" % (func, exp) - else: - if func in accepted_latex_functions: - name = r"\%s" % func - else: - name = r"\operatorname{%s}" % func - return name + r"\left(%s\right)" % ",".join(args) - - def _print_floor(self, expr, exp=None): - tex = r"\lfloor{%s}\rfloor" % self._print(expr.args[0]) - - if exp is not None: - return r"%s^{%s}" % (tex, exp) - else: - return tex - - def _print_ceiling(self, expr, exp=None): - tex = r"\lceil{%s}\rceil" % self._print(expr.args[0]) - - if exp is not None: - return r"%s^{%s}" % (tex, exp) - else: - return tex - - def _print_abs(self, expr, exp=None): - tex = r"\lvert{%s}\rvert" % self._print(expr.args[0]) - - if exp is not None: - return r"%s^{%s}" % (tex, exp) - else: - return tex - - def _print_re(self, expr, exp=None): - if self._needs_brackets(expr.args[0]): - tex = r"\Re\left(%s\right)" % self._print(expr.args[0]) - else: - tex = r"\Re{%s}" % self._print(expr.args[0]) - - return self._do_exponent(tex, exp) - - def _print_im(self, expr, exp=None): - if self._needs_brackets(expr.args[0]): - tex = r"\Im\left(%s\right)" % self._print(expr.args[0]) - else: - tex = r"\Im{%s}" % self._print(expr.args[0]) - - return self._do_exponent(tex, exp) - - def _print_conjugate(self, expr, exp=None): - tex = r"\overline{%s}" % self._print(expr.args[0]) - - if exp is not None: - return r"%s^{%s}" % (tex, exp) - else: - return tex - - def _print_exp(self, expr, exp=None): - tex = r"{e}^{%s}" % self._print(expr.args[0]) - return self._do_exponent(tex, exp) - - def _print_gamma(self, expr, exp=None): - tex = r"\left(%s\right)" % self._print(expr.args[0]) - - if exp is not None: - return r"\Gamma^{%s}%s" % (exp, tex) - else: - return r"\Gamma%s" % tex - - def _print_factorial(self, expr, exp=None): - x = expr.args[0] - if self._needs_brackets(x): - tex = r"\left(%s\right)!" % self._print(x) - else: - tex = self._print(x) + "!" - - if exp is not None: - return r"%s^{%s}" % (tex, exp) - else: - return tex - - def _print_factorial(self, expr, exp=None): - x = expr.args[0] - if self._needs_brackets(x): - tex = r"!\left(%s\right)" % self._print(x) - else: - tex = "!" + self._print(x) - - if exp is not None: - return r"%s^{%s}" % (tex, exp) - else: - return tex - - def _print_binomial(self, expr, exp=None): - tex = r"{{%s}\choose{%s}}" % (self._print(expr[0]), - self._print(expr[1])) - - if exp is not None: - return r"%s^{%s}" % (tex, exp) - else: - return tex - - def _print_RisingFactorial(self, expr, exp=None): - tex = r"{\left(%s\right)}^{\left(%s\right)}" % \ - (self._print(expr[0]), self._print(expr[1])) - - return self._do_exponent(tex, exp) - - def _print_FallingFactorial(self, expr, exp=None): - tex = r"{\left(%s\right)}_{\left(%s\right)}" % \ - (self._print(expr[0]), self._print(expr[1])) - - return self._do_exponent(tex, exp) - - def _print_Rational(self, expr): - if expr.q != 1: - sign = "" - p = expr.p - if expr.p < 0: - sign = "- " - p = -p - return r"%s\frac{%d}{%d}" % (sign, p, expr.q) - else: - return self._print(expr.p) - - def _print_Infinity(self, expr): - return r"\infty" - - def _print_NegativeInfinity(self, expr): - return r"-\infty" - - def _print_ComplexInfinity(self, expr): - return r"\tilde{\infty}" - - def _print_ImaginaryUnit(self, expr): - return r"\mathbf{\imath}" - - def _print_NaN(self, expr): - return r"\bot" - - def _print_Pi(self, expr): - return r"\pi" - - def _print_Exp1(self, expr): - return r"e" - - def _print_EulerGamma(self, expr): - return r"\gamma" - - def _print_Order(self, expr): - return r"\\mathcal{O}\left(%s\right)" % \ - self._print(expr.args[0]) - - @staticmethod - def print_Symbol_name(name_str): - if len(name_str) == 1: - return (name_str) - if LatexPrinter.fmt_dict['sym'] == 1: - return LatexPrinter.extended_symbol(name_str) - else: - return(name_str) - - #convert trailing digits to subscript - m = regrep.match('(^[a-zA-Z]+)([0-9]+)$', name_str) - if m is not None: - name, sub = m.groups() - tex = self._print_Symbol(Symbol(name)) - tex = "%s_{%s}" % (tex, sub) - return tex - - # insert braces to expresions containing '_' or '^' - m = regrep.match( - '(^[a-zA-Z0-9]+)([_\^]{1})([a-zA-Z0-9]+)$', name_str) - if m is not None: - name, sep, rest = m.groups() - tex = self._print_Symbol(Symbol(name)) - tex = "%s%s{%s}" % (tex, sep, rest) - return tex - - 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' ]) - - other = set( ['aleph', 'beth', 'daleth', 'gimel', 'ell', 'eth', - 'hbar', 'hslash', 'mho' ]) - - if name_str.lower() in greek: - return "\\" + name_str - elif name_str in other: - return "\\" + name_str - else: - return name_str - - def _print_Symbol(self, expr): - return LatexPrinter.print_Symbol_name(expr.name) - - def _print_str(self, expr): - if LatexPrinter.fmt_dict['str'] > 0: - expr = expr.replace('^', '{\\wedge}') - expr = expr.replace('|', '{\\cdot}') - expr = expr.replace('__', '^') - return(expr) - - def _print_ndarray(self, expr): - shape = numpy.shape(expr) - ndim = len(shape) - expr_str = '' - - if ndim == 1: - expr_str += '#\\left [ \\begin{array}{' + shape[0]*'c' + '} \n' - for col in expr: - expr_str += self._print(col) + ' & ' - expr_str = expr_str[:-2] + '\n\\end{array}\\right ]#\n' - return(expr_str) - - if ndim == 2: - expr_str += '#\\left [ \\begin{array}{' + shape[1]*'c' + '} \n' - for row in expr[:-1]: - for xij in row[:-1]: - expr_str += self._print(xij) + ' & ' - expr_str += self._print(row[-1]) + ' \\\\ \n' - for xij in expr[-1][:-1]: - expr_str += self._print(xij) + ' & ' - expr_str += self._print( - expr[-1][-1]) + '\n \\end{array} \\right ] #\n' - return(expr_str) - - if ndim == 3: - expr_str = '#\\left \\{ \\begin{array}{' + shape[0]*'c' + '} \n' - for x in expr[:-1]: - xstr = self._print(x).replace('#', '') - expr_str += xstr + ' , & ' - xstr = self._print(expr[-1]).replace('#', '') - expr_str += xstr + '\n\\end{array} \\right \\}#\n' - return(expr_str) - - def _print_MV(self, expr): - igrade = 0 - MV_str = '' - line_lst = [] - for grade in expr.mv: - if not isinstance(grade, int): - ibase = 0 - for base in grade: - if base != 0: - tmp = Symbol('XYZW') - base_str = str(base*tmp) - if base_str[0] != '-': - base_str = '+' + base_str - base_str = base_str.replace('- ', '-') - if base_str[1:5] == 'XYZW': - base_str = base_str.replace('XYZW', '') - else: - base_str = base_str.replace('XYZW', '1') - MV_str += base_str + LatexPrinter.build_base( - igrade, ibase, expr.bladeflg) - if LatexPrinter.fmt_dict['mv'] == 3: - line_lst.append(MV_str) - MV_str = '' - ibase += 1 - if LatexPrinter.fmt_dict['mv'] == 2: - if MV_str != '': - line_lst.append(MV_str) - MV_str = '' - igrade += 1 - n_lines = len(line_lst) - if MV_str == '': - if n_lines > 0 and line_lst[0][0] == '+': - line_lst[0] = line_lst[0][1:] - else: - if MV_str[0] == '+': - MV_str = MV_str[1:] - if n_lines == 1: - MV_str = line_lst[0] - n_lines = 0 - if LatexPrinter.fmt_dict['mv'] >= 2: - MV_str = '@' + line_lst[0] + ' \\\\ \n' - for line in line_lst[1:-1]: - MV_str += '& ' + line + ' \\\\ \n' - MV_str += '& ' + line_lst[-1] + '@\n' - if MV_str == '': - MV_str = '0' - if expr.name != '': - MV_str = LatexPrinter.extended_symbol(expr.name) + ' = ' + MV_str - return(MV_str) - - def _print_OMV(self, expr): - igrade = 0 - MV_str = '' - line_lst = [] - for grade in expr.mv: - if not isinstance(grade, None): - ibase = 0 - for base in grade: - if base != 0: - tmp = Symbol('XYZW') - base_str = str(base*tmp) - if base_str[0] != '-': - base_str = '+' + base_str - base_str = base_str.replace('- ', '-') - if base_str[1:5] == 'XYZW': - base_str = base_str.replace('XYZW', '') - else: - base_str = base_str.replace('XYZW', '1') - MV_str += base_str + LatexPrinter.build_base( - igrade, ibase, expr.bladeflg) - if LatexPrinter.fmt_dict['mv'] == 3: - line_lst.append(MV_str) - MV_str = '' - ibase += 1 - if LatexPrinter.fmt_dict['mv'] == 2: - if MV_str != '': - line_lst.append(MV_str) - MV_str = '' - igrade += 1 - n_lines = len(line_lst) - if MV_str == '': - if n_lines > 0 and line_lst[0][0] == '+': - line_lst[0] = line_lst[0][1:] - else: - if MV_str[0] == '+': - MV_str = MV_str[1:] - if n_lines == 1: - MV_str = line_lst[0] - n_lines = 0 - if LatexPrinter.fmt_dict['mv'] >= 2: - MV_str = '@' + line_lst[0] + ' \\\\ \n' - for line in line_lst[1:-1]: - MV_str += '& ' + line + ' \\\\ \n' - MV_str += '& ' + line_lst[-1] + '@\n' - if MV_str == '': - MV_str = '0' - if expr.name != '': - MV_str = LatexPrinter.extended_symbol(expr.name) + ' = ' + MV_str - return(MV_str) - - def _print_Relational(self, expr): - charmap = { - "==": "=", - "<": "<", - "<=": r"\leq", - "!=": r"\neq", - } - - return "%s %s %s" % (self._print(expr.lhs), - charmap[expr.rel_op], self._print(expr.rhs)) - - def _print_Matrix(self, expr): - lines = [] - - for line in range(expr.lines): # horrible, should be 'rows' - lines.append(" & ".join([ self._print(i) for i in expr[line, :] ])) - - if self._inline: - tex = r"\left(\begin{smallmatrix}%s\end{smallmatrix}\right)" - else: - tex = r"\begin{pmatrix}%s\end{pmatrix}" - - return tex % r"\\".join(lines) - - def _print_tuple(self, expr): - return r"\begin{pmatrix}%s\end{pmatrix}" % \ - r", & ".join([ self._print(i) for i in expr ]) - - def _print_list(self, expr): - return r"\begin{bmatrix}%s\end{bmatrix}" % \ - r", & ".join([ self._print(i) for i in expr ]) - - def _print_dict(self, expr): - items = [] - - keys = list(expr.keys()) - keys.sort(key=default_sort_key) - for key in keys: - val = expr[key] - items.append("%s : %s" % (self._print(key), self._print(val))) - - return r"\begin{Bmatrix}%s\end{Bmatrix}" % r", & ".join(items) - - def _print_DiracDelta(self, expr): - if len(expr.args) == 1 or expr.args[1] == 0: - tex = r"\delta\left(%s\right)" % self._print(expr.args[0]) - else: - tex = r"\delta^{\left( %s \right)}\left( %s \right)" % ( - self._print(expr.args[1]), self._print(expr.args[0])) - return tex - -
    -
    [docs]def LaTeX(expr, inline=True): - """ - Convert the given expression to LaTeX representation. - - You can specify how the generated code will be delimited. - If the 'inline' keyword is set then inline LaTeX $ $ will - be used. Otherwise the resulting code will be enclosed in - 'equation*' environment (remember to import 'amsmath'). - - >>> from sympy import Rational - >>> from sympy.abc import tau, mu - - >>> latex((2*tau)**Rational(7,2)) - '$8 \\\\sqrt{2} \\\\sqrt[7]{\\\\tau}$' - - >>> latex((2*mu)**Rational(7,2), inline=False) - '\\\\begin{equation*}8 \\\\sqrt{2} \\\\sqrt[7]{\\\\mu}\\\\end{equation*}' - - Besides all Basic based expressions, you can recursively - convert Python containers (lists, tuples and dicts) and - also SymPy matrices: - - >>> latex([2/x, y]) - '$\\\\begin{bmatrix}\\\\frac{2}{x}, & y\\\\end{bmatrix}$' - - The extended latex printer will also append the output to a - string (LatexPrinter.body) that will be processed by xdvi() - for immediate display one xdvi() is called. - """ - xstr = LatexPrinter(inline).doprint(expr) - return (xstr) - -
    - -def Format(fmt='1 1 1 1'): - LatexPrinter.format_str(fmt) - return - - -
    [docs]def xdvi(filename='tmplatex.tex', debug=False): - """ - Post processes LaTeX output (see comments below), adds preamble and - postscript, generates tex file, inputs file to latex, displays resulting - dvi file with xdvi or yap. - """ - if not LatexPrinter.LaTeX_flg: - return - body = sys.stdout.getvalue() - LatexPrinter.restore() - - body_lst = body.split('\n') - body = '' - array_flg = False - eqnarray_flg = False - raw_flg = False - i = iter(body_lst) - line = next(i) - - while True: - if '$' in line: # Inline math expression(s) - if len(line) > 0: - line += '\\newline \n' - body += line - try: - line = next(i) - except StopIteration: - break - - elif '%' in line: # Raw LaTeX input - """ - If % in line assume line is beginning of raw LaTeX input and stop - post processing - """ - line = line.replace('%', '') - raw_flg = True - while raw_flg: - if '%' in line: - """ - If % in line assume line is end of LaTeX input and begin - post processing - """ - raw_flg = False - line = line.replace('%', '') + '\n' - else: - line += '\n' - line = process_equals(line) - body += line - try: - line = next(i) - except StopIteration: - break - - elif '#' in line: # Array input - """ - If # in line assume line is beginning of array input and contains - \begin{array} statement - """ - line = line.replace('#', '') - array_flg = True - line = '\\begin{equation*}\n' + line - while array_flg: - if '#' in line: - """ - If # in line assume line is end of array input and contains - \end{array} statement - """ - array_flg = False - line = line.replace('#', '') - line += '\\end{equation*}\n' - else: - line += '\n' - line = process_equals(line) - body += line - try: - line = next(i) - except StopIteration: - break - - elif '@' in line: # Align input - """ - If @ in line assume line is beginning of align input - """ - line = line.replace('@', '') - line = line.replace('=', '& = ') - eqnarray_flg = True - line = '\\begin{align*}\n' + line - line = process_equals(line) - body += line - try: - line = next(i) - except StopIteration: - break - while eqnarray_flg: - if '@' in line: - """ - If @ in line assume line is end of align input - """ - eqnarray_flg = False - line = line.replace('@', '') - line += '\\end{align*}\n' - else: - line + '\n' - line = process_equals(line) - body += line - try: - line = next(i) - except StopIteration: - break - - else: - if '=' in line: # Single line equation - line = '\\begin{equation*}\n' + line + '\n\\end{equation*}' - else: # Text with no math expression(s)unless \ or _ in line - if '\\' in line or '_' in line or '^' in line: - line = '\\begin{equation*}\n' + line + '\n\\end{equation*}' - else: - if len(line) > 0: - line += '\\newline \n' - line = process_equals(line) - body += line - try: - line = next(i) - except StopIteration: - break - body = LatexPrinter.preamble + body + LatexPrinter.postscript - - with open(filename, 'w') as latex_file: - latex_file.write(body) - - latex_str = None - xdvi_str = None - - if find_executable('latex') is not None: - latex_str = 'latex' - - if find_executable('xdvi') is not None: - xdvi_str = 'xdvi' - - if find_executable('yap') is not None: - xdvi_str = 'yap' - - if latex_str is not None and xdvi_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 - if sys.platform.startswith('linux'): - os.system(latex_str + ' ' + filename[:-4] + ' > /dev/null') - else: - os.system(latex_str + ' ' + filename[:-4] + ' > NUL') - os.system(xdvi_str + ' ' + filename[:-4] + ' &') - LatexPrinter.LaTeX_flg = False - return - -
    -
    [docs]def MV_format(mv_fmt): - """ - 0 or 1 - Print multivector on one line - - 2 - Print each multivector grade on one line - - 3 - Print each multivector base on one line - """ - if LatexPrinter.LaTeX_flg: - LatexPrinter.fmt_dict['mv'] = mv_fmt - return - -
    -
    [docs]def fct_format(fct_fmt): - """ - 0 - Default sympy latex format - - 1 - Do not print arguments of arbitrary functions. - Use symbol font for arbitrary functions. - Use enhanced symbol naming for arbitrary functions. - Use new names for standard functions (acos -> Cos^{-1}) - - """ - if LatexPrinter.LaTeX_flg: - LatexPrinter.fct = fct_fmt - return - -
    -
    [docs]def pdiff_format(pdiff_fmt): - """ - 0 - Use default sympy partial derivative format - - 1 - Contracted derivative format (no fraction symbols) - """ - if LatexPrinter.LaTeX_flg: - LatexPrinter.fmt_dict['pdiff'] = pdiff_fmt - return - -
    -
    [docs]def sym_format(sym_fmt): - """ - 0 - Use default sympy format - - 1 - Use extended symbol format including multiple Greek letters in - basic symbol (symbol preceding sub and superscripts)and in - sub and superscripts of basic symbol and accents in basic symbol - - """ - if LatexPrinter.LaTeX_flg: - LatexPrinter.fmt_dict['sym'] = sym_fmt - return - -
    -
    [docs]def str_format(str_fmt): - """ - 0 - Use default sympy format - - 1 - Use extended symbol format including multiple Greek letters in - basic symbol (symbol preceding sub and superscripts)and in - sub and superscripts of basic symbol and accents in basic symbol - - """ - if LatexPrinter.LaTeX_flg: - LatexPrinter.fmt_dict['str'] = str_fmt - return - -
    -def ext_str(xstr): - return(LatexPrinter.extended_symbol(xstr)) -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/geometry/curve.html b/dev-py3k/_modules/sympy/geometry/curve.html deleted file mode 100644 index 9d10fcd8e37..00000000000 --- a/dev-py3k/_modules/sympy/geometry/curve.html +++ /dev/null @@ -1,450 +0,0 @@ - - - - - - - - - - sympy.geometry.curve — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.geometry.curve

    -"""Curves in 2-dimensional Euclidean space.
    -
    -Contains
    -========
    -Curve
    -
    -"""
    -
    -from sympy.core import sympify
    -from sympy.core.compatibility import is_sequence
    -from sympy.core.containers import Tuple
    -from sympy.geometry.entity import GeometryEntity
    -from sympy.geometry.point import Point
    -from .util import _symbol
    -
    -
    -
    [docs]class Curve(GeometryEntity): - """A curve in space. - - A curve is defined by parametric functions for the coordinates, a - parameter and the lower and upper bounds for the parameter value. - - Parameters - ========== - - function : list of functions - limits : 3-tuple - Function parameter and lower and upper bounds. - - Attributes - ========== - - functions - parameter - limits - - Raises - ====== - - ValueError - When `functions` are specified incorrectly. - When `limits` are specified incorrectly. - - See Also - ======== - - sympy.core.function.Function - sympy.polys.polyfuncs.interpolate - - Examples - ======== - - >>> from sympy import sin, cos, Symbol, interpolate - >>> from sympy.abc import t, a - >>> from sympy.geometry import Curve - >>> C = Curve((sin(t), cos(t)), (t, 0, 2)) - >>> C.functions - (sin(t), cos(t)) - >>> C.limits - (t, 0, 2) - >>> C.parameter - t - >>> C = Curve((t, interpolate([1, 4, 9, 16], t)), (t, 0, 1)); C - Curve((t, t**2), (t, 0, 1)) - >>> C.subs(t, 4) - Point(4, 16) - >>> C.arbitrary_point(a) - Point(a, a**2) - """ - - def __new__(cls, function, limits): - fun = sympify(function) - if not is_sequence(fun) or len(fun) != 2: - raise ValueError("Function argument should be (x(t), y(t)) " - "but got %s" % str(function)) - if not is_sequence(limits) or len(limits) != 3: - raise ValueError("Limit argument should be (t, tmin, tmax) " - "but got %s" % str(limits)) - - return GeometryEntity.__new__(cls, Tuple(*fun), Tuple(*limits)) - - def _eval_subs(self, old, new): - if old == self.parameter: - return Point(*[f.subs(old, new) for f in self.functions]) - - @property -
    [docs] def free_symbols(self): - """ - Return a set of symbols other than the bound symbols used to - parametrically define the Curve. - - Examples - ======== - - >>> from sympy.abc import t, a - >>> from sympy.geometry import Curve - >>> Curve((t, t**2), (t, 0, 2)).free_symbols - set() - >>> Curve((t, t**2), (t, a, 2)).free_symbols - set([a]) - """ - free = set() - for a in self.functions + self.limits[1:]: - free |= a.free_symbols - free = free.difference(set([self.parameter])) - return free -
    - @property -
    [docs] def functions(self): - """The functions specifying the curve. - - Returns - ======= - - functions : list of parameterized coordinate functions. - - See Also - ======== - - parameter - - Examples - ======== - - >>> from sympy.abc import t - >>> from sympy.geometry import Curve - >>> C = Curve((t, t**2), (t, 0, 2)) - >>> C.functions - (t, t**2) - - """ - return self.args[0] -
    - @property -
    [docs] def parameter(self): - """The curve function variable. - - Returns - ======= - - parameter : SymPy symbol - - See Also - ======== - - functions - - Examples - ======== - - >>> from sympy.abc import t - >>> from sympy.geometry import Curve - >>> C = Curve([t, t**2], (t, 0, 2)) - >>> C.parameter - t - - """ - return self.args[1][0] -
    - @property -
    [docs] def limits(self): - """The limits for the curve. - - Returns - ======= - - limits : tuple - Contains parameter and lower and upper limits. - - See Also - ======== - - plot_interval - - Examples - ======== - - >>> from sympy.abc import t - >>> from sympy.geometry import Curve - >>> C = Curve([t, t**3], (t, -2, 2)) - >>> C.limits - (t, -2, 2) - - """ - return self.args[1] -
    -
    [docs] def rotate(self, angle=0, pt=None): - """Rotate ``angle`` radians counterclockwise about Point ``pt``. - - The default pt is the origin, Point(0, 0). - - Examples - ======== - >>> from sympy.geometry.curve import Curve - >>> from sympy.abc import x - >>> from sympy import pi - >>> Curve((x, x), (x, 0, 1)).rotate(pi/2) - Curve((-x, x), (x, 0, 1)) - """ - from sympy.matrices import Matrix, rot_axis3 - pt = -Point(pt or (0, 0)) - rv = self.translate(*pt.args) - f = list(rv.functions) - f.append(0) - f = Matrix(1, 3, f) - f *= rot_axis3(angle) - rv = self.func(f[0, :2].tolist()[0], self.limits) - if pt is not None: - pt = -pt - return rv.translate(*pt.args) - return rv -
    -
    [docs] def scale(self, x=1, y=1, pt=None): - """Override GeometryEntity.scale since Curve is not made up of Points. - - Examples - ======== - >>> from sympy.geometry.curve import Curve - >>> from sympy import pi - >>> from sympy.abc import x - >>> Curve((x, x), (x, 0, 1)).scale(2) - Curve((2*x, x), (x, 0, 1)) - """ - if pt: - pt = Point(pt) - return self.translate(*(-pt).args).scale(x, y).translate(*pt.args) - fx, fy = self.functions - return self.func((fx*x, fy*y), self.limits) -
    -
    [docs] def translate(self, x=0, y=0): - """Translate the Curve by (x, y). - - Examples - ======== - >>> from sympy.geometry.curve import Curve - >>> from sympy import pi - >>> from sympy.abc import x - >>> Curve((x, x), (x, 0, 1)).translate(1, 2) - Curve((x + 1, x + 2), (x, 0, 1)) - """ - fx, fy = self.functions - return self.func((fx + x, fy + y), self.limits) -
    -
    [docs] def arbitrary_point(self, parameter='t'): - """ - A parameterized point on the curve. - - Parameters - ========== - - parameter : str or Symbol, optional - Default value is 't'; - the Curve's parameter is selected with None or self.parameter - otherwise the provided symbol is used. - - Returns - ======= - - arbitrary_point : Point - - Raises - ====== - - ValueError - When `parameter` already appears in the functions. - - See Also - ======== - - sympy.geometry.point.Point - - Examples - ======== - - >>> from sympy import Symbol - >>> from sympy.abc import s - >>> from sympy.geometry import Curve - >>> C = Curve([2*s, s**2], (s, 0, 2)) - >>> C.arbitrary_point() - Point(2*t, t**2) - >>> C.arbitrary_point(C.parameter) - Point(2*s, s**2) - >>> C.arbitrary_point(None) - Point(2*s, s**2) - >>> C.arbitrary_point(Symbol('a')) - Point(2*a, a**2) - - """ - if parameter is None: - return Point(*self.functions) - - tnew = _symbol(parameter, self.parameter) - t = self.parameter - if (tnew.name != t.name and - tnew.name in (f.name for f in self.free_symbols)): - raise ValueError('Symbol %s already appears in object ' - 'and cannot be used as a parameter.' % tnew.name) - return Point(*[w.subs(t, tnew) for w in self.functions]) -
    -
    [docs] def plot_interval(self, parameter='t'): - """The plot interval for the default geometric plot of the curve. - - Parameters - ========== - - parameter : str or Symbol, optional - Default value is 't'; - otherwise the provided symbol is used. - - Returns - ======= - - plot_interval : list (plot interval) - [parameter, lower_bound, upper_bound] - - See Also - ======== - - limits : Returns limits of the parameter interval - - Examples - ======== - - >>> from sympy import Curve, sin - >>> from sympy.abc import x, t, s - >>> Curve((x, sin(x)), (x, 1, 2)).plot_interval() - [t, 1, 2] - >>> Curve((x, sin(x)), (x, 1, 2)).plot_interval(s) - [s, 1, 2] - - """ - t = _symbol(parameter, self.parameter) - return [t] + list(self.limits[1:])
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/geometry/ellipse.html b/dev-py3k/_modules/sympy/geometry/ellipse.html deleted file mode 100644 index 0795091dafb..00000000000 --- a/dev-py3k/_modules/sympy/geometry/ellipse.html +++ /dev/null @@ -1,1441 +0,0 @@ - - - - - - - - - - sympy.geometry.ellipse — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.geometry.ellipse

    -"""Elliptical geometrical entities.
    -
    -Contains
    -* Ellipse
    -* Circle
    -
    -"""
    -
    -from sympy.core import S, C, sympify, pi, Dummy
    -from sympy.core.logic import fuzzy_bool
    -from sympy.simplify import simplify, trigsimp
    -from sympy.functions.elementary.miscellaneous import sqrt, Max, Min
    -from sympy.functions.elementary.complexes import im
    -from sympy.geometry.exceptions import GeometryError
    -from sympy.solvers import solve
    -from .entity import GeometryEntity
    -from .point import Point
    -from .line import LinearEntity, Line
    -from .util import _symbol, idiff
    -
    -import random
    -
    -
    -
    [docs]class Ellipse(GeometryEntity): - """An elliptical GeometryEntity. - - Parameters - ========== - - center : Point, optional - Default value is Point(0, 0) - hradius : number or SymPy expression, optional - vradius : number or SymPy expression, optional - eccentricity : number or SymPy expression, optional - Two of `hradius`, `vradius` and `eccentricity` must be supplied to - create an Ellipse. The third is derived from the two supplied. - - Attributes - ========== - - center - hradius - vradius - area - circumference - eccentricity - periapsis - apoapsis - focus_distance - foci - - Raises - ====== - - GeometryError - When `hradius`, `vradius` and `eccentricity` are incorrectly supplied - as parameters. - TypeError - When `center` is not a Point. - - See Also - ======== - - Circle - - Notes - ----- - Constructed from a center and two radii, the first being the horizontal - radius (along the x-axis) and the second being the vertical radius (along - the y-axis). - - When symbolic value for hradius and vradius are used, any calculation that - refers to the foci or the major or minor axis will assume that the ellipse - has its major radius on the x-axis. If this is not true then a manual - rotation is necessary. - - Examples - ======== - - >>> from sympy import Ellipse, Point, Rational - >>> e1 = Ellipse(Point(0, 0), 5, 1) - >>> e1.hradius, e1.vradius - (5, 1) - >>> e2 = Ellipse(Point(3, 1), hradius=3, eccentricity=Rational(4, 5)) - >>> e2 - Ellipse(Point(3, 1), 3, 9/5) - - Plotting: - - >>> from sympy import Circle, Plot, Segment - >>> c1 = Circle(Point(0,0), 1) - >>> Plot(c1) # doctest: +SKIP - [0]: cos(t), sin(t), 'mode=parametric' - >>> p = Plot() # doctest: +SKIP - >>> p[0] = c1 # doctest: +SKIP - >>> radius = Segment(c1.center, c1.random_point()) # doctest: +SKIP - >>> p[1] = radius # doctest: +SKIP - >>> p # doctest: +SKIP - [0]: cos(t), sin(t), 'mode=parametric' - [1]: t*cos(1.546086215036205357975518382), - t*sin(1.546086215036205357975518382), 'mode=parametric' - - """ - - def __new__( - cls, center=None, hradius=None, vradius=None, eccentricity=None, - **kwargs): - hradius = sympify(hradius) - vradius = sympify(vradius) - - eccentricity = sympify(eccentricity) - - if center is None: - center = Point(0, 0) - else: - center = Point(center) - - if len([_f for _f in (hradius, vradius, eccentricity) if _f]) != 2: - raise ValueError('Exactly two arguments of "hradius", ' - '"vradius", and "eccentricity" must not be None."') - - if eccentricity is not None: - if hradius is None: - hradius = vradius / sqrt(1 - eccentricity**2) - elif vradius is None: - vradius = hradius * sqrt(1 - eccentricity**2) - - if hradius == vradius: - return Circle(center, hradius, **kwargs) - - return GeometryEntity.__new__(cls, center, hradius, vradius, **kwargs) - - @property -
    [docs] def center(self): - """The center of the ellipse. - - Returns - ======= - - center : number - - See Also - ======== - - sympy.geometry.point.Point - - Examples - ======== - - >>> from sympy import Point, Ellipse - >>> p1 = Point(0, 0) - >>> e1 = Ellipse(p1, 3, 1) - >>> e1.center - Point(0, 0) - - """ - return self.args[0] -
    - @property -
    [docs] def hradius(self): - """The horizontal radius of the ellipse. - - Returns - ======= - - hradius : number - - See Also - ======== - - vradius, major, minor - - Examples - ======== - - >>> from sympy import Point, Ellipse - >>> p1 = Point(0, 0) - >>> e1 = Ellipse(p1, 3, 1) - >>> e1.hradius - 3 - - """ - return self.args[1] -
    - @property -
    [docs] def vradius(self): - """The vertical radius of the ellipse. - - Returns - ======= - - vradius : number - - See Also - ======== - - hradius, major, minor - - Examples - ======== - - >>> from sympy import Point, Ellipse - >>> p1 = Point(0, 0) - >>> e1 = Ellipse(p1, 3, 1) - >>> e1.vradius - 1 - - """ - return self.args[2] -
    - @property -
    [docs] def minor(self): - """Shorter axis of the ellipse (if it can be determined) else vradius. - - Returns - ======= - - minor : number or expression - - See Also - ======== - - hradius, vradius, major - - Examples - ======== - - >>> from sympy import Point, Ellipse, Symbol - >>> p1 = Point(0, 0) - >>> e1 = Ellipse(p1, 3, 1) - >>> e1.minor - 1 - - >>> a = Symbol('a') - >>> b = Symbol('b') - >>> Ellipse(p1, a, b).minor - b - >>> Ellipse(p1, b, a).minor - a - - >>> m = Symbol('m') - >>> M = m + 1 - >>> Ellipse(p1, m, M).minor - m - - """ - rv = Min(*self.args[1:3]) - if rv.func is Min: - return self.vradius - return rv -
    - @property -
    [docs] def major(self): - """Longer axis of the ellipse (if it can be determined) else hradius. - - Returns - ======= - - major : number or expression - - See Also - ======== - - hradius, vradius, minor - - Examples - ======== - - >>> from sympy import Point, Ellipse, Symbol - >>> p1 = Point(0, 0) - >>> e1 = Ellipse(p1, 3, 1) - >>> e1.major - 3 - - >>> a = Symbol('a') - >>> b = Symbol('b') - >>> Ellipse(p1, a, b).major - a - >>> Ellipse(p1, b, a).major - b - - >>> m = Symbol('m') - >>> M = m + 1 - >>> Ellipse(p1, m, M).major - m + 1 - - """ - rv = Max(*self.args[1:3]) - if rv.func is Max: - return self.hradius - return rv -
    - @property -
    [docs] def area(self): - """The area of the ellipse. - - Returns - ======= - - area : number - - Examples - ======== - - >>> from sympy import Point, Ellipse - >>> p1 = Point(0, 0) - >>> e1 = Ellipse(p1, 3, 1) - >>> e1.area - 3*pi - - """ - return simplify(S.Pi * self.hradius * self.vradius) -
    - @property -
    [docs] def circumference(self): - """The circumference of the ellipse. - - Examples - ======== - - >>> from sympy import Point, Ellipse - >>> p1 = Point(0, 0) - >>> e1 = Ellipse(p1, 3, 1) - >>> e1.circumference - 12*Integral(sqrt((-8*_x**2/9 + 1)/(-_x**2 + 1)), (_x, 0, 1)) - - """ - if self.eccentricity == 1: - return 2*pi*self.hradius - else: - x = C.Dummy('x', real=True) - return 4*self.major*C.Integral( - sqrt((1 - (self.eccentricity*x)**2)/(1 - x**2)), (x, 0, 1)) -
    - @property -
    [docs] def eccentricity(self): - """The eccentricity of the ellipse. - - Returns - ======= - - eccentricity : number - - Examples - ======== - - >>> from sympy import Point, Ellipse, sqrt - >>> p1 = Point(0, 0) - >>> e1 = Ellipse(p1, 3, sqrt(2)) - >>> e1.eccentricity - sqrt(7)/3 - - """ - return self.focus_distance / self.major -
    - @property -
    [docs] def periapsis(self): - """The periapsis of the ellipse. - - The shortest distance between the focus and the contour. - - Returns - ======= - - periapsis : number - - See Also - ======== - - apoapsis : Returns greatest distance between focus and contour - - Examples - ======== - - >>> from sympy import Point, Ellipse - >>> p1 = Point(0, 0) - >>> e1 = Ellipse(p1, 3, 1) - >>> e1.periapsis - -2*sqrt(2) + 3 - - """ - return self.major * (1 - self.eccentricity) -
    - @property -
    [docs] def apoapsis(self): - """The apoapsis of the ellipse. - - The greatest distance between the focus and the contour. - - Returns - ======= - - apoapsis : number - - See Also - ======== - - periapsis : Returns shortest distance between foci and contour - - Examples - ======== - - >>> from sympy import Point, Ellipse - >>> p1 = Point(0, 0) - >>> e1 = Ellipse(p1, 3, 1) - >>> e1.apoapsis - 2*sqrt(2) + 3 - - """ - return self.major * (1 + self.eccentricity) -
    - @property -
    [docs] def focus_distance(self): - """The focale distance of the ellipse. - - The distance between the center and one focus. - - Returns - ======= - - focus_distance : number - - See Also - ======== - - foci - - Examples - ======== - - >>> from sympy import Point, Ellipse - >>> p1 = Point(0, 0) - >>> e1 = Ellipse(p1, 3, 1) - >>> e1.focus_distance - 2*sqrt(2) - - """ - return Point.distance(self.center, self.foci[0]) -
    - @property -
    [docs] def foci(self): - """The foci of the ellipse. - - Notes - ----- - The foci can only be calculated if the major/minor axes are known. - - Raises - ====== - - ValueError - When the major and minor axis cannot be determined. - - See Also - ======== - - sympy.geometry.point.Point - focus_distance : Returns the distance between focus and center - - Examples - ======== - - >>> from sympy import Point, Ellipse - >>> p1 = Point(0, 0) - >>> e1 = Ellipse(p1, 3, 1) - >>> e1.foci - (Point(-2*sqrt(2), 0), Point(2*sqrt(2), 0)) - - """ - c = self.center - hr, vr = self.hradius, self.vradius - if hr == vr: - return (c, c) - - # calculate focus distance manually, since focus_distance calls this routine - fd = sqrt(self.major**2 - self.minor**2) - if hr == self.minor: - # foci on the y-axis - return (c + Point(0, -fd), c + Point(0, fd)) - elif hr == self.major: - # foci on the x-axis - return (c + Point(-fd, 0), c + Point(fd, 0)) -
    -
    [docs] def rotate(self, angle=0, pt=None): - """Rotate ``angle`` radians counterclockwise about Point ``pt``. - - Note: since the general ellipse is not supported, the axes of - the ellipse will not be rotated. Only the center is rotated to - a new position. - - Examples - ======== - - >>> from sympy import Ellipse, pi - >>> Ellipse((1, 0), 2, 1).rotate(pi/2) - Ellipse(Point(0, 1), 2, 1) - """ - return super(Ellipse, self).rotate(angle, pt) -
    -
    [docs] def scale(self, x=1, y=1, pt=None): - """Override GeometryEntity.scale since it is the major and minor - axes which must be scaled and they are not GeometryEntities. - - Examples - ======== - - >>> from sympy import Ellipse - >>> Ellipse((0, 0), 2, 1).scale(2, 4) - Circle(Point(0, 0), 4) - >>> Ellipse((0, 0), 2, 1).scale(2) - Ellipse(Point(0, 0), 4, 1) - """ - c = self.center - if pt: - pt = Point(pt) - return self.translate(*(-pt).args).scale(x, y).translate(*pt.args) - h = self.hradius - v = self.vradius - return self.func(c.scale(x, y), hradius=h*x, vradius=v*y) -
    -
    [docs] def encloses_point(self, p): - """ - Return True if p is enclosed by (is inside of) self. - - Notes - ----- - Being on the border of self is considered False. - - Parameters - ========== - - p : Point - - Returns - ======= - - encloses_point : True, False or None - - See Also - ======== - - sympy.geometry.point.Point - - Examples - ======== - - >>> from sympy import Ellipse, S - >>> from sympy.abc import t - >>> e = Ellipse((0, 0), 3, 2) - >>> e.encloses_point((0, 0)) - True - >>> e.encloses_point(e.arbitrary_point(t).subs(t, S.Half)) - False - >>> e.encloses_point((4, 0)) - False - - """ - p = Point(p) - if p in self: - return False - - if len(self.foci) == 2: - # if the combined distance from the foci to p (h1 + h2) is less - # than the combined distance from the foci to the minor axis - # (which is the same as the major axis length) then p is inside - # the ellipse - h1, h2 = [f.distance(p) for f in self.foci] - test = 2*self.major - (h1 + h2) - else: - test = self.radius - self.center.distance(p) - - return fuzzy_bool(test.is_positive) -
    -
    [docs] def tangent_lines(self, p): - """Tangent lines between `p` and the ellipse. - - If `p` is on the ellipse, returns the tangent line through point `p`. - Otherwise, returns the tangent line(s) from `p` to the ellipse, or - None if no tangent line is possible (e.g., `p` inside ellipse). - - Parameters - ========== - - p : Point - - Returns - ======= - - tangent_lines : list with 1 or 2 Lines - - Raises - ====== - - NotImplementedError - Can only find tangent lines for a point, `p`, on the ellipse. - - See Also - ======== - - sympy.geometry.point.Point, sympy.geometry.line.Line - - Examples - ======== - - >>> from sympy import Point, Ellipse - >>> e1 = Ellipse(Point(0, 0), 3, 2) - >>> e1.tangent_lines(Point(3, 0)) - [Line(Point(3, 0), Point(3, -12))] - - >>> # This will plot an ellipse together with a tangent line. - >>> from sympy import Point, Ellipse, Plot - >>> e = Ellipse(Point(0,0), 3, 2) - >>> t = e.tangent_lines(e.random_point()) # doctest: +SKIP - >>> p = Plot() # doctest: +SKIP - >>> p[0] = e # doctest: +SKIP - >>> p[1] = t # doctest: +SKIP - - """ - if self.encloses_point(p): - return [] - - if p in self: - delta = self.center - p - rise = (self.vradius ** 2)*delta.x - run = -(self.hradius ** 2)*delta.y - p2 = Point(simplify(p.x + run), - simplify(p.y + rise)) - return [Line(p, p2)] - else: - if len(self.foci) == 2: - f1, f2 = self.foci - maj = self.hradius - test = (2*maj - - Point.distance(f1, p) - - Point.distance(f2, p)) - else: - test = self.radius - Point.distance(self.center, p) - if test.is_number and test.is_positive: - return [] - # else p is outside the ellipse or we can't tell. In case of the - # latter, the solutions returned will only be valid if - # the point is not inside the ellipse; if it is, nan will result. - x, y = Dummy('x'), Dummy('y') - eq = self.equation(x, y) - dydx = idiff(eq, y, x) - slope = Line(p, Point(x, y)).slope - tangent_points = solve([slope - dydx, eq], [x, y]) - - # handle horizontal and vertical tangent lines - if len(tangent_points) == 1: - assert tangent_points[0][ - 0] == p.x or tangent_points[0][1] == p.y - return [Line(p, p + Point(1, 0)), Line(p, p + Point(0, 1))] - - # others - return [Line(p, tangent_points[0]), Line(p, tangent_points[1])] -
    -
    [docs] def is_tangent(self, o): - """Is `o` tangent to the ellipse? - - Parameters - ========== - - o : GeometryEntity - An Ellipse, LinearEntity or Polygon - - Raises - ====== - - NotImplementedError - When the wrong type of argument is supplied. - - Returns - ======= - - is_tangent: boolean - True if o is tangent to the ellipse, False otherwise. - - See Also - ======== - - tangent_lines - - Examples - ======== - - >>> from sympy import Point, Ellipse, Line - >>> p0, p1, p2 = Point(0, 0), Point(3, 0), Point(3, 3) - >>> e1 = Ellipse(p0, 3, 2) - >>> l1 = Line(p1, p2) - >>> e1.is_tangent(l1) - True - - """ - inter = None - if isinstance(o, Ellipse): - inter = self.intersection(o) - if isinstance(inter, Ellipse): - return False - return (inter is not None and isinstance(inter[0], Point) - and len(inter) == 1) - elif isinstance(o, LinearEntity): - inter = self._do_line_intersection(o) - if inter is not None and len(inter) == 1: - return inter[0] in o - else: - return False - elif isinstance(o, Polygon): - c = 0 - for seg in o.sides: - inter = self._do_line_intersection(seg) - c += len([True for point in inter if point in seg]) - return c == 1 - else: - raise NotImplementedError("Unknown argument type") -
    -
    [docs] def arbitrary_point(self, parameter='t'): - """A parameterized point on the ellipse. - - Parameters - ========== - - parameter : str, optional - Default value is 't'. - - Returns - ======= - - arbitrary_point : Point - - Raises - ====== - - ValueError - When `parameter` already appears in the functions. - - See Also - ======== - - sympy.geometry.point.Point - - Examples - ======== - - >>> from sympy import Point, Ellipse - >>> e1 = Ellipse(Point(0, 0), 3, 2) - >>> e1.arbitrary_point() - Point(3*cos(t), 2*sin(t)) - - """ - t = _symbol(parameter) - if t.name in (f.name for f in self.free_symbols): - raise ValueError('Symbol %s already appears in object and cannot be used as a parameter.' % t.name) - return Point(self.center.x + self.hradius*C.cos(t), - self.center.y + self.vradius*C.sin(t)) -
    -
    [docs] def plot_interval(self, parameter='t'): - """The plot interval for the default geometric plot of the Ellipse. - - Parameters - ========== - - parameter : str, optional - Default value is 't'. - - Returns - ======= - - plot_interval : list - [parameter, lower_bound, upper_bound] - - Examples - ======== - - >>> from sympy import Point, Ellipse - >>> e1 = Ellipse(Point(0, 0), 3, 2) - >>> e1.plot_interval() - [t, -pi, pi] - - """ - t = _symbol(parameter) - return [t, -S.Pi, S.Pi] -
    -
    [docs] def random_point(self, seed=None): - """A random point on the ellipse. - - Returns - ======= - - point : Point - - See Also - ======== - - sympy.geometry.point.Point - arbitrary_point : Returns parameterized point on ellipse - - Notes - ----- - - A random point may not appear to be on the ellipse, ie, `p in e` may - return False. This is because the coordinates of the point will be - floating point values, and when these values are substituted into the - equation for the ellipse the result may not be zero because of floating - point rounding error. - - Examples - ======== - - >>> from sympy import Point, Ellipse, Segment - >>> e1 = Ellipse(Point(0, 0), 3, 2) - >>> e1.random_point() # gives some random point - Point(...) - >>> p1 = e1.random_point(seed=0); p1.n(2) - Point(2.1, 1.4) - - The random_point method assures that the point will test as being - in the ellipse: - - >>> p1 in e1 - True - - Notes - ===== - - An arbitrary_point with a random value of t substituted into it may - not test as being on the ellipse because the expression tested that - a point is on the ellipse doesn't simplify to zero and doesn't evaluate - exactly to zero: - - >>> from sympy.abc import t - >>> e1.arbitrary_point(t) - Point(3*cos(t), 2*sin(t)) - >>> p2 = _.subs(t, 0.1) - >>> p2 in e1 - False - - Note that arbitrary_point routine does not take this approach. A value for - cos(t) and sin(t) (not t) is substituted into the arbitrary point. There is - a small chance that this will give a point that will not test as being - in the ellipse, so the process is repeated (up to 10 times) until a - valid point is obtained. - - """ - from sympy import nsimplify, sin, cos - t = _symbol('t') - x, y = self.arbitrary_point(t).args - # get a random value in [-1, 1) corresponding to cos(t) - # and confirm that it will test as being in the ellipse - if seed is not None: - rng = random.Random(seed) - else: - rng = random - for i in range(10): # should be enough? - c = nsimplify(2*rng.random() - 1) - s = sqrt(1 - c**2) - p1 = Point(x.subs(cos(t), c), y.subs(sin(t), s)) - if p1 in self: - return p1 - raise GeometryError( - 'Having problems generating a point in the ellipse.') -
    -
    [docs] def equation(self, x='x', y='y'): - """The equation of the ellipse. - - Parameters - ========== - - x : str, optional - Label for the x-axis. Default value is 'x'. - y : str, optional - Label for the y-axis. Default value is 'y'. - - Returns - ======= - - equation : sympy expression - - See Also - ======== - - arbitrary_point : Returns parameterized point on ellipse - - Examples - ======== - - >>> from sympy import Point, Ellipse - >>> e1 = Ellipse(Point(1, 0), 3, 2) - >>> e1.equation() - y**2/4 + (x/3 - 1/3)**2 - 1 - - """ - x = _symbol(x) - y = _symbol(y) - t1 = ((x - self.center.x) / self.hradius)**2 - t2 = ((y - self.center.y) / self.vradius)**2 - return t1 + t2 - 1 -
    - def _do_line_intersection(self, o): - """ - Find the intersection of a LinearEntity and the ellipse. - - All LinearEntities are treated as a line and filtered at - the end to see that they lie in o. - - """ - - hr_sq = self.hradius ** 2 - vr_sq = self.vradius ** 2 - lp = o.points - - ldir = lp[1] - lp[0] - diff = lp[0] - self.center - mdir = Point(ldir.x/hr_sq, ldir.y/vr_sq) - mdiff = Point(diff.x/hr_sq, diff.y/vr_sq) - - a = ldir.dot(mdir) - b = ldir.dot(mdiff) - c = diff.dot(mdiff) - 1 - det = simplify(b*b - a*c) - - result = [] - if det == 0: - t = -b / a - result.append(lp[0] + (lp[1] - lp[0]) * t) - else: - is_good = True - try: - is_good = (det > 0) - except NotImplementedError: # symbolic, allow - is_good = True - - if is_good: - root = sqrt(det) - t_a = (-b - root) / a - t_b = (-b + root) / a - result.append( lp[0] + (lp[1] - lp[0]) * t_a ) - result.append( lp[0] + (lp[1] - lp[0]) * t_b ) - - return [r for r in result if r in o] - - def _do_circle_intersection(self, o): - """The intersection of an Ellipse and a Circle. - - Private helper method for `intersection`. - - """ - variables = self.equation().atoms(C.Symbol) - if len(variables) > 2: - return None - if self.center == o.center: - a, b, r = o.major, o.minor, self.radius - x = a*sqrt(simplify((r**2 - b**2)/(a**2 - b**2))) - y = b*sqrt(simplify((a**2 - r**2)/(a**2 - b**2))) - return list(set([Point(x, y), Point(x, -y), Point(-x, y), - Point(-x, -y)])) - else: - x, y = variables - xx = solve(self.equation(), x) - intersect = [] - for xi in xx: - yy = solve(o.equation().subs(x, xi), y) - for yi in yy: - intersect.append(Point(xi, yi)) - return list(set(intersect)) - - def _do_ellipse_intersection(self, o): - """The intersection of two ellipses. - - Private helper method for `intersection`. - - """ - x = Dummy('x') - y = Dummy('y') - seq = self.equation(x, y) - oeq = o.equation(x, y) - result = solve([seq, oeq], [x, y]) - return [Point(*r) for r in result if im(r[0]).is_zero is not False and im(r[1]).is_zero is not False] - -
    [docs] def intersection(self, o): - """The intersection of this ellipse and another geometrical entity - `o`. - - Parameters - ========== - - o : GeometryEntity - - Returns - ======= - - intersection : list of GeometryEntity objects - - Notes - ----- - Currently supports intersections with Point, Line, Segment, Ray, - Circle and Ellipse types. - - See Also - ======== - - sympy.geometry.entity.GeometryEntity - - Examples - ======== - - >>> from sympy import Ellipse, Point, Line, sqrt - >>> e = Ellipse(Point(0, 0), 5, 7) - >>> e.intersection(Point(0, 0)) - [] - >>> e.intersection(Point(5, 0)) - [Point(5, 0)] - >>> e.intersection(Line(Point(0,0), Point(0, 1))) - [Point(0, -7), Point(0, 7)] - >>> e.intersection(Line(Point(5,0), Point(5, 1))) - [Point(5, 0)] - >>> e.intersection(Line(Point(6,0), Point(6, 1))) - [] - >>> e = Ellipse(Point(-1, 0), 4, 3) - >>> e.intersection(Ellipse(Point(1, 0), 4, 3)) - [Point(0, -3*sqrt(15)/4), Point(0, 3*sqrt(15)/4)] - >>> e.intersection(Ellipse(Point(5, 0), 4, 3)) - [Point(2, -3*sqrt(7)/4), Point(2, 3*sqrt(7)/4)] - >>> e.intersection(Ellipse(Point(100500, 0), 4, 3)) - [] - >>> e.intersection(Ellipse(Point(0, 0), 3, 4)) - [Point(-363/175, -48*sqrt(111)/175), Point(-363/175, 48*sqrt(111)/175), - Point(3, 0)] - >>> e.intersection(Ellipse(Point(-1, 0), 3, 4)) - [Point(-17/5, -12/5), Point(-17/5, 12/5), Point(7/5, -12/5), - Point(7/5, 12/5)] - """ - if isinstance(o, Point): - if o in self: - return [o] - else: - return [] - - elif isinstance(o, LinearEntity): - # LinearEntity may be a ray/segment, so check the points - # of intersection for coincidence first - return self._do_line_intersection(o) - - elif isinstance(o, Circle): - return self._do_circle_intersection(o) - - elif isinstance(o, Ellipse): - if o == self: - return self - else: - return self._do_ellipse_intersection(o) - - return o.intersection(self) -
    - def __eq__(self, o): - """Is the other GeometryEntity the same as this ellipse?""" - return isinstance(o, GeometryEntity) and (self.center == o.center and - self.hradius == o.hradius and - self.vradius == o.vradius) - - def __hash__(self): - return super(Ellipse, self).__hash__() - - def __contains__(self, o): - if isinstance(o, Point): - x = C.Dummy('x', real=True) - y = C.Dummy('y', real=True) - - res = self.equation(x, y).subs({x: o.x, y: o.y}) - return trigsimp(simplify(res)) is S.Zero - elif isinstance(o, Ellipse): - return self == o - return False - -
    -
    [docs]class Circle(Ellipse): - """A circle in space. - - Constructed simply from a center and a radius, or from three - non-collinear points. - - Parameters - ========== - - center : Point - radius : number or sympy expression - points : sequence of three Points - - Attributes - ========== - - radius (synonymous with hradius, vradius, major and minor) - circumference - equation - - Raises - ====== - - GeometryError - When trying to construct circle from three collinear points. - When trying to construct circle from incorrect parameters. - - See Also - ======== - - Ellipse, sympy.geometry.point.Point - - Examples - ======== - - >>> from sympy.geometry import Point, Circle - >>> # a circle constructed from a center and radius - >>> c1 = Circle(Point(0, 0), 5) - >>> c1.hradius, c1.vradius, c1.radius - (5, 5, 5) - - >>> # a circle costructed from three points - >>> c2 = Circle(Point(0, 0), Point(1, 1), Point(1, 0)) - >>> c2.hradius, c2.vradius, c2.radius, c2.center - (sqrt(2)/2, sqrt(2)/2, sqrt(2)/2, Point(1/2, 1/2)) - - """ - def __new__(cls, *args, **kwargs): - c, r = None, None - if len(args) == 3: - args = [Point(a) for a in args] - if Point.is_collinear(*args): - raise GeometryError( - "Cannot construct a circle from three collinear points") - from .polygon import Triangle - t = Triangle(*args) - c = t.circumcenter - r = t.circumradius - elif len(args) == 2: - # Assume (center, radius) pair - c = Point(args[0]) - r = sympify(args[1]) - - if not (c is None or r is None): - return GeometryEntity.__new__(cls, c, r, **kwargs) - - raise GeometryError("Circle.__new__ received unknown arguments") - - @property -
    [docs] def radius(self): - """The radius of the circle. - - Returns - ======= - - radius : number or sympy expression - - See Also - ======== - - Ellipse.major, Ellipse.minor, Ellipse.hradius, Ellipse.vradius - - Examples - ======== - - >>> from sympy import Point, Circle - >>> c1 = Circle(Point(3, 4), 6) - >>> c1.radius - 6 - - """ - return self.args[1] -
    - @property -
    [docs] def vradius(self): - """ - This Ellipse property is an alias for the Circle's radius. - - Whereas hradius, major and minor can use Ellipse's conventions, - the vradius does not exist for a circle. - - Examples - ======== - - >>> from sympy import Point, Circle - >>> c1 = Circle(Point(3, 4), 6) - >>> c1.vradius - 6 - """ - return self.radius -
    - @property -
    [docs] def circumference(self): - """The circumference of the circle. - - Returns - ======= - - circumference : number or SymPy expression - - Examples - ======== - - >>> from sympy import Point, Circle - >>> c1 = Circle(Point(3, 4), 6) - >>> c1.circumference - 12*pi - - """ - return 2 * S.Pi * self.radius -
    -
    [docs] def equation(self, x='x', y='y'): - """The equation of the circle. - - Parameters - ========== - - x : str or Symbol, optional - Default value is 'x'. - y : str or Symbol, optional - Default value is 'y'. - - Returns - ======= - - equation : SymPy expression - - Examples - ======== - - >>> from sympy import Point, Circle - >>> c1 = Circle(Point(0, 0), 5) - >>> c1.equation() - x**2 + y**2 - 25 - - """ - x = _symbol(x) - y = _symbol(y) - t1 = (x - self.center.x)**2 - t2 = (y - self.center.y)**2 - return t1 + t2 - self.major**2 -
    -
    [docs] def intersection(self, o): - """The intersection of this circle with another geometrical entity. - - Parameters - ========== - - o : GeometryEntity - - Returns - ======= - - intersection : list of GeometryEntities - - Examples - ======== - - >>> from sympy import Point, Circle, Line, Ray - >>> p1, p2, p3 = Point(0, 0), Point(5, 5), Point(6, 0) - >>> p4 = Point(5, 0) - >>> c1 = Circle(p1, 5) - >>> c1.intersection(p2) - [] - >>> c1.intersection(p4) - [Point(5, 0)] - >>> c1.intersection(Ray(p1, p2)) - [Point(5*sqrt(2)/2, 5*sqrt(2)/2)] - >>> c1.intersection(Line(p2, p3)) - [] - - """ - if isinstance(o, Circle): - if o.center == self.center: - if o.radius == self.radius: - return o - return [] - dx, dy = (o.center - self.center).args - d = sqrt(simplify(dy**2 + dx**2)) - R = o.radius + self.radius - if d > R or d < abs(self.radius - o.radius): - return [] - - a = simplify((self.radius**2 - o.radius**2 + d**2) / (2*d)) - - x2 = self.center.x + (dx * a/d) - y2 = self.center.y + (dy * a/d) - - h = sqrt(simplify(self.radius**2 - a**2)) - rx = -dy * (h/d) - ry = dx * (h/d) - - xi_1 = simplify(x2 + rx) - xi_2 = simplify(x2 - rx) - yi_1 = simplify(y2 + ry) - yi_2 = simplify(y2 - ry) - - ret = [Point(xi_1, yi_1)] - if xi_1 != xi_2 or yi_1 != yi_2: - ret.append(Point(xi_2, yi_2)) - return ret - - return Ellipse.intersection(self, o) -
    -
    [docs] def scale(self, x=1, y=1, pt=None): - """Override GeometryEntity.scale since the radius - is not a GeometryEntity. - - Examples - ======== - - >>> from sympy import Circle - >>> Circle((0, 0), 1).scale(2, 2) - Circle(Point(0, 0), 2) - >>> Circle((0, 0), 1).scale(2, 4) - Ellipse(Point(0, 0), 2, 4) - """ - c = self.center - if pt: - pt = Point(pt) - return self.translate(*(-pt).args).scale(x, y).translate(*pt.args) - c = c.scale(x, y) - if x == y: - return self.func(c, x*self.radius) - h = v = self.radius - return Ellipse(c, hradius=h*x, vradius=v*y) - -
    -from .polygon import Polygon -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/geometry/entity.html b/dev-py3k/_modules/sympy/geometry/entity.html deleted file mode 100644 index c256a8e5a17..00000000000 --- a/dev-py3k/_modules/sympy/geometry/entity.html +++ /dev/null @@ -1,457 +0,0 @@ - - - - - - - - - - sympy.geometry.entity — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.geometry.entity

    -"""The definition of the base geometrical entity with attributes common to
    -all derived geometrical entities.
    -
    -Contains
    -========
    -GeometryEntity
    -
    -"""
    -
    -from sympy.core.compatibility import cmp, is_sequence
    -from sympy.core.basic import Basic
    -from sympy.core.sympify import sympify
    -from sympy.functions import cos, sin
    -from sympy.matrices import eye
    -
    -# How entities are ordered; used by __cmp__ in GeometryEntity
    -ordering_of_classes = [
    -    "Point",
    -    "Segment",
    -    "Ray",
    -    "Line",
    -    "Triangle",
    -    "RegularPolygon",
    -    "Polygon",
    -    "Circle",
    -    "Ellipse",
    -    "Curve"
    -]
    -
    -
    -
    [docs]class GeometryEntity(Basic): - """The base class for all geometrical entities. - - This class doesn't represent any particular geometric entity, it only - provides the implementation of some methods common to all subclasses. - - """ - - def __new__(cls, *args, **kwargs): - args = list(map(sympify, args)) - return Basic.__new__(cls, *args) - - def _sympy_(self): - return self - - def __getnewargs__(self): - return tuple(self.args) - -
    [docs] def intersection(self, o): - """ - Returns a list of all of the intersections of self with o. - - Notes - ===== - - An entity is not required to implement this method. - - If two different types of entities can intersect, the item with - higher index in ordering_of_classes should implement - intersections with anything having a lower index. - - See Also - ======== - - sympy.geometry.util.intersection - - """ - raise NotImplementedError() -
    -
    [docs] def rotate(self, angle, pt=None): - """Rotate ``angle`` radians counterclockwise about Point ``pt``. - - The default pt is the origin, Point(0, 0) - - See Also - ======== - - scale, translate - - Examples - ======== - - >>> from sympy import Point, RegularPolygon, Polygon, pi - >>> t = Polygon(*RegularPolygon(Point(0, 0), 1, 3).vertices) - >>> t # vertex on x axis - Triangle(Point(1, 0), Point(-1/2, sqrt(3)/2), Point(-1/2, -sqrt(3)/2)) - >>> t.rotate(pi/2) # vertex on y axis now - Triangle(Point(0, 1), Point(-sqrt(3)/2, -1/2), Point(sqrt(3)/2, -1/2)) - - """ - newargs = [] - for a in self.args: - if isinstance(a, GeometryEntity): - newargs.append(a.rotate(angle, pt)) - else: - newargs.append(a) - return type(self)(*newargs) -
    -
    [docs] def scale(self, x=1, y=1, pt=None): - """Scale the object by multiplying the x,y-coordinates by x and y. - - If pt is given, the scaling is done relative to that point; the - object is shifted by -pt, scaled, and shifted by pt. - - See Also - ======== - - rotate, translate - - Examples - ======== - - >>> from sympy import RegularPolygon, Point, Polygon - >>> t = Polygon(*RegularPolygon(Point(0, 0), 1, 3).vertices) - >>> t - Triangle(Point(1, 0), Point(-1/2, sqrt(3)/2), Point(-1/2, -sqrt(3)/2)) - >>> t.scale(2) - Triangle(Point(2, 0), Point(-1, sqrt(3)/2), Point(-1, -sqrt(3)/2)) - >>> t.scale(2,2) - Triangle(Point(2, 0), Point(-1, sqrt(3)), Point(-1, -sqrt(3))) - - """ - from sympy.geometry.point import Point - if pt: - pt = Point(pt) - return self.translate(*(-pt).args).scale(x, y).translate(*pt.args) - return type(self)(*[a.scale(x, y) for a in self.args]) # if this fails, override this class -
    -
    [docs] def translate(self, x=0, y=0): - """Shift the object by adding to the x,y-coordinates the values x and y. - - See Also - ======== - - rotate, scale - - Examples - ======== - - >>> from sympy import RegularPolygon, Point, Polygon - >>> t = Polygon(*RegularPolygon(Point(0, 0), 1, 3).vertices) - >>> t - Triangle(Point(1, 0), Point(-1/2, sqrt(3)/2), Point(-1/2, -sqrt(3)/2)) - >>> t.translate(2) - Triangle(Point(3, 0), Point(3/2, sqrt(3)/2), Point(3/2, -sqrt(3)/2)) - >>> t.translate(2, 2) - Triangle(Point(3, 2), Point(3/2, sqrt(3)/2 + 2), - Point(3/2, -sqrt(3)/2 + 2)) - - """ - newargs = [] - for a in self.args: - if isinstance(a, GeometryEntity): - newargs.append(a.translate(x, y)) - else: - newargs.append(a) - return type(self)(*newargs) -
    -
    [docs] def encloses(self, o): - """ - Return True if o is inside (not on or outside) the boundaries of self. - - The object will be decomposed into Points and individual Entities need - only define an encloses_point method for their class. - - See Also - ======== - - sympy.geometry.ellipse.Ellipse.encloses_point - sympy.geometry.polygon.Polygon.encloses_point - - Examples - ======== - - >>> from sympy import RegularPolygon, Point, Polygon - >>> t = Polygon(*RegularPolygon(Point(0, 0), 1, 3).vertices) - >>> t2 = Polygon(*RegularPolygon(Point(0, 0), 2, 3).vertices) - >>> t2.encloses(t) - True - >>> t.encloses(t2) - False - """ - from sympy.geometry.point import Point - from sympy.geometry.line import Segment, Ray, Line - from sympy.geometry.ellipse import Ellipse - from sympy.geometry.polygon import Polygon, RegularPolygon - - if isinstance(o, Point): - return self.encloses_point(o) - elif isinstance(o, Segment): - return all(self.encloses_point(x) for x in o.points) - elif isinstance(o, Ray) or isinstance(o, Line): - return False - elif isinstance(o, Ellipse): - return self.encloses_point(o.center) and not self.intersection(o) - elif isinstance(o, Polygon): - if isinstance(o, RegularPolygon): - if not self.encloses_point(o.center): - return False - return all(self.encloses_point(v) for v in o.vertices) - raise NotImplementedError() -
    -
    [docs] def is_similar(self, other): - """Is this geometrical entity similar to another geometrical entity? - - Two entities are similar if a uniform scaling (enlarging or - shrinking) of one of the entities will allow one to obtain the other. - - Notes - ===== - - This method is not intended to be used directly but rather - through the `are_similar` function found in util.py. - An entity is not required to implement this method. - If two different types of entities can be similar, it is only - required that one of them be able to determine this. - - See Also - ======== - - scale - - """ - raise NotImplementedError() -
    - def __ne__(self, o): - """Test inequality of two geometrical entities.""" - return not self.__eq__(o) - - def __radd__(self, a): - return a.__add__(self) - - def __rsub__(self, a): - return a.__sub__(self) - - def __rmul__(self, a): - return a.__mul__(self) - - def __rdiv__(self, a): - return a.__div__(self) - - def __str__(self): - """String representation of a GeometryEntity.""" - from sympy.printing import sstr - return type(self).__name__ + sstr(self.args) - - def __repr__(self): - """String representation of a GeometryEntity that can be evaluated - by sympy.""" - return type(self).__name__ + repr(self.args) - - def __cmp__(self, other): - """Comparison of two GeometryEntities.""" - n1 = self.__class__.__name__ - n2 = other.__class__.__name__ - c = cmp(n1, n2) - if not c: - return 0 - - i1 = -1 - for cls in self.__class__.__mro__: - try: - i1 = ordering_of_classes.index(cls.__name__) - break - except ValueError: - i1 = -1 - if i1 == -1: - return c - - i2 = -1 - for cls in other.__class__.__mro__: - try: - i2 = ordering_of_classes.index(cls.__name__) - break - except ValueError: - i2 = -1 - if i2 == -1: - return c - - return cmp(i1, i2) - - def __contains__(self, other): - """Subclasses should implement this method for anything more complex than equality.""" - if type(self) == type(other): - return self == other - raise NotImplementedError() - - def _eval_subs(self, old, new): - from sympy.geometry.point import Point - if is_sequence(old) or is_sequence(new): - old = Point(old) - new = Point(new) - return self._subs(old, new) - -
    -def translate(x, y): - """Return the matrix to translate a 2-D point by x and y.""" - rv = eye(3) - rv[2, 0] = x - rv[2, 1] = y - return rv - - -def scale(x, y, pt=None): - """Return the matrix to multiply a 2-D point's coordinates by x and y. - - If pt is given, the scaling is done relative to that point.""" - rv = eye(3) - rv[0, 0] = x - rv[1, 1] = y - if pt: - from sympy.geometry.point import Point - pt = Point(pt) - tr1 = translate(*(-pt).args) - tr2 = translate(*pt.args) - return tr1*rv*tr2 - return rv - - -def rotate(th): - """Return the matrix to rotate a 2-D point about the origin by ``angle``. - - The angle is measured in radians. To Point a point about a point other - then the origin, translate the Point, do the rotation, and - translate it back: - - >>> from sympy.geometry.entity import rotate, translate - >>> from sympy import Point, pi - >>> rot_about_11 = translate(-1, -1)*rotate(pi/2)*translate(1, 1) - >>> Point(1, 1).transform(rot_about_11) - Point(1, 1) - >>> Point(0, 0).transform(rot_about_11) - Point(2, 0) - """ - s = sin(th) - rv = eye(3)*cos(th) - rv[0, 1] = s - rv[1, 0] = -s - rv[2, 2] = 1 - return rv -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/geometry/line.html b/dev-py3k/_modules/sympy/geometry/line.html deleted file mode 100644 index 60a37ba5136..00000000000 --- a/dev-py3k/_modules/sympy/geometry/line.html +++ /dev/null @@ -1,1861 +0,0 @@ - - - - - - - - - - sympy.geometry.line — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.geometry.line

    -"""Line-like geometrical entities.
    -
    -Contains
    -========
    -LinearEntity
    -Line
    -Ray
    -Segment
    -
    -"""
    -from sympy.core import S, C, sympify, Dummy
    -from sympy.functions.elementary.trigonometric import _pi_coeff as pi_coeff
    -from sympy.core.logic import fuzzy_and
    -from sympy.simplify import simplify
    -from sympy.solvers import solve
    -from sympy.geometry.exceptions import GeometryError
    -from .entity import GeometryEntity
    -from .point import Point
    -from .util import _symbol
    -
    -# TODO: this should be placed elsewhere and reused in other modules
    -
    -
    -class Undecidable(ValueError):
    -    pass
    -
    -
    -
    [docs]class LinearEntity(GeometryEntity): - """An abstract base class for all linear entities (line, ray and segment) - in a 2-dimensional Euclidean space. - - Attributes - ========== - - p1 - p2 - coefficients - slope - points - - Notes - ===== - - This is an abstract class and is not meant to be instantiated. - Subclasses should implement the following methods: - - * __eq__ - * contains - - See Also - ======== - - sympy.geometry.entity.GeometryEntity - - """ - - def __new__(cls, p1, p2, **kwargs): - p1 = Point(p1) - p2 = Point(p2) - if p1 == p2: - # Rolygon returns lower priority classes...should LinearEntity, too? - return p1 # raise ValueError("%s.__new__ requires two unique Points." % cls.__name__) - - return GeometryEntity.__new__(cls, p1, p2, **kwargs) - - @property -
    [docs] def p1(self): - """The first defining point of a linear entity. - - See Also - ======== - - sympy.geometry.point.Point - - Examples - ======== - - >>> from sympy import Point, Line - >>> p1, p2 = Point(0, 0), Point(5, 3) - >>> l = Line(p1, p2) - >>> l.p1 - Point(0, 0) - - """ - return self.args[0] -
    - @property -
    [docs] def p2(self): - """The second defining point of a linear entity. - - See Also - ======== - - sympy.geometry.point.Point - - Examples - ======== - - >>> from sympy import Point, Line - >>> p1, p2 = Point(0, 0), Point(5, 3) - >>> l = Line(p1, p2) - >>> l.p2 - Point(5, 3) - - """ - return self.args[1] -
    - @property -
    [docs] def coefficients(self): - """The coefficients (`a`, `b`, `c`) for the linear equation `ax + by + c = 0`. - - See Also - ======== - - sympy.geometry.line.Line.equation - - Examples - ======== - - >>> from sympy import Point, Line - >>> from sympy.abc import x, y - >>> p1, p2 = Point(0, 0), Point(5, 3) - >>> l = Line(p1, p2) - >>> l.coefficients - (-3, 5, 0) - - >>> p3 = Point(x, y) - >>> l2 = Line(p1, p3) - >>> l2.coefficients - (-y, x, 0) - - """ - p1, p2 = self.points - if p1.x == p2.x: - return (S.One, S.Zero, -p1.x) - elif p1.y == p2.y: - return (S.Zero, S.One, -p1.y) - return tuple([simplify(i) for i in - (self.p1.y - self.p2.y, - self.p2.x - self.p1.x, - self.p1.x*self.p2.y - self.p1.y*self.p2.x)]) -
    -
    [docs] def is_concurrent(*lines): - """Is a sequence of linear entities concurrent? - - Two or more linear entities are concurrent if they all - intersect at a single point. - - Parameters - ========== - - lines : a sequence of linear entities. - - Returns - ======= - - True : if the set of linear entities are concurrent, - False : otherwise. - - Notes - ===== - - Simply take the first two lines and find their intersection. - If there is no intersection, then the first two lines were - parallel and had no intersection so concurrency is impossible - amongst the whole set. Otherwise, check to see if the - intersection point of the first two lines is a member on - the rest of the lines. If so, the lines are concurrent. - - See Also - ======== - - sympy.geometry.util.intersection - - Examples - ======== - - >>> from sympy import Point, Line - >>> p1, p2 = Point(0, 0), Point(3, 5) - >>> p3, p4 = Point(-2, -2), Point(0, 2) - >>> l1, l2, l3 = Line(p1, p2), Line(p1, p3), Line(p1, p4) - >>> l1.is_concurrent(l2, l3) - True - - >>> l4 = Line(p2, p3) - >>> l4.is_concurrent(l2, l3) - False - - """ - - # Concurrency requires intersection at a single point; One linear - # entity cannot be concurrent. - if len(lines) <= 1: - return False - - try: - # Get the intersection (if parallel) - p = lines[0].intersection(lines[1]) - if len(p) == 0: - return False - - # Make sure the intersection is on every linear entity - for line in lines[2:]: - if p[0] not in line: - return False - return True - except AttributeError: - return False -
    -
    [docs] def is_parallel(l1, l2): - """Are two linear entities parallel? - - Parameters - ========== - - l1 : LinearEntity - l2 : LinearEntity - - Returns - ======= - - True : if l1 and l2 are parallel, - False : otherwise. - - See Also - ======== - - coefficients - - Examples - ======== - - >>> from sympy import Point, Line - >>> p1, p2 = Point(0, 0), Point(1, 1) - >>> p3, p4 = Point(3, 4), Point(6, 7) - >>> l1, l2 = Line(p1, p2), Line(p3, p4) - >>> Line.is_parallel(l1, l2) - True - - >>> p5 = Point(6, 6) - >>> l3 = Line(p3, p5) - >>> Line.is_parallel(l1, l3) - False - - """ - try: - a1, b1, c1 = l1.coefficients - a2, b2, c2 = l2.coefficients - return bool(simplify(a1*b2 - b1*a2) == 0) - except AttributeError: - return False -
    -
    [docs] def is_perpendicular(l1, l2): - """Are two linear entities perpendicular? - - Parameters - ========== - - l1 : LinearEntity - l2 : LinearEntity - - Returns - ======= - - True : if l1 and l2 are perpendicular, - False : otherwise. - - See Also - ======== - - coefficients - - Examples - ======== - - >>> from sympy import Point, Line - >>> p1, p2, p3 = Point(0, 0), Point(1, 1), Point(-1, 1) - >>> l1, l2 = Line(p1, p2), Line(p1, p3) - >>> l1.is_perpendicular(l2) - True - - >>> p4 = Point(5, 3) - >>> l3 = Line(p1, p4) - >>> l1.is_perpendicular(l3) - False - - """ - try: - a1, b1, c1 = l1.coefficients - a2, b2, c2 = l2.coefficients - return bool(simplify(a1*a2 + b1*b2) == 0) - except AttributeError: - return False -
    -
    [docs] def angle_between(l1, l2): - """The angle formed between the two linear entities. - - Parameters - ========== - - l1 : LinearEntity - l2 : LinearEntity - - Returns - ======= - - angle : angle in radians - - Notes - ===== - - From the dot product of vectors v1 and v2 it is known that: - - ``dot(v1, v2) = |v1|*|v2|*cos(A)`` - - where A is the angle formed between the two vectors. We can - get the directional vectors of the two lines and readily - find the angle between the two using the above formula. - - See Also - ======== - - is_perpendicular - - Examples - ======== - - >>> from sympy import Point, Line - >>> p1, p2, p3 = Point(0, 0), Point(0, 4), Point(2, 0) - >>> l1, l2 = Line(p1, p2), Line(p1, p3) - >>> l1.angle_between(l2) - pi/2 - - """ - v1 = l1.p2 - l1.p1 - v2 = l2.p2 - l2.p1 - return C.acos(v1.dot(v2)/(abs(v1)*abs(v2))) -
    -
    [docs] def parallel_line(self, p): - """Create a new Line parallel to this linear entity which passes - through the point `p`. - - Parameters - ========== - - p : Point - - Returns - ======= - - line : Line - - See Also - ======== - - is_parallel - - Examples - ======== - - >>> from sympy import Point, Line - >>> p1, p2, p3 = Point(0, 0), Point(2, 3), Point(-2, 2) - >>> l1 = Line(p1, p2) - >>> l2 = l1.parallel_line(p3) - >>> p3 in l2 - True - >>> l1.is_parallel(l2) - True - - """ - d = self.p1 - self.p2 - return Line(p, p + d) -
    -
    [docs] def perpendicular_line(self, p): - """Create a new Line perpendicular to this linear entity which passes - through the point `p`. - - Parameters - ========== - - p : Point - - Returns - ======= - - line : Line - - See Also - ======== - - is_perpendicular, perpendicular_segment - - Examples - ======== - - >>> from sympy import Point, Line - >>> p1, p2, p3 = Point(0, 0), Point(2, 3), Point(-2, 2) - >>> l1 = Line(p1, p2) - >>> l2 = l1.perpendicular_line(p3) - >>> p3 in l2 - True - >>> l1.is_perpendicular(l2) - True - - """ - d1, d2 = (self.p1 - self.p2).args - if d2 == 0: # If a horizontal line - if p.y == self.p1.y: # if p is on this linear entity - return Line(p, p + Point(0, 1)) - else: - p2 = Point(p.x, self.p1.y) - return Line(p, p2) - else: - p2 = Point(p.x - d2, p.y + d1) - return Line(p, p2) -
    -
    [docs] def perpendicular_segment(self, p): - """Create a perpendicular line segment from `p` to this line. - - The enpoints of the segment are ``p`` and the closest point in - the line containing self. (If self is not a line, the point might - not be in self.) - - Parameters - ========== - - p : Point - - Returns - ======= - - segment : Segment - - Notes - ===== - - Returns `p` itself if `p` is on this linear entity. - - See Also - ======== - - perpendicular_line - - Examples - ======== - - >>> from sympy import Point, Line - >>> p1, p2, p3 = Point(0, 0), Point(1, 1), Point(0, 2) - >>> l1 = Line(p1, p2) - >>> s1 = l1.perpendicular_segment(p3) - >>> l1.is_perpendicular(s1) - True - >>> p3 in s1 - True - >>> l1.perpendicular_segment(Point(4, 0)) - Segment(Point(2, 2), Point(4, 0)) - - """ - if p in self: - return p - pl = self.perpendicular_line(p) - p2 = Line(self).intersection(pl)[0] - return Segment(p, p2) -
    - @property -
    [docs] def length(self): - """ - The length of the line. - - Examples - ======== - - >>> from sympy import Point, Line - >>> p1, p2 = Point(0, 0), Point(3, 5) - >>> l1 = Line(p1, p2) - >>> l1.length - oo - """ - return S.Infinity -
    - @property -
    [docs] def slope(self): - """The slope of this linear entity, or infinity if vertical. - - Returns - ======= - - slope : number or sympy expression - - See Also - ======== - - coefficients - - Examples - ======== - - >>> from sympy import Point, Line - >>> p1, p2 = Point(0, 0), Point(3, 5) - >>> l1 = Line(p1, p2) - >>> l1.slope - 5/3 - - >>> p3 = Point(0, 4) - >>> l2 = Line(p1, p3) - >>> l2.slope - oo - - """ - d1, d2 = (self.p1 - self.p2).args - if d1 == 0: - return S.Infinity - return simplify(d2/d1) -
    - @property -
    [docs] def points(self): - """The two points used to define this linear entity. - - Returns - ======= - - points : tuple of Points - - See Also - ======== - - sympy.geometry.point.Point - - Examples - ======== - - >>> from sympy import Point, Line - >>> p1, p2 = Point(0, 0), Point(5, 11) - >>> l1 = Line(p1, p2) - >>> l1.points - (Point(0, 0), Point(5, 11)) - - """ - return (self.p1, self.p2) -
    -
    [docs] def projection(self, o): - """Project a point, line, ray, or segment onto this linear entity. - - Parameters - ========== - - other : Point or LinearEntity (Line, Ray, Segment) - - Returns - ======= - - projection : Point or LinearEntity (Line, Ray, Segment) - The return type matches the type of the parameter ``other``. - - Raises - ====== - - GeometryError - When method is unable to perform projection. - - Notes - ===== - - A projection involves taking the two points that define - the linear entity and projecting those points onto a - Line and then reforming the linear entity using these - projections. - A point P is projected onto a line L by finding the point - on L that is closest to P. This is done by creating a - perpendicular line through P and L and finding its - intersection with L. - - See Also - ======== - - sympy.geometry.point.Point, perpendicular_line - - Examples - ======== - - >>> from sympy import Point, Line, Segment, Rational - >>> p1, p2, p3 = Point(0, 0), Point(1, 1), Point(Rational(1, 2), 0) - >>> l1 = Line(p1, p2) - >>> l1.projection(p3) - Point(1/4, 1/4) - - >>> p4, p5 = Point(10, 0), Point(12, 1) - >>> s1 = Segment(p4, p5) - >>> l1.projection(s1) - Segment(Point(5, 5), Point(13/2, 13/2)) - - """ - tline = Line(self.p1, self.p2) - - def _project(p): - """Project a point onto the line representing self.""" - if p in tline: - return p - l1 = tline.perpendicular_line(p) - return tline.intersection(l1)[0] - - projected = None - if isinstance(o, Point): - return _project(o) - elif isinstance(o, LinearEntity): - n_p1 = _project(o.p1) - n_p2 = _project(o.p2) - if n_p1 == n_p2: - projected = n_p1 - else: - projected = o.__class__(n_p1, n_p2) - - # Didn't know how to project so raise an error - if projected is None: - n1 = self.__class__.__name__ - n2 = o.__class__.__name__ - raise GeometryError( - "Do not know how to project %s onto %s" % (n2, n1)) - - return self.intersection(projected)[0] -
    -
    [docs] def intersection(self, o): - """The intersection with another geometrical entity. - - Parameters - ========== - - o : Point or LinearEntity - - Returns - ======= - - intersection : list of geometrical entities - - See Also - ======== - - sympy.geometry.point.Point - - Examples - ======== - - >>> from sympy import Point, Line, Segment - >>> p1, p2, p3 = Point(0, 0), Point(1, 1), Point(7, 7) - >>> l1 = Line(p1, p2) - >>> l1.intersection(p3) - [Point(7, 7)] - - >>> p4, p5 = Point(5, 0), Point(0, 3) - >>> l2 = Line(p4, p5) - >>> l1.intersection(l2) - [Point(15/8, 15/8)] - - >>> p6, p7 = Point(0, 5), Point(2, 6) - >>> s1 = Segment(p6, p7) - >>> l1.intersection(s1) - [] - - """ - if isinstance(o, Point): - if o in self: - return [o] - else: - return [] - elif isinstance(o, LinearEntity): - a1, b1, c1 = self.coefficients - a2, b2, c2 = o.coefficients - t = simplify(a1*b2 - a2*b1) - if t.equals(0) is not False: # assume they are parallel - if isinstance(self, Line): - if o.p1 in self: - return [o] - return [] - elif isinstance(o, Line): - if self.p1 in o: - return [self] - return [] - elif isinstance(self, Ray): - if isinstance(o, Ray): - # case 1, rays in the same direction - if self.xdirection == o.xdirection: - if self.source.x < o.source.x: - return [o] - return [self] - # case 2, rays in the opposite directions - else: - if o.source in self: - if self.source == o.source: - return [self.source] - return [Segment(o.source, self.source)] - return [] - elif isinstance(o, Segment): - if o.p1 in self: - if o.p2 in self: - return [o] - return [Segment(o.p1, self.source)] - elif o.p2 in self: - return [Segment(o.p2, self.source)] - return [] - elif isinstance(self, Segment): - if isinstance(o, Ray): - return o.intersection(self) - elif isinstance(o, Segment): - # A reminder that the points of Segments are ordered - # in such a way that the following works. See - # Segment.__new__ for details on the ordering. - if self.p1 not in o: - if self.p2 not in o: - # Neither of the endpoints are in o so either - # o is contained in this segment or it isn't - if o in self: - return [self] - return [] - else: - # p1 not in o but p2 is. Either there is a - # segment as an intersection, or they only - # intersect at an endpoint - if self.p2 == o.p1: - return [o.p1] - return [Segment(o.p1, self.p2)] - elif self.p2 not in o: - # p2 not in o but p1 is. Either there is a - # segment as an intersection, or they only - # intersect at an endpoint - if self.p1 == o.p2: - return [o.p2] - return [Segment(o.p2, self.p1)] - - # Both points of self in o so the whole segment - # is in o - return [self] - - # Unknown linear entity - return [] - - # Not parallel, so find the point of intersection - px = simplify((b1*c2 - c1*b2) / t) - py = simplify((a2*c1 - a1*c2) / t) - inter = Point(px, py) - # we do not use a simplistic 'inter in self and inter in o' - # because that requires an equality test that is fragile; - # instead we employ some diagnostics to see if the intersection - # is valid - - def inseg(self): - def _between(a, b, c): - return c >= a and c <= b or c <= a and c >= b - if _between(self.p1.x, self.p2.x, inter.x) and \ - _between(self.p1.y, self.p2.y, inter.y): - return True - - def inray(self): - sray = Ray(self.p1, inter) - if sray.xdirection == self.xdirection and \ - sray.ydirection == self.ydirection: - return True - for i in range(2): - if isinstance(self, Line): - if isinstance(o, Line): - return [inter] - elif isinstance(o, Ray) and inray(o): - return [inter] - elif isinstance(o, Segment) and inseg(o): - return [inter] - elif isinstance(self, Ray) and inray(self): - if isinstance(o, Ray) and inray(o): - return [inter] - elif isinstance(o, Segment) and inseg(o): - return [inter] - elif isinstance(self, Segment) and inseg(self): - if isinstance(o, Segment) and inseg(o): - return [inter] - self, o = o, self - return [] - - return o.intersection(self) -
    -
    [docs] def random_point(self): - """A random point on a LinearEntity. - - Returns - ======= - - point : Point - - See Also - ======== - - sympy.geometry.point.Point - - Examples - ======== - - >>> from sympy import Point, Line - >>> p1, p2 = Point(0, 0), Point(5, 3) - >>> l1 = Line(p1, p2) - >>> p3 = l1.random_point() - >>> # random point - don't know its coords in advance - >>> p3 # doctest: +ELLIPSIS - Point(...) - >>> # point should belong to the line - >>> p3 in l1 - True - - """ - from random import randint - - # The lower and upper - lower, upper = -2**32 - 1, 2**32 - - if self.slope is S.Infinity: - if isinstance(self, Ray): - if self.ydirection is S.Infinity: - lower = self.p1.y - else: - upper = self.p1.y - elif isinstance(self, Segment): - lower = self.p1.y - upper = self.p2.y - - x = self.p1.x - y = randint(lower, upper) - else: - if isinstance(self, Ray): - if self.xdirection is S.Infinity: - lower = self.p1.x - else: - upper = self.p1.x - elif isinstance(self, Segment): - lower = self.p1.x - upper = self.p2.x - - a, b, c = self.coefficients - x = randint(lower, upper) - y = simplify((-c - a*x) / b) - return Point(x, y) -
    -
    [docs] def is_similar(self, other): - """ - Return True if self and other are contained in the same line. - - Examples - ======== - - >>> from sympy import Point, Line - >>> p1, p2, p3 = Point(0, 1), Point(3, 4), Point(2, 3) - >>> l1 = Line(p1, p2) - >>> l2 = Line(p1, p3) - >>> l1.is_similar(l2) - True - """ - def _norm(a, b, c): - if a != 0: - return 1, b/a, c/a - elif b != 0: - return a/b, 1, c/b - else: - return c - return _norm(*self.coefficients) == _norm(*other.coefficients) -
    - def __contains__(self, other): - """Return a definitive answer or else raise an error if it cannot - be determined that other is on the boundaries of self.""" - result = self.contains(other) - - if result is not None: - return result - else: - raise Undecidable( - "can't decide whether '%s' contains '%s'" % (self, other)) - -
    [docs] def contains(self, other): - """Subclasses should implement this method and should return - True if other is on the boundaries of self; - False if not on the boundaries of self; - None if a determination cannot be made.""" - raise NotImplementedError() -
    - def __eq__(self, other): - """Subclasses should implement this method.""" - raise NotImplementedError() - - def __hash__(self): - return super(LinearEntity, self).__hash__() - -
    -
    [docs]class Line(LinearEntity): - """An infinite line in space. - - A line is declared with two distinct points or a point and slope - as defined using keyword `slope`. - - Notes - ===== - - At the moment only lines in a 2D space can be declared, because - Points can be defined only for 2D spaces. - - Parameters - ========== - - p1 : Point - pt : Point - slope : sympy expression - - See Also - ======== - - sympy.geometry.point.Point - - Examples - ======== - - >>> import sympy - >>> from sympy import Point - >>> from sympy.abc import L - >>> from sympy.geometry import Line, Segment - >>> L = Line(Point(2,3), Point(3,5)) - >>> L - Line(Point(2, 3), Point(3, 5)) - >>> L.points - (Point(2, 3), Point(3, 5)) - >>> L.equation() - -2*x + y + 1 - >>> L.coefficients - (-2, 1, 1) - - Instantiate with keyword ``slope``: - - >>> Line(Point(0, 0), slope=0) - Line(Point(0, 0), Point(1, 0)) - - Instantiate with another linear object - - >>> s = Segment((0, 0), (0, 1)) - >>> Line(s).equation() - x - """ - - def __new__(cls, p1, pt=None, slope=None, **kwargs): - if isinstance(p1, LinearEntity): - p1, pt = p1.args - else: - p1 = Point(p1) - if pt is not None and slope is None: - try: - p2 = Point(pt) - except NotImplementedError: - raise ValueError('The 2nd argument was not a valid Point; if it was meant to be a slope it should be given with keyword "slope".') - if p1 == p2: - raise ValueError('A line requires two distinct points.') - elif slope is not None and pt is None: - slope = sympify(slope) - if slope.is_bounded is False: - # when unbounded slope, don't change x - p2 = p1 + Point(0, 1) - else: - # go over 1 up slope - p2 = p1 + Point(1, slope) - else: - raise ValueError('A 2nd Point or keyword "slope" must be used.') - - return LinearEntity.__new__(cls, p1, p2, **kwargs) - -
    [docs] def arbitrary_point(self, parameter='t'): - """A parameterized point on the Line. - - Parameters - ========== - - parameter : str, optional - The name of the parameter which will be used for the parametric - point. The default value is 't'. - - Returns - ======= - - point : Point - - Raises - ====== - - ValueError - When ``parameter`` already appears in the Line's definition. - - See Also - ======== - - sympy.geometry.point.Point - - Examples - ======== - - >>> from sympy import Point, Line - >>> p1, p2 = Point(1, 0), Point(5, 3) - >>> l1 = Line(p1, p2) - >>> l1.arbitrary_point() - Point(4*t + 1, 3*t) - - """ - t = _symbol(parameter) - if t.name in (f.name for f in self.free_symbols): - raise ValueError('Symbol %s already appears in object and cannot be used as a parameter.' % t.name) - x = simplify(self.p1.x + t*(self.p2.x - self.p1.x)) - y = simplify(self.p1.y + t*(self.p2.y - self.p1.y)) - return Point(x, y) -
    -
    [docs] def plot_interval(self, parameter='t'): - """The plot interval for the default geometric plot of line. - - Parameters - ========== - - parameter : str, optional - Default value is 't'. - - Returns - ======= - - plot_interval : list (plot interval) - [parameter, lower_bound, upper_bound] - - Examples - ======== - - >>> from sympy import Point, Line - >>> p1, p2 = Point(0, 0), Point(5, 3) - >>> l1 = Line(p1, p2) - >>> l1.plot_interval() - [t, -5, 5] - - """ - t = _symbol(parameter) - return [t, -5, 5] -
    -
    [docs] def equation(self, x='x', y='y'): - """The equation of the line: ax + by + c. - - Parameters - ========== - - x : str, optional - The name to use for the x-axis, default value is 'x'. - y : str, optional - The name to use for the y-axis, default value is 'y'. - - Returns - ======= - - equation : sympy expression - - See Also - ======== - - LinearEntity.coefficients - - Examples - ======== - - >>> from sympy import Point, Line - >>> p1, p2 = Point(1, 0), Point(5, 3) - >>> l1 = Line(p1, p2) - >>> l1.equation() - -3*x + 4*y + 3 - - """ - x, y = _symbol(x), _symbol(y) - p1, p2 = self.points - if p1.x == p2.x: - return x - p1.x - elif p1.y == p2.y: - return y - p1.y - - a, b, c = self.coefficients - return simplify(a*x + b*y + c) -
    -
    [docs] def contains(self, o): - """Return True if o is on this Line, or False otherwise.""" - if isinstance(o, Point): - x, y = Dummy(), Dummy() - eq = self.equation(x, y) - if not eq.has(y): - return (solve(eq, x)[0] - o.x).equals(0) - if not eq.has(x): - return (solve(eq, y)[0] - o.y).equals(0) - return (solve(eq.subs(x, o.x), y)[0] - o.y).equals(0) - elif not isinstance(o, LinearEntity): - return False - elif isinstance(o, Line): - return self.__eq__(o) - elif not self.is_similar(o): - return False - else: - return o.p1 in self and o.p2 in self -
    - def __eq__(self, other): - """Return True if other is equal to this Line, or False otherwise.""" - if not isinstance(other, Line): - return False - return Point.is_collinear(self.p1, self.p2, other.p1, other.p2) - - def __hash__(self): - return super(Line, self).__hash__() - -
    -
    [docs]class Ray(LinearEntity): - """ - A Ray is a semi-line in the space with a source point and a direction. - - Parameters - ========== - - p1 : Point - The source of the Ray - p2 : Point or radian value - This point determines the direction in which the Ray propagates. - If given as an angle it is interpreted in radians with the positive - direction being ccw. - - Attributes - ========== - - source - xdirection - ydirection - - See Also - ======== - - sympy.geometry.point.Point, Line - - Notes - ===== - - At the moment only rays in a 2D space can be declared, because - Points can be defined only for 2D spaces. - - Examples - ======== - - >>> import sympy - >>> from sympy import Point, pi - >>> from sympy.abc import r - >>> from sympy.geometry import Ray - >>> r = Ray(Point(2, 3), Point(3, 5)) - >>> r = Ray(Point(2, 3), Point(3, 5)) - >>> r - Ray(Point(2, 3), Point(3, 5)) - >>> r.points - (Point(2, 3), Point(3, 5)) - >>> r.source - Point(2, 3) - >>> r.xdirection - oo - >>> r.ydirection - oo - >>> r.slope - 2 - >>> Ray(Point(0, 0), angle=pi/4).slope - 1 - - """ - - def __new__(cls, p1, pt=None, angle=None, **kwargs): - p1 = Point(p1) - if pt is not None and angle is None: - try: - p2 = Point(pt) - except NotImplementedError: - raise ValueError('The 2nd argument was not a valid Point;\nif it was meant to be an angle it should be given with keyword "angle".') - if p1 == p2: - raise ValueError('A Ray requires two distinct points.') - elif angle is not None and pt is None: - # we need to know if the angle is an odd multiple of pi/2 - c = pi_coeff(sympify(angle)) - p2 = None - if c is not None: - if c.is_Rational: - if c.q == 2: - if c.p == 1: - p2 = p1 + Point(0, 1) - elif c.p == 3: - p2 = p1 + Point(0, -1) - elif c.q == 1: - if c.p == 0: - p2 = p1 + Point(1, 0) - elif c.p == 1: - p2 = p1 + Point(-1, 0) - if p2 is None: - c *= S.Pi - else: - c = angle - if not p2: - p2 = p1 + Point(1, C.tan(c)) - else: - raise ValueError('A 2nd point or keyword "angle" must be used.') - - return LinearEntity.__new__(cls, p1, p2, **kwargs) - - @property -
    [docs] def source(self): - """The point from which the ray emanates. - - See Also - ======== - - sympy.geometry.point.Point - - Examples - ======== - - >>> from sympy import Point, Ray - >>> p1, p2 = Point(0, 0), Point(4, 1) - >>> r1 = Ray(p1, p2) - >>> r1.source - Point(0, 0) - - """ - return self.p1 -
    - @property -
    [docs] def xdirection(self): - """The x direction of the ray. - - Positive infinity if the ray points in the positive x direction, - negative infinity if the ray points in the negative x direction, - or 0 if the ray is vertical. - - See Also - ======== - - ydirection - - Examples - ======== - - >>> from sympy import Point, Ray - >>> p1, p2, p3 = Point(0, 0), Point(1, 1), Point(0, -1) - >>> r1, r2 = Ray(p1, p2), Ray(p1, p3) - >>> r1.xdirection - oo - >>> r2.xdirection - 0 - - """ - if self.p1.x < self.p2.x: - return S.Infinity - elif self.p1.x == self.p2.x: - return S.Zero - else: - return S.NegativeInfinity -
    - @property -
    [docs] def ydirection(self): - """The y direction of the ray. - - Positive infinity if the ray points in the positive y direction, - negative infinity if the ray points in the negative y direction, - or 0 if the ray is horizontal. - - See Also - ======== - - xdirection - - Examples - ======== - - >>> from sympy import Point, Ray - >>> p1, p2, p3 = Point(0, 0), Point(-1, -1), Point(-1, 0) - >>> r1, r2 = Ray(p1, p2), Ray(p1, p3) - >>> r1.ydirection - -oo - >>> r2.ydirection - 0 - - """ - if self.p1.y < self.p2.y: - return S.Infinity - elif self.p1.y == self.p2.y: - return S.Zero - else: - return S.NegativeInfinity -
    -
    [docs] def arbitrary_point(self, parameter='t'): - """A parameterized point on the Ray. - - Parameters - ========== - - parameter : str, optional - The name of the parameter which will be used for the parametric - point. The default value is 't'. - - Returns - ======= - - point : Point - - Raises - ====== - - ValueError - When ``parameter`` already appears in the Ray's definition. - - See Also - ======== - - sympy.geometry.point.Point - - Examples - ======== - - >>> from sympy import Ray, Point, Segment, S, simplify, solve - >>> from sympy.abc import t - >>> r = Ray(Point(0, 0), Point(2, 3)) - - >>> p = r.arbitrary_point(t) - - The parameter `t` used in the arbitrary point maps 0 to the - origin of the ray and 1 to the end of the ray at infinity - (which will show up as NaN). - - >>> p.subs(t, 0), p.subs(t, 1) - (Point(0, 0), Point(oo, oo)) - - The unit that `t` moves you is based on the spacing of the - points used to define the ray. - - >>> p.subs(t, 1/(S(1) + 1)) # one unit - Point(2, 3) - >>> p.subs(t, 2/(S(1) + 2)) # two units out - Point(4, 6) - >>> p.subs(t, S.Half/(S(1) + S.Half)) # half a unit out - Point(1, 3/2) - - If you want to be located a distance of 1 from the origin of the - ray, what value of `t` is needed? - - a) Find the unit length and pick `t` accordingly. - - >>> u = Segment(r.p1, p.subs(t, S.Half)).length # S.Half = 1/(1 + 1) - >>> want = 1 - >>> t_need = want/u - >>> p_want = p.subs(t, t_need/(1 + t_need)) - >>> simplify(Segment(r.p1, p_want).length) - 1 - - b) Find the `t` that makes the length from origin to `p` equal to 1. - - >>> l = Segment(r.p1, p).length - >>> t_need = solve(l**2 - want**2, t) # use the square to remove abs() if it is there - >>> t_need = [w for w in t_need if w.n() > 0][0] # take positive t - >>> p_want = p.subs(t, t_need) - >>> simplify(Segment(r.p1, p_want).length) - 1 - - """ - t = _symbol(parameter) - if t.name in (f.name for f in self.free_symbols): - raise ValueError('Symbol %s already appears in object and cannot be used as a parameter.' % t.name) - x = simplify(self.p1.x + t/(1 - t)*(self.p2.x - self.p1.x)) - y = simplify(self.p1.y + t/(1 - t)*(self.p2.y - self.p1.y)) - return Point(x, y) -
    -
    [docs] def plot_interval(self, parameter='t'): - """The plot interval for the default geometric plot of the Ray. - - Parameters - ========== - - parameter : str, optional - Default value is 't'. - - Returns - ======= - - plot_interval : list - [parameter, lower_bound, upper_bound] - - Examples - ======== - - >>> from sympy import Point, Ray, pi - >>> r = Ray((0, 0), angle=pi/4) - >>> r.plot_interval() - [t, 0, 5*sqrt(2)/(1 + 5*sqrt(2))] - - """ - t = _symbol(parameter) - p = self.arbitrary_point(t) - # get a t corresponding to length of 10 - want = 10 - u = Segment(self.p1, p.subs(t, S.Half)).length # gives unit length - t_need = want/u - return [t, 0, t_need/(1 + t_need)] -
    - def __eq__(self, other): - """Is the other GeometryEntity equal to this Ray?""" - if not isinstance(other, Ray): - return False - return (self.source == other.source) and (other.p2 in self) - - def __hash__(self): - return super(Ray, self).__hash__() - -
    [docs] def contains(self, o): - """Is other GeometryEntity contained in this Ray?""" - if isinstance(o, Ray): - return (Point.is_collinear(self.p1, self.p2, o.p1, o.p2) and - self.xdirection == o.xdirection and - self.ydirection == o.ydirection) - elif isinstance(o, Segment): - return o.p1 in self and o.p2 in self - elif isinstance(o, Point): - if Point.is_collinear(self.p1, self.p2, o): - if self.xdirection is S.Infinity: - rv = o.x >= self.source.x - elif self.xdirection is S.NegativeInfinity: - rv = o.x <= self.source.x - elif self.ydirection is S.Infinity: - rv = o.y >= self.source.y - else: - rv = o.y <= self.source.y - if isinstance(rv, bool): - return rv - raise Undecidable( - 'Cannot determine if %s is in %s' % (o, self)) - else: - # Points are not collinear, so the rays are not parallel - # and hence it is impossible for self to contain o - return False - - # No other known entity can be contained in a Ray - return False - -
    -
    [docs]class Segment(LinearEntity): - """An undirected line segment in space. - - Parameters - ========== - - p1 : Point - p2 : Point - - Attributes - ========== - - length : number or sympy expression - midpoint : Point - - See Also - ======== - - sympy.geometry.point.Point, Line - - Notes - ===== - - At the moment only segments in a 2D space can be declared, because - Points can be defined only for 2D spaces. - - Examples - ======== - - >>> import sympy - >>> from sympy import Point - >>> from sympy.abc import s - >>> from sympy.geometry import Segment - >>> Segment((1, 0), (1, 1)) # tuples are interpreted as pts - Segment(Point(1, 0), Point(1, 1)) - >>> s = Segment(Point(4, 3), Point(1, 1)) - >>> s - Segment(Point(1, 1), Point(4, 3)) - >>> s.points - (Point(1, 1), Point(4, 3)) - >>> s.slope - 2/3 - >>> s.length - sqrt(13) - >>> s.midpoint - Point(5/2, 2) - - """ - - def __new__(cls, p1, p2, **kwargs): - # Reorder the two points under the following ordering: - # if p1.x != p2.x then p1.x < p2.x - # if p1.x == p2.x then p1.y < p2.y - p1 = Point(p1) - p2 = Point(p2) - if p1 == p2: - return Point(p1) - if p1.x > p2.x: - p1, p2 = p2, p1 - elif p1.x == p2.x and p1.y > p2.y: - p1, p2 = p2, p1 - return LinearEntity.__new__(cls, p1, p2, **kwargs) - -
    [docs] def arbitrary_point(self, parameter='t'): - """A parameterized point on the Segment. - - Parameters - ========== - - parameter : str, optional - The name of the parameter which will be used for the parametric - point. The default value is 't'. - - Returns - ======= - - point : Point - - - Parameters - ========== - - parameter : str, optional - The name of the parameter which will be used for the parametric - point. The default value is 't'. - - Returns - ======= - - point : Point - - Raises - ====== - - ValueError - When ``parameter`` already appears in the Segment's definition. - - See Also - ======== - - sympy.geometry.point.Point - - Examples - ======== - - >>> from sympy import Point, Segment - >>> p1, p2 = Point(1, 0), Point(5, 3) - >>> s1 = Segment(p1, p2) - >>> s1.arbitrary_point() - Point(4*t + 1, 3*t) - - """ - t = _symbol(parameter) - if t.name in (f.name for f in self.free_symbols): - raise ValueError('Symbol %s already appears in object and cannot be used as a parameter.' % t.name) - x = simplify(self.p1.x + t*(self.p2.x - self.p1.x)) - y = simplify(self.p1.y + t*(self.p2.y - self.p1.y)) - return Point(x, y) -
    -
    [docs] def plot_interval(self, parameter='t'): - """The plot interval for the default geometric plot of the Segment. - - Parameters - ========== - - parameter : str, optional - Default value is 't'. - - Returns - ======= - - plot_interval : list - [parameter, lower_bound, upper_bound] - - Examples - ======== - - >>> from sympy import Point, Segment - >>> p1, p2 = Point(0, 0), Point(5, 3) - >>> s1 = Segment(p1, p2) - >>> s1.plot_interval() - [t, 0, 1] - - """ - t = _symbol(parameter) - return [t, 0, 1] -
    -
    [docs] def perpendicular_bisector(self, p=None): - """The perpendicular bisector of this segment. - - If no point is specified or the point specified is not on the - bisector then the bisector is returned as a Line. Otherwise a - Segment is returned that joins the point specified and the - intersection of the bisector and the segment. - - Parameters - ========== - - p : Point - - Returns - ======= - - bisector : Line or Segment - - See Also - ======== - - LinearEntity.perpendicular_segment - - Examples - ======== - - >>> from sympy import Point, Segment - >>> p1, p2, p3 = Point(0, 0), Point(6, 6), Point(5, 1) - >>> s1 = Segment(p1, p2) - >>> s1.perpendicular_bisector() - Line(Point(3, 3), Point(9, -3)) - - >>> s1.perpendicular_bisector(p3) - Segment(Point(3, 3), Point(5, 1)) - - """ - l = LinearEntity.perpendicular_line(self, self.midpoint) - if p is None or p not in l: - return l - else: - return Segment(self.midpoint, p) -
    - @property -
    [docs] def length(self): - """The length of the line segment. - - See Also - ======== - - sympy.geometry.point.Point.distance - - Examples - ======== - - >>> from sympy import Point, Segment - >>> p1, p2 = Point(0, 0), Point(4, 3) - >>> s1 = Segment(p1, p2) - >>> s1.length - 5 - - """ - return Point.distance(self.p1, self.p2) -
    - @property -
    [docs] def midpoint(self): - """The midpoint of the line segment. - - See Also - ======== - - sympy.geometry.point.Point.midpoint - - Examples - ======== - - >>> from sympy import Point, Segment - >>> p1, p2 = Point(0, 0), Point(4, 3) - >>> s1 = Segment(p1, p2) - >>> s1.midpoint - Point(2, 3/2) - - """ - return Point.midpoint(self.p1, self.p2) -
    -
    [docs] def distance(self, o): - """ - Finds the shortest distance between a line segment and a point. - - Raises - ====== - - NotImplementedError is raised if o is not a Point - - Examples - ======== - - >>> from sympy import Point, Segment - >>> p1, p2 = Point(0, 1), Point(3, 4) - >>> s = Segment(p1, p2) - >>> s.distance(Point(10, 15)) - sqrt(170) - """ - if isinstance(o, Point): - return self._do_point_distance(o) - raise NotImplementedError() -
    - def _do_point_distance(self, pt): - """Calculates the distance between a point and a line segment.""" - - seg_vector = self.p2 - self.p1 - pt_vector = pt - self.p1 - t = seg_vector.dot(pt_vector)/self.length**2 - if t >= 1: - distance = Point.distance(self.p2, pt) - elif t <= 0: - distance = Point.distance(self.p1, pt) - else: - distance = Point.distance( - self.p1 + Point(t*seg_vector.x, t*seg_vector.y), pt) - return distance - - def __eq__(self, other): - """Is the other GeometryEntity equal to this Ray?""" - if not isinstance(other, Segment): - return False - return (self.p1 == other.p1) and (self.p2 == other.p2) - - def __hash__(self): - return super(Segment, self).__hash__() - -
    [docs] def contains(self, other): - """ - Is the other GeometryEntity contained within this Segment? - - Examples - ======== - - >>> from sympy import Point, Segment - >>> p1, p2 = Point(0, 1), Point(3, 4) - >>> s = Segment(p1, p2) - >>> s2 = Segment(p2, p1) - >>> s.contains(s2) - True - """ - if isinstance(other, Segment): - return other.p1 in self and other.p2 in self - elif isinstance(other, Point): - if Point.is_collinear(self.p1, self.p2, other): - t = Dummy('t') - x, y = self.arbitrary_point(t).args - if self.p1.x != self.p2.x: - ti = solve(x - other.x, t)[0] - else: - ti = solve(y - other.y, t)[0] - if ti.is_number: - return 0 <= ti <= 1 - return None - - # No other known entity can be contained in a Ray - return False
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/geometry/point.html b/dev-py3k/_modules/sympy/geometry/point.html deleted file mode 100644 index e685a119318..00000000000 --- a/dev-py3k/_modules/sympy/geometry/point.html +++ /dev/null @@ -1,718 +0,0 @@ - - - - - - - - - - sympy.geometry.point — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.geometry.point

    -"""Geometrical Points.
    -
    -Contains
    -========
    -Point
    -
    -"""
    -
    -from sympy.core import S, sympify
    -from sympy.core.compatibility import iterable
    -from sympy.core.containers import Tuple
    -from sympy.simplify import simplify
    -from sympy.geometry.exceptions import GeometryError
    -from sympy.functions.elementary.miscellaneous import sqrt
    -from .entity import GeometryEntity
    -from sympy.matrices import Matrix
    -from sympy.core.numbers import Float
    -from sympy.simplify.simplify import nsimplify
    -
    -
    -
    [docs]class Point(GeometryEntity): - """A point in a 2-dimensional Euclidean space. - - Parameters - ========== - - coords : sequence of 2 coordinate values. - - Attributes - ========== - - x - y - length - - Raises - ====== - - NotImplementedError - When trying to create a point with more than two 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 points are supported. - - See Also - ======== - - sympy.geometry.line.Segment : Connects two Points - - Examples - ======== - - >>> from sympy.geometry import Point - >>> from sympy.abc import x - >>> Point(1, 2) - Point(1, 2) - >>> Point([1, 2]) - Point(1, 2) - >>> Point(0, x) - Point(0, x) - - Floats are automatically converted to Rational unless the - evaluate flag is False: - - >>> Point(0.5, 0.25) - Point(1/2, 1/4) - >>> Point(0.5, 0.25, evaluate=False) - Point(0.5, 0.25) - - """ - - def __new__(cls, *args, **kwargs): - if iterable(args[0]): - coords = Tuple(*args[0]) - elif isinstance(args[0], Point): - coords = args[0].args - else: - coords = Tuple(*args) - - if len(coords) != 2: - raise NotImplementedError( - "Only two dimensional points currently supported") - if kwargs.get('evaluate', True): - coords = [nsimplify(c) for c in coords] - - return GeometryEntity.__new__(cls, *coords) - - def __hash__(self): - return super(Point, self).__hash__() - - def __eq__(self, other): - ts, to = type(self), type(other) - if ts is not to: - return False - return self.args == other.args - - def __lt__(self, other): - return self.args < other.args - - def __contains__(self, item): - return item == self - - @property -
    [docs] def x(self): - """ - Returns the X coordinate of the Point. - - Examples - ======== - - >>> from sympy import Point - >>> p = Point(0, 1) - >>> p.x - 0 - """ - return self.args[0] -
    - @property -
    [docs] def y(self): - """ - Returns the Y coordinate of the Point. - - Examples - ======== - - >>> from sympy import Point - >>> p = Point(0, 1) - >>> p.y - 1 - """ - return self.args[1] -
    - @property -
    [docs] def length(self): - """ - Treating a Point as a Line, this returns 0 for the length of a Point. - - Examples - ======== - - >>> from sympy import Point - >>> p = Point(0, 1) - >>> p.length - 0 - """ - return S.Zero -
    -
    [docs] def is_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 - ======= - - is_collinear : boolean - - Notes - ===== - - Slope is preserved everywhere on a line, so the slope between - any two points on the line should be the same. Take the first - two points, p1 and p2, and create a translated point v1 - with p1 as the origin. Now for every other point we create - a translated point, vi with p1 also as the origin. Note that - these translations preserve slope since everything is - consistently translated to a new origin of p1. Since slope - is preserved then we have the following equality: - - * v1_slope = vi_slope - * v1.y/v1.x = vi.y/vi.x (due to translation) - * v1.y*vi.x = vi.y*v1.x - * v1.y*vi.x - vi.y*v1.x = 0 (*) - - Hence, if we have a vi such that the equality in (*) is False - then the points are not collinear. We do this test for every - point in the list, and if all pass then they are collinear. - - See Also - ======== - - sympy.geometry.line.Line - - Examples - ======== - - >>> from sympy import Point - >>> from sympy.abc import x - >>> p1, p2 = Point(0, 0), Point(1, 1) - >>> p3, p4, p5 = Point(2, 2), Point(x, x), Point(1, 2) - >>> Point.is_collinear(p1, p2, p3, p4) - True - >>> Point.is_collinear(p1, p2, p3, p5) - False - - """ - if len(points) == 0: - return False - if len(points) <= 2: - return True # two points always form a line - points = [Point(a) for a in points] - - # XXX Cross product is used now, but that only extends to three - # dimensions. If the concept needs to extend to greater - # dimensions then another method would have to be used - p1 = points[0] - p2 = points[1] - v1 = p2 - p1 - x1, y1 = v1.args - rv = True - for p3 in points[2:]: - x2, y2 = (p3 - p1).args - test = simplify(x1*y2 - y1*x2).equals(0) - if test is False: - return False - if rv and not test: - rv = test - return rv -
    -
    [docs] def is_concyclic(*points): - """Is a sequence of points concyclic? - - Test whether or not a sequence of points are concyclic (i.e., they lie - on a circle). - - Parameters - ========== - - points : sequence of Points - - Returns - ======= - - is_concyclic : boolean - True if points are concyclic, False otherwise. - - See Also - ======== - - sympy.geometry.ellipse.Circle - - Notes - ===== - - No points are not considered to be concyclic. One or two points - are definitely concyclic and three points are conyclic iff they - are not collinear. - - For more than three points, create a circle from the first three - points. If the circle cannot be created (i.e., they are collinear) - then all of the points cannot be concyclic. If the circle is created - successfully then simply check the remaining points for containment - in the circle. - - Examples - ======== - - >>> from sympy.geometry import Point - >>> p1, p2 = Point(-1, 0), Point(1, 0) - >>> p3, p4 = Point(0, 1), Point(-1, 2) - >>> Point.is_concyclic(p1, p2, p3) - True - >>> Point.is_concyclic(p1, p2, p3, p4) - False - - """ - if len(points) == 0: - return False - if len(points) <= 2: - return True - points = [Point(p) for p in points] - if len(points) == 3: - return (not Point.is_collinear(*points)) - - try: - from .ellipse import Circle - c = Circle(points[0], points[1], points[2]) - for point in points[3:]: - if point not in c: - return False - return True - except GeometryError: - # Circle could not be created, because of collinearity of the - # three points passed in, hence they are not concyclic. - return False - -# """ -# # This code is from Maple -# def f(u): -# dd = u[0]**2 + u[1]**2 + 1 -# u1 = 2*u[0] / dd -# u2 = 2*u[1] / dd -# u3 = (dd - 2) / dd -# return u1,u2,u3 - -# u1,u2,u3 = f(points[0]) -# v1,v2,v3 = f(points[1]) -# w1,w2,w3 = f(points[2]) -# p = [v1 - u1, v2 - u2, v3 - u3] -# q = [w1 - u1, w2 - u2, w3 - u3] -# r = [p[1]*q[2] - p[2]*q[1], p[2]*q[0] - p[0]*q[2], p[0]*q[1] - p[1]*q[0]] -# for ind in xrange(3, len(points)): -# s1,s2,s3 = f(points[ind]) -# test = simplify(r[0]*(s1-u1) + r[1]*(s2-u2) + r[2]*(s3-u3)) -# if test != 0: -# return False -# return True -# """ -
    -
    [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.geometry import Point - >>> p1, p2 = Point(1, 1), Point(4, 5) - >>> p1.distance(p2) - 5 - - >>> from sympy.abc import x, y - >>> p3 = Point(x, y) - >>> p3.distance(Point(0, 0)) - sqrt(x**2 + y**2) - - """ - p = Point(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.geometry import Point - >>> p1, p2 = Point(1, 1), Point(13, 5) - >>> p1.midpoint(p2) - Point(7, 3) - - """ - return Point([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 Point, Rational - >>> p1 = Point(Rational(1, 2), Rational(3, 2)) - >>> p1 - Point(1/2, 3/2) - >>> p1.evalf() - Point(0.5, 1.5) - - """ - if prec is None: - coords = [x.evalf(**options) for x in self.args] - else: - coords = [x.evalf(prec, **options) for x in self.args] - return Point(*coords, **dict(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 Point - >>> p1, p2, p3 = Point(0, 0), Point(1, 1), Point(0, 0) - >>> p1.intersection(p2) - [] - >>> p1.intersection(p3) - [Point(0, 0)] - - """ - if isinstance(o, Point): - if self == o: - return [self] - return [] - - return o.intersection(self) -
    -
    [docs] def rotate(self, angle, pt=None): - """Rotate ``angle`` radians counterclockwise about Point ``pt``. - - See Also - ======== - - rotate, scale - - Examples - ======== - - >>> from sympy import Point, pi - >>> t = Point(1, 0) - >>> t.rotate(pi/2) - Point(0, 1) - >>> t.rotate(pi/2, (2, 0)) - Point(2, -1) - - """ - from sympy import cos, sin, Point - - c = cos(angle) - s = sin(angle) - - rv = self - if pt is not None: - pt = Point(pt) - rv -= pt - x, y = rv.args - rv = Point(c*x - s*y, s*x + c*y) - if pt is not None: - rv += pt - return rv -
    -
    [docs] def scale(self, x=1, y=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 - ======== - - rotate, translate - - Examples - ======== - - >>> from sympy import Point - >>> t = Point(1, 1) - >>> t.scale(2) - Point(2, 1) - >>> t.scale(2, 2) - Point(2, 2) - - """ - if pt: - pt = Point(pt) - return self.translate(*(-pt).args).scale(x, y).translate(*pt.args) - return Point(self.x*x, self.y*y) -
    -
    [docs] def translate(self, x=0, y=0): - """Shift the Point by adding x and y to the coordinates of the Point. - - See Also - ======== - - rotate, scale - - Examples - ======== - - >>> from sympy import Point - >>> t = Point(0, 1) - >>> t.translate(2) - Point(2, 1) - >>> t.translate(2, 2) - Point(2, 3) - >>> t + Point(2, 2) - Point(2, 3) - - """ - return Point(self.x + x, self.y + y) -
    -
    [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 = self.args - return Point(*(Matrix(1, 3, [x, y, 1])*matrix).tolist()[0][:2]) -
    -
    [docs] def dot(self, p2): - """Return dot product of self with another Point.""" - p2 = Point(p2) - x1, y1 = self.args - x2, y2 = p2.args - return x1*x2 + y1*y2 -
    - 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, Point): - if len(other.args) == len(self.args): - return Point(*[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.""" - return self + (-other) - - def __mul__(self, factor): - """Multiply point's coordinates by a factor.""" - factor = sympify(factor) - return Point([x*factor for x in self.args]) - - def __div__(self, divisor): - """Divide point's coordinates by a factor.""" - divisor = sympify(divisor) - return Point([x/divisor for x in self.args]) - - __truediv__ = __div__ - - def __neg__(self): - """Negate the point.""" - return Point([-x for x in self.args]) - - def __abs__(self): - """Returns the distance between this point and the origin.""" - origin = Point([0]*len(self.args)) - return Point.distance(origin, self)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/geometry/polygon.html b/dev-py3k/_modules/sympy/geometry/polygon.html deleted file mode 100644 index 99581cf817f..00000000000 --- a/dev-py3k/_modules/sympy/geometry/polygon.html +++ /dev/null @@ -1,2342 +0,0 @@ - - - - - - - - - - sympy.geometry.polygon — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.geometry.polygon

    -from sympy.core import Expr, S, sympify, oo, pi, Symbol, zoo
    -from sympy.core.compatibility import as_int
    -from sympy.functions.elementary.piecewise import Piecewise
    -from sympy.functions.elementary.trigonometric import cos, sin, tan, sqrt
    -from sympy.simplify import simplify, nsimplify
    -from sympy.geometry.exceptions import GeometryError
    -from sympy.matrices import Matrix
    -from sympy.solvers import solve
    -from sympy.utilities.iterables import has_variety, has_dups
    -
    -from .entity import GeometryEntity
    -from .point import Point
    -from .ellipse import Circle
    -from .line import Line, Segment
    -from .util import _symbol
    -
    -import warnings
    -
    -
    -
    [docs]class Polygon(GeometryEntity): - """A two-dimensional polygon. - - A simple polygon in space. Can be constructed from a sequence of points - or from a center, radius, number of sides and rotation angle. - - Parameters - ========== - - vertices : sequence of Points - - Attributes - ========== - - area - angles - perimeter - vertices - centroid - sides - - Raises - ====== - - GeometryError - If all parameters are not Points. - - If the Polygon has intersecting sides. - - See Also - ======== - - sympy.geometry.point.Point, sympy.geometry.line.Segment, Triangle - - Notes - ===== - - Polygons are treated as closed paths rather than 2D areas so - some calculations can be be negative or positive (e.g., area) - based on the orientation of the points. - - Any consecutive identical points are reduced to a single point - and any points collinear and between two points will be removed - unless they are needed to define an explicit intersection (see examples). - - A Triangle, Segment or Point will be returned when there are 3 or - fewer points provided. - - Examples - ======== - - >>> from sympy import Point, Polygon, pi - >>> p1, p2, p3, p4, p5 = [(0, 0), (1, 0), (5, 1), (0, 1), (3, 0)] - >>> Polygon(p1, p2, p3, p4) - Polygon(Point(0, 0), Point(1, 0), Point(5, 1), Point(0, 1)) - >>> Polygon(p1, p2) - Segment(Point(0, 0), Point(1, 0)) - >>> Polygon(p1, p2, p5) - Segment(Point(0, 0), Point(3, 0)) - - While the sides of a polygon are not allowed to cross implicitly, they - can do so explicitly. For example, a polygon shaped like a Z with the top - left connecting to the bottom right of the Z must have the point in the - middle of the Z explicitly given: - - >>> mid = Point(1, 1) - >>> Polygon((0, 2), (2, 2), mid, (0, 0), (2, 0), mid).area - 0 - >>> Polygon((0, 2), (2, 2), mid, (2, 0), (0, 0), mid).area - -2 - - When the the keyword `n` is used to define the number of sides of the - Polygon then a RegularPolygon is created and the other arguments are - interpreted as center, radius and rotation. The unrotated RegularPolygon - will always have a vertex at Point(r, 0) where `r` is the radius of the - circle that circumscribes the RegularPolygon. Its method `spin` can be - used to increment that angle. - - >>> p = Polygon((0,0), 1, n=3) - >>> p - RegularPolygon(Point(0, 0), 1, 3, 0) - >>> p.vertices[0] - Point(1, 0) - >>> p.args[0] - Point(0, 0) - >>> p.spin(pi/2) - >>> p.vertices[0] - Point(0, 1) - - """ - - def __new__(cls, *args, **kwargs): - if kwargs.get('n', 0): - n = kwargs.pop('n') - args = list(args) - # return a virtual polygon with n sides - if len(args) == 2: # center, radius - args.append(n) - elif len(args) == 3: # center, radius, rotation - args.insert(2, n) - return RegularPolygon(*args, **kwargs) - - vertices = [Point(a) for a in args] - - # remove consecutive duplicates - nodup = [] - for p in vertices: - if nodup and p == nodup[-1]: - continue - nodup.append(p) - if len(nodup) > 1 and nodup[-1] == nodup[0]: - nodup.pop() # last point was same as first - - # remove collinear points unless they are shared points - got = set() - shared = set() - for p in nodup: - if p in got: - shared.add(p) - else: - got.add(p) - i = -3 - while i < len(nodup) - 3 and len(nodup) > 2: - a, b, c = sorted([nodup[i], nodup[i + 1], nodup[i + 2]]) - if b not in shared and Point.is_collinear(a, b, c): - nodup[i] = a - nodup[i + 1] = None - nodup.pop(i + 1) - i += 1 - - vertices = [x for x in nodup if x is not None] - - if len(vertices) > 3: - rv = GeometryEntity.__new__(cls, *vertices, **kwargs) - elif len(vertices) == 3: - return Triangle(*vertices, **kwargs) - elif len(vertices) == 2: - return Segment(*vertices, **kwargs) - else: - return Point(*vertices, **kwargs) - - # reject polygons that have intersecting sides unless the - # intersection is a shared point or a generalized intersection. - # A self-intersecting polygon is easier to detect than a - # random set of segments since only those sides that are not - # part of the convex hull can possibly intersect with other - # sides of the polygon...but for now we use the n**2 algorithm - # and check all sides with intersection with any preceding sides - hit = _symbol('hit') - if not rv.is_convex: - sides = rv.sides - for i, si in enumerate(sides): - pts = si[0], si[1] - ai = si.arbitrary_point(hit) - for j in range(i): - sj = sides[j] - if sj[0] not in pts and sj[1] not in pts: - aj = si.arbitrary_point(hit) - tx = (solve(ai[0] - aj[0]) or [S.Zero])[0] - if tx.is_number and 0 <= tx <= 1: - ty = (solve(ai[1] - aj[1]) or [S.Zero])[0] - if (tx or ty) and ty.is_number and 0 <= ty <= 1: - print(ai, aj) - raise GeometryError( - "Polygon has intersecting sides.") - - return rv - - @property -
    [docs] def area(self): - """ - The area of the polygon. - - Notes - ===== - - The area calculation can be positive or negative based on the - orientation of the points. - - See Also - ======== - - sympy.geometry.ellipse.Ellipse.area - - Examples - ======== - - >>> from sympy import Point, Polygon - >>> p1, p2, p3, p4 = list(map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)])) - >>> poly = Polygon(p1, p2, p3, p4) - >>> poly.area - 3 - - """ - area = 0 - args = self.args - for i in range(len(args)): - x1, y1 = args[i - 1].args - x2, y2 = args[i].args - area += x1*y2 - x2*y1 - return simplify(area) / 2 -
    - @property -
    [docs] def angles(self): - """The internal angle at each vertex. - - Returns - ======= - - angles : dict - A dictionary where each key is a vertex and each value is the - internal angle at that vertex. The vertices are represented as - Points. - - See Also - ======== - - sympy.geometry.point.Point, sympy.geometry.line.LinearEntity.angle_between - - Examples - ======== - - >>> from sympy import Point, Polygon - >>> p1, p2, p3, p4 = list(map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)])) - >>> poly = Polygon(p1, p2, p3, p4) - >>> poly.angles[p1] - pi/2 - >>> poly.angles[p2] - acos(-4*sqrt(17)/17) - - """ - - def _isright(a, b, c): - ba = b - a - ca = c - a - t_area = ba.x*ca.y - ca.x*ba.y - return bool(t_area <= 0) - - # Determine orientation of points - args = self.vertices - cw = _isright(args[-1], args[0], args[1]) - - ret = {} - for i in range(len(args)): - a, b, c = args[i - 2], args[i - 1], args[i] - ang = Line.angle_between(Line(b, a), Line(b, c)) - if cw ^ _isright(a, b, c): - ret[b] = 2*S.Pi - ang - else: - ret[b] = ang - return ret -
    - @property -
    [docs] def perimeter(self): - """The perimeter of the polygon. - - Returns - ======= - - perimeter : number or Basic instance - - See Also - ======== - - sympy.geometry.line.Segment.length - - Examples - ======== - - >>> from sympy import Point, Polygon - >>> p1, p2, p3, p4 = list(map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)])) - >>> poly = Polygon(p1, p2, p3, p4) - >>> poly.perimeter - sqrt(17) + 7 - """ - p = 0 - args = self.vertices - for i in range(len(args)): - p += args[i - 1].distance(args[i]) - return simplify(p) -
    - @property -
    [docs] def vertices(self): - """The vertices of the polygon. - - Returns - ======= - - vertices : tuple of Points - - Notes - ===== - - When iterating over the vertices, it is more efficient to index self - rather than to request the vertices and index them. Only use the - vertices when you want to process all of them at once. This is even - more important with RegularPolygons that calculate each vertex. - - See Also - ======== - - sympy.geometry.point.Point - - Examples - ======== - - >>> from sympy import Point, Polygon - >>> p1, p2, p3, p4 = list(map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)])) - >>> poly = Polygon(p1, p2, p3, p4) - >>> poly.vertices - (Point(0, 0), Point(1, 0), Point(5, 1), Point(0, 1)) - >>> poly.args[0] - Point(0, 0) - - """ - return self.args -
    - @property -
    [docs] def centroid(self): - """The centroid of the polygon. - - Returns - ======= - - centroid : Point - - See Also - ======== - - sympy.geometry.point.Point, sympy.geometry.util.centroid - - Examples - ======== - - >>> from sympy import Point, Polygon - >>> p1, p2, p3, p4 = list(map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)])) - >>> poly = Polygon(p1, p2, p3, p4) - >>> poly.centroid - Point(31/18, 11/18) - - """ - A = 1/(6*self.area) - cx, cy = 0, 0 - args = self.args - for i in range(len(args)): - x1, y1 = args[i - 1].args - x2, y2 = args[i].args - v = x1*y2 - x2*y1 - cx += v*(x1 + x2) - cy += v*(y1 + y2) - return Point(simplify(A*cx), simplify(A*cy)) -
    - @property -
    [docs] def sides(self): - """The line segments that form the sides of the polygon. - - Returns - ======= - - sides : list of sides - Each side is a Segment. - - Notes - ===== - - The Segments that represent the sides are an undirected - line segment so cannot be used to tell the orientation of - the polygon. - - See Also - ======== - - sympy.geometry.point.Point, sympy.geometry.line.Segment - - Examples - ======== - - >>> from sympy import Point, Polygon - >>> p1, p2, p3, p4 = list(map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)])) - >>> poly = Polygon(p1, p2, p3, p4) - >>> poly.sides - [Segment(Point(0, 0), Point(1, 0)), - Segment(Point(1, 0), Point(5, 1)), - Segment(Point(0, 1), Point(5, 1)), Segment(Point(0, 0), Point(0, 1))] - - """ - res = [] - args = self.vertices - for i in range(-len(args), 0): - res.append(Segment(args[i], args[i + 1])) - return res -
    -
    [docs] def is_convex(self): - """Is the polygon convex? - - A polygon is convex if all its interior angles are less than 180 - degrees. - - Returns - ======= - - is_convex : boolean - True if this polygon is convex, False otherwise. - - See Also - ======== - - sympy.geometry.util.convex_hull - - Examples - ======== - - >>> from sympy import Point, Polygon - >>> p1, p2, p3, p4 = list(map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)])) - >>> poly = Polygon(p1, p2, p3, p4) - >>> poly.is_convex() - True - - """ - - def _isright(a, b, c): - ba = b - a - ca = c - a - t_area = simplify(ba.x*ca.y - ca.x*ba.y) - return bool(t_area <= 0) - - # Determine orientation of points - args = self.vertices - cw = _isright(args[-2], args[-1], args[0]) - for i in range(1, len(args)): - if cw ^ _isright(args[i - 2], args[i - 1], args[i]): - return False - - return True -
    -
    [docs] def encloses_point(self, p): - """ - Return True if p is enclosed by (is inside of) self. - - Notes - ===== - - Being on the border of self is considered False. - - Parameters - ========== - - p : Point - - Returns - ======= - - encloses_point : True, False or None - - See Also - ======== - - sympy.geometry.point.Point, sympy.geometry.ellipse.Ellipse.encloses_point - - Examples - ======== - - >>> from sympy import Polygon, Point - >>> from sympy.abc import t - >>> p = Polygon((0, 0), (4, 0), (4, 4)) - >>> p.encloses_point(Point(2, 1)) - True - >>> p.encloses_point(Point(2, 2)) - False - >>> p.encloses_point(Point(5, 5)) - False - - References - ========== - - [1] http://www.ariel.com.au/a/python-point-int-poly.html - - """ - p = Point(p) - if p in self.vertices or any(p in s for s in self.sides): - return False - - # move to p, checking that the result is numeric - lit = [] - for v in self.vertices: - lit.append(v - p) # the difference is simplified - if lit[-1].free_symbols: - return None - self = Polygon(*lit) - - # polygon closure is assumed in the following test but Polygon removes duplicate pts so - # the last point has to be added so all sides are computed. Using Polygon.sides is - # not good since Segments are unordered. - args = self.args - indices = list(range(-len(args), 1)) - - if self.is_convex(): - orientation = None - for i in indices: - a = args[i] - b = args[i + 1] - test = ((-a.y)*(b.x - a.x) - (-a.x)*(b.y - a.y)).is_negative - if orientation is None: - orientation = test - elif test is not orientation: - return False - return True - - hit_odd = False - p1x, p1y = args[0].args - for i in indices[1:]: - p2x, p2y = args[i].args - if 0 > min(p1y, p2y): - if 0 <= max(p1y, p2y): - if 0 <= max(p1x, p2x): - if p1y != p2y: - xinters = (-p1y)*(p2x - p1x)/(p2y - p1y) + p1x - if p1x == p2x or 0 <= xinters: - hit_odd = not hit_odd - p1x, p1y = p2x, p2y - return hit_odd -
    -
    [docs] def arbitrary_point(self, parameter='t'): - """A parameterized point on the polygon. - - The parameter, varying from 0 to 1, assigns points to the position on - the perimeter that is that fraction of the total perimeter. So the - point evaluated at t=1/2 would return the point from the first vertex - that is 1/2 way around the polygon. - - Parameters - ========== - - parameter : str, optional - Default value is 't'. - - Returns - ======= - - arbitrary_point : Point - - Raises - ====== - - ValueError - When `parameter` already appears in the Polygon's definition. - - See Also - ======== - - sympy.geometry.point.Point - - Examples - ======== - - >>> from sympy import Polygon, S, Symbol - >>> t = Symbol('t', real=True) - >>> tri = Polygon((0, 0), (1, 0), (1, 1)) - >>> p = tri.arbitrary_point('t') - >>> perimeter = tri.perimeter - >>> s1, s2 = [s.length for s in tri.sides[:2]] - >>> p.subs(t, (s1 + s2/2)/perimeter) - Point(1, 1/2) - - """ - t = _symbol(parameter) - if t.name in (f.name for f in self.free_symbols): - raise ValueError('Symbol %s already appears in object and cannot be used as a parameter.' % t.name) - sides = [] - perimeter = self.perimeter - perim_fraction_start = 0 - for s in self.sides: - side_perim_fraction = s.length/perimeter - perim_fraction_end = perim_fraction_start + side_perim_fraction - pt = s.arbitrary_point(parameter).subs( - t, (t - perim_fraction_start)/side_perim_fraction) - sides.append( - (pt, (perim_fraction_start <= t < perim_fraction_end))) - perim_fraction_start = perim_fraction_end - return Piecewise(*sides) -
    -
    [docs] def plot_interval(self, parameter='t'): - """The plot interval for the default geometric plot of the polygon. - - Parameters - ========== - - parameter : str, optional - Default value is 't'. - - Returns - ======= - - plot_interval : list (plot interval) - [parameter, lower_bound, upper_bound] - - Examples - ======== - - >>> from sympy import Polygon - >>> p = Polygon((0, 0), (1, 0), (1, 1)) - >>> p.plot_interval() - [t, 0, 1] - - """ - t = Symbol(parameter, real=True) - return [t, 0, 1] -
    -
    [docs] def intersection(self, o): - """The intersection of two polygons. - - The intersection may be empty and can contain individual Points and - complete Line Segments. - - Parameters - ========== - - other: Polygon - - Returns - ======= - - intersection : list - The list of Segments and Points - - See Also - ======== - - sympy.geometry.point.Point, sympy.geometry.line.Segment - - Examples - ======== - - >>> from sympy import Point, Polygon - >>> p1, p2, p3, p4 = list(map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)])) - >>> poly1 = Polygon(p1, p2, p3, p4) - >>> p5, p6, p7 = list(map(Point, [(3, 2), (1, -1), (0, 2)])) - >>> poly2 = Polygon(p5, p6, p7) - >>> poly1.intersection(poly2) - [Point(2/3, 0), Point(9/5, 1/5), Point(7/3, 1), Point(1/3, 1)] - - """ - res = [] - for side in self.sides: - inter = side.intersection(o) - if inter is not None: - res.extend(inter) - return res -
    -
    [docs] def distance(self, o): - """ - Returns the shortest distance between self and o. - - If o is a point, then self does not need to be convex. - If o is another polygon self and o must be complex. - - Examples - ======== - - >>> from sympy import Point, Polygon, RegularPolygon - >>> p1, p2 = list(map(Point, [(0, 0), (7, 5)])) - >>> poly = Polygon(*RegularPolygon(p1, 1, 3).vertices) - >>> poly.distance(p2) - sqrt(61) - """ - if isinstance(o, Point): - dist = oo - for side in self.sides: - current = side.distance(o) - if current == 0: - return S.Zero - elif current < dist: - dist = current - return dist - elif isinstance(o, Polygon) and self.is_convex() and o.is_convex(): - return self._do_poly_distance(o) - raise NotImplementedError() -
    - def _do_poly_distance(self, e2): - """ - Calculates the least distance between the exteriors of two - convex polygons e1 and e2. Does not check for the convexity - of the polygons as this is checked by Polygon.distance. - - Notes - ===== - - - Prints a warning if the two polygons possibly intersect as the return - value will not be valid in such a case. For a more through test of - intersection use intersection(). - - See Also - ======== - - sympy.geometry.point.Point.distance - - Examples - ======= - - >>> from sympy.geometry import Point, Polygon - >>> square = Polygon(Point(0, 0), Point(0, 1), Point(1, 1), Point(1, 0)) - >>> triangle = Polygon(Point(1, 2), Point(2, 2), Point(2, 1)) - >>> square._do_poly_distance(triangle) - sqrt(2)/2 - - Description of method used - ========================== - - Method: - [1] http://cgm.cs.mcgill.ca/~orm/mind2p.html - Uses rotating calipers: - [2] http://en.wikipedia.org/wiki/Rotating_calipers - and antipodal points: - [3] http://en.wikipedia.org/wiki/Antipodal_point - """ - e1 = self - - '''Tests for a possible intersection between the polygons and outputs a warning''' - e1_center = e1.centroid - e2_center = e2.centroid - e1_max_radius = S.Zero - e2_max_radius = S.Zero - for vertex in e1.vertices: - r = Point.distance(e1_center, vertex) - if e1_max_radius < r: - e1_max_radius = r - for vertex in e2.vertices: - r = Point.distance(e2_center, vertex) - if e2_max_radius < r: - e2_max_radius = r - center_dist = Point.distance(e1_center, e2_center) - if center_dist <= e1_max_radius + e2_max_radius: - warnings.warn("Polygons may intersect producing erroneous output") - - ''' - Find the upper rightmost vertex of e1 and the lowest leftmost vertex of e2 - ''' - e1_ymax = Point(0, -oo) - e2_ymin = Point(0, oo) - - for vertex in e1.vertices: - if vertex.y > e1_ymax.y or (vertex.y == e1_ymax.y and vertex.x > e1_ymax.x): - e1_ymax = vertex - for vertex in e2.vertices: - if vertex.y < e2_ymin.y or (vertex.y == e2_ymin.y and vertex.x < e2_ymin.x): - e2_ymin = vertex - min_dist = Point.distance(e1_ymax, e2_ymin) - - ''' - Produce a dictionary with vertices of e1 as the keys and, for each vertex, the points - to which the vertex is connected as its value. The same is then done for e2. - ''' - e1_connections = {} - e2_connections = {} - - for side in e1.sides: - if side.p1 in e1_connections: - e1_connections[side.p1].append(side.p2) - else: - e1_connections[side.p1] = [side.p2] - - if side.p2 in e1_connections: - e1_connections[side.p2].append(side.p1) - else: - e1_connections[side.p2] = [side.p1] - - for side in e2.sides: - if side.p1 in e2_connections: - e2_connections[side.p1].append(side.p2) - else: - e2_connections[side.p1] = [side.p2] - - if side.p2 in e2_connections: - e2_connections[side.p2].append(side.p1) - else: - e2_connections[side.p2] = [side.p1] - - e1_current = e1_ymax - e2_current = e2_ymin - support_line = Line(Point(S.Zero, S.Zero), Point(S.One, S.Zero)) - - ''' - Determine which point in e1 and e2 will be selected after e2_ymin and e1_ymax, - this information combined with the above produced dictionaries determines the - path that will be taken around the polygons - ''' - point1 = e1_connections[e1_ymax][0] - point2 = e1_connections[e1_ymax][1] - angle1 = support_line.angle_between(Line(e1_ymax, point1)) - angle2 = support_line.angle_between(Line(e1_ymax, point2)) - if angle1 < angle2: - e1_next = point1 - elif angle2 < angle1: - e1_next = point2 - elif Point.distance(e1_ymax, point1) > Point.distance(e1_ymax, point2): - e1_next = point2 - else: - e1_next = point1 - - point1 = e2_connections[e2_ymin][0] - point2 = e2_connections[e2_ymin][1] - angle1 = support_line.angle_between(Line(e2_ymin, point1)) - angle2 = support_line.angle_between(Line(e2_ymin, point2)) - if angle1 > angle2: - e2_next = point1 - elif angle2 > angle1: - e2_next = point2 - elif Point.distance(e2_ymin, point1) > Point.distance(e2_ymin, point2): - e2_next = point2 - else: - e2_next = point1 - - ''' - Loop which determins the distance between anti-podal pairs and updates the - minimum distance accordingly. It repeats until it reaches the starting position. - ''' - while True: - e1_angle = support_line.angle_between(Line(e1_current, e1_next)) - e2_angle = pi - support_line.angle_between(Line( - e2_current, e2_next)) - - if e1_angle < e2_angle: - support_line = Line(e1_current, e1_next) - e1_segment = Segment(e1_current, e1_next) - min_dist_current = e1_segment.distance(e2_current) - - if min_dist_current.evalf() < min_dist.evalf(): - min_dist = min_dist_current - - if e1_connections[e1_next][0] != e1_current: - e1_current = e1_next - e1_next = e1_connections[e1_next][0] - else: - e1_current = e1_next - e1_next = e1_connections[e1_next][1] - elif e1_angle > e2_angle: - support_line = Line(e2_next, e2_current) - e2_segment = Segment(e2_current, e2_next) - min_dist_current = e2_segment.distance(e1_current) - - if min_dist_current.evalf() < min_dist.evalf(): - min_dist = min_dist_current - - if e2_connections[e2_next][0] != e2_current: - e2_current = e2_next - e2_next = e2_connections[e2_next][0] - else: - e2_current = e2_next - e2_next = e2_connections[e2_next][1] - else: - support_line = Line(e1_current, e1_next) - e1_segment = Segment(e1_current, e1_next) - e2_segment = Segment(e2_current, e2_next) - min1 = e1_segment.distance(e2_next) - min2 = e2_segment.distance(e1_next) - - min_dist_current = min(min1, min2) - if min_dist_current.evalf() < min_dist.evalf(): - min_dist = min_dist_current - - if e1_connections[e1_next][0] != e1_current: - e1_current = e1_next - e1_next = e1_connections[e1_next][0] - else: - e1_current = e1_next - e1_next = e1_connections[e1_next][1] - - if e2_connections[e2_next][0] != e2_current: - e2_current = e2_next - e2_next = e2_connections[e2_next][0] - else: - e2_current = e2_next - e2_next = e2_connections[e2_next][1] - if e1_current == e1_ymax and e2_current == e2_ymin: - break - return min_dist - - def __eq__(self, o): - if not isinstance(o, Polygon) or len(self.args) != len(o.args): - return False - - # See if self can ever be traversed (cw or ccw) from any of its - # vertices to match all points of o - args = self.args - oargs = o.args - n = len(args) - o0 = oargs[0] - for i0 in range(n): - if args[i0] == o0: - if all(args[(i0 + i) % n] == oargs[i] for i in range(1, n)): - return True - if all(args[(i0 - i) % n] == oargs[i] for i in range(1, n)): - return True - return False - - def __hash__(self): - return super(Polygon, self).__hash__() - - def __contains__(self, o): - """ - Return True if o is contained within the boundary lines of self.altitudes - - Parameters - ========== - - other : GeometryEntity - - Returns - ======= - - contained in : bool - The points (and sides, if applicable) are contained in self. - - See Also - ======== - - sympy.geometry.entity.GeometryEntity.encloses - - Examples - ======== - - >>> from sympy import Line, Segment, Point - >>> p = Point(0, 0) - >>> q = Point(1, 1) - >>> s = Segment(p, q*2) - >>> l = Line(p, q) - >>> p in q - False - >>> p in s - True - >>> q*3 in s - False - >>> s in l - True - - """ - - if isinstance(o, Polygon): - return self == o - elif isinstance(o, Segment): - return any(o in s for s in self.sides) - elif isinstance(o, Point): - if o in self.vertices: - return True - for side in self.sides: - if o in side: - return True - - return False - -
    -
    [docs]class RegularPolygon(Polygon): - """ - A regular polygon. - - Such a polygon has all internal angles equal and all sides the same length. - - Parameters - ========== - - center : Point - radius : number or Basic instance - The distance from the center to a vertex - n : int - The number of sides - - Attributes - ========== - - vertices - center - radius - rotation - apothem - interior_angle - exterior_angle - circumcircle - incircle - angles - - Raises - ====== - - GeometryError - If the `center` is not a Point, or the `radius` is not a number or Basic - instance, or the number of sides, `n`, is less than three. - - Notes - ===== - - A RegularPolygon can be instantiated with Polygon with the kwarg n. - - Regular polygons are instantiated with a center, radius, number of sides - and a rotation angle. Whereas the arguments of a Polygon are vertices, the - vertices of the RegularPolygon must be obtained with the vertices method. - - See Also - ======== - - sympy.geometry.point.Point, Polygon - - Examples - ======== - - >>> from sympy.geometry import RegularPolygon, Point - >>> r = RegularPolygon(Point(0, 0), 5, 3) - >>> r - RegularPolygon(Point(0, 0), 5, 3, 0) - >>> r.vertices[0] - Point(5, 0) - - """ - - __slots__ = ['_n', '_center', '_radius', '_rot'] - - def __new__(self, c, r, n, rot=0, **kwargs): - r, n, rot = list(map(sympify, (r, n, rot))) - c = Point(c) - if not isinstance(r, Expr): - raise GeometryError("r must be an Expr object, not %s" % r) - if n.is_Number: - as_int(n) # let an error raise if necessary - if n < 3: - raise GeometryError("n must be a >= 3, not %s" % n) - - obj = GeometryEntity.__new__(self, c, r, n, **kwargs) - obj._n = n - obj._center = c - obj._radius = r - obj._rot = rot - return obj - - @property -
    [docs] def args(self): - """ - Returns the center point, the radius, - the number of sides, and the orientation angle. - - Examples - ======== - - >>> from sympy import RegularPolygon, Point - >>> r = RegularPolygon(Point(0, 0), 5, 3) - >>> r.args - (Point(0, 0), 5, 3, 0) - """ - return self._center, self._radius, self._n, self._rot -
    - def __str__(self): - return 'RegularPolygon(%s, %s, %s, %s)' % tuple(self.args) - - def __repr__(self): - return 'RegularPolygon(%s, %s, %s, %s)' % tuple(self.args) - - @property -
    [docs] def area(self): - """Returns the area. - - Examples - ======== - >>> from sympy.geometry import RegularPolygon - >>> square = RegularPolygon((0, 0), 1, 4) - >>> square.area - 2 - >>> _ == square.length**2 - True - """ - c, r, n, rot = self.args - return n*self.length**2/(4*tan(pi/n)) -
    - @property -
    [docs] def length(self): - """Returns the length of the sides. - - The half-length of the side and the apothem form two legs - of a right triangle whose hypotenuse is the radius of the - regular polygon. - - Examples - ======== - >>> from sympy.geometry import RegularPolygon - >>> from sympy import sqrt - >>> s = square_in_unit_circle = RegularPolygon((0, 0), 1, 4) - >>> s.length - sqrt(2) - >>> sqrt((_/2)**2 + s.apothem**2) == s.radius - True - - """ - return self.radius*2*sin(pi/self._n) -
    - @property -
    [docs] def center(self): - """The center of the RegularPolygon - - This is also the center of the circumscribing circle. - - Returns - ======= - - center : Point - - See Also - ======== - - sympy.geometry.point.Point, sympy.geometry.ellipse.Ellipse.center - - Examples - ======== - - >>> from sympy.geometry import RegularPolygon, Point - >>> rp = RegularPolygon(Point(0, 0), 5, 4) - >>> rp.center - Point(0, 0) - """ - return self._center -
    - @property -
    [docs] def circumcenter(self): - """ - Alias for center. - - Examples - ======== - - >>> from sympy.geometry import RegularPolygon, Point - >>> rp = RegularPolygon(Point(0, 0), 5, 4) - >>> rp.circumcenter - Point(0, 0) - """ - return self.center -
    - @property -
    [docs] def radius(self): - """Radius of the RegularPolygon - - This is also the radius of the circumscribing circle. - - Returns - ======= - - radius : number or instance of Basic - - See Also - ======== - - sympy.geometry.line.Segment.length, sympy.geometry.ellipse.Circle.radius - - Examples - ======== - - >>> from sympy import Symbol - >>> from sympy.geometry import RegularPolygon, Point - >>> radius = Symbol('r') - >>> rp = RegularPolygon(Point(0, 0), radius, 4) - >>> rp.radius - r - - """ - return self._radius -
    - @property -
    [docs] def circumradius(self): - """ - Alias for radius. - - Examples - ======== - - >>> from sympy import Symbol - >>> from sympy.geometry import RegularPolygon, Point - >>> radius = Symbol('r') - >>> rp = RegularPolygon(Point(0, 0), radius, 4) - >>> rp.circumradius - r - """ - return self.radius -
    - @property -
    [docs] def rotation(self): - """CCW angle by which the RegularPolygon is rotated - - Returns - ======= - - rotation : number or instance of Basic - - Examples - ======== - - >>> from sympy import pi - >>> from sympy.geometry import RegularPolygon, Point - >>> RegularPolygon(Point(0, 0), 3, 4, pi).rotation - pi - - """ - return self._rot -
    - @property -
    [docs] def apothem(self): - """The inradius of the RegularPolygon. - - The apothem/inradius is the radius of the inscribed circle. - - Returns - ======= - - apothem : number or instance of Basic - - See Also - ======== - - sympy.geometry.line.Segment.length, sympy.geometry.ellipse.Circle.radius - - Examples - ======== - - >>> from sympy import Symbol - >>> from sympy.geometry import RegularPolygon, Point - >>> radius = Symbol('r') - >>> rp = RegularPolygon(Point(0, 0), radius, 4) - >>> rp.apothem - sqrt(2)*r/2 - - """ - return self.radius * cos(S.Pi/self._n) -
    - @property -
    [docs] def inradius(self): - """ - Alias for apothem. - - Examples - ======== - - >>> from sympy import Symbol - >>> from sympy.geometry import RegularPolygon, Point - >>> radius = Symbol('r') - >>> rp = RegularPolygon(Point(0, 0), radius, 4) - >>> rp.inradius - sqrt(2)*r/2 - """ - return self.apothem -
    - @property -
    [docs] def interior_angle(self): - """Measure of the interior angles. - - Returns - ======= - - interior_angle : number - - See Also - ======== - - sympy.geometry.line.LinearEntity.angle_between - - Examples - ======== - - >>> from sympy.geometry import RegularPolygon, Point - >>> rp = RegularPolygon(Point(0, 0), 4, 8) - >>> rp.interior_angle - 3*pi/4 - - """ - return (self._n - 2)*S.Pi/self._n -
    - @property -
    [docs] def exterior_angle(self): - """Measure of the exterior angles. - - Returns - ======= - - exterior_angle : number - - See Also - ======== - - sympy.geometry.line.LinearEntity.angle_between - - Examples - ======== - - >>> from sympy.geometry import RegularPolygon, Point - >>> rp = RegularPolygon(Point(0, 0), 4, 8) - >>> rp.exterior_angle - pi/4 - - """ - return 2*S.Pi/self._n -
    - @property -
    [docs] def circumcircle(self): - """The circumcircle of the RegularPolygon. - - Returns - ======= - - circumcircle : Circle - - See Also - ======== - - circumcenter, sympy.geometry.ellipse.Circle - - Examples - ======== - - >>> from sympy.geometry import RegularPolygon, Point - >>> rp = RegularPolygon(Point(0, 0), 4, 8) - >>> rp.circumcircle - Circle(Point(0, 0), 4) - - """ - return Circle(self.center, self.radius) -
    - @property -
    [docs] def incircle(self): - """The incircle of the RegularPolygon. - - Returns - ======= - - incircle : Circle - - See Also - ======== - - inradius, sympy.geometry.ellipse.Circle - - Examples - ======== - - >>> from sympy.geometry import RegularPolygon, Point - >>> rp = RegularPolygon(Point(0, 0), 4, 7) - >>> rp.incircle - Circle(Point(0, 0), 4*cos(pi/7)) - - """ - return Circle(self.center, self.apothem) -
    - @property -
    [docs] def angles(self): - """ - Returns a dictionary with keys, the vertices of the Polygon, - and values, the interior angle at each vertex. - - Examples - ======== - - >>> from sympy import RegularPolygon, Point - >>> r = RegularPolygon(Point(0, 0), 5, 3) - >>> r.angles - {Point(-5/2, -5*sqrt(3)/2): pi/3, - Point(-5/2, 5*sqrt(3)/2): pi/3, - Point(5, 0): pi/3} - """ - ret = {} - ang = self.interior_angle - for v in self.vertices: - ret[v] = ang - return ret -
    -
    [docs] def encloses_point(self, p): - """ - Return True if p is enclosed by (is inside of) self. - - Notes - ===== - - Being on the border of self is considered False. - - The general Polygon.encloses_point method is called only if - a point is not within or beyond the incircle or circumcircle, - respectively. - - Parameters - ========== - - p : Point - - Returns - ======= - - encloses_point : True, False or None - - See Also - ======== - - sympy.geometry.ellipse.Ellipse.encloses_point - - Examples - ======== - - >>> from sympy import RegularPolygon, S, Point, Symbol - >>> p = RegularPolygon((0, 0), 3, 4) - >>> p.encloses_point(Point(0, 0)) - True - >>> r, R = p.inradius, p.circumradius - >>> p.encloses_point(Point((r + R)/2, 0)) - True - >>> p.encloses_point(Point(R/2, R/2 + (R - r)/10)) - False - >>> t = Symbol('t', real=True) - >>> p.encloses_point(p.arbitrary_point().subs(t, S.Half)) - False - >>> p.encloses_point(Point(5, 5)) - False - - """ - - c = self.center - d = Segment(c, p).length - if d >= self.radius: - return False - elif d < self.inradius: - return True - else: - # now enumerate the RegularPolygon like a general polygon. - return Polygon.encloses_point(self, p) -
    -
    [docs] def spin(self, angle): - """Increment *in place* the virtual Polygon's rotation by ccw angle. - - See also: rotate method which moves the center. - - >>> from sympy import Polygon, Point, pi - >>> r = Polygon(Point(0,0), 1, n=3) - >>> r.vertices[0] - Point(1, 0) - >>> r.spin(pi/6) - >>> r.vertices[0] - Point(sqrt(3)/2, 1/2) - - See Also - ======== - - rotation - rotate : Creates a copy of the RegularPolygon rotated about a Point - - """ - self._rot += angle -
    -
    [docs] def rotate(self, angle, pt=None): - """Override GeometryEntity.rotate to first rotate the RegularPolygon - about its center. - - >>> from sympy import Point, RegularPolygon, Polygon, pi - >>> t = RegularPolygon(Point(1, 0), 1, 3) - >>> t.vertices[0] # vertex on x-axis - Point(2, 0) - >>> t.rotate(pi/2).vertices[0] # vertex on y axis now - Point(0, 2) - - See Also - ======== - - rotation - spin : Rotates a RegularPolygon in place - - """ - - r = type(self)(*self.args) # need a copy or else changes are in-place - r._rot += angle - return GeometryEntity.rotate(r, angle, pt) -
    -
    [docs] def scale(self, x=1, y=1, pt=None): - """Override GeometryEntity.scale since it is the radius that must be - scaled (if x == y) or else a new Polygon must be returned. - - >>> from sympy import RegularPolygon - - Symmetric scaling returns a RegularPolygon: - - >>> RegularPolygon((0, 0), 1, 4).scale(2, 2) - RegularPolygon(Point(0, 0), 2, 4, 0) - - Asymmetric scaling returns a kite as a Polygon: - - >>> RegularPolygon((0, 0), 1, 4).scale(2, 1) - Polygon(Point(2, 0), Point(0, 1), Point(-2, 0), Point(0, -1)) - - """ - if pt: - pt = Point(pt) - return self.translate(*(-pt).args).scale(x, y).translate(*pt.args) - if x != y: - return Polygon(*self.vertices).scale(x, y) - c, r, n, rot = self.args - r *= x - return self.func(c, r, n, rot) -
    - @property -
    [docs] def vertices(self): - """The vertices of the RegularPolygon. - - Returns - ======= - - vertices : list - Each vertex is a Point. - - See Also - ======== - - sympy.geometry.point.Point - - Examples - ======== - - >>> from sympy.geometry import RegularPolygon, Point - >>> rp = RegularPolygon(Point(0, 0), 5, 4) - >>> rp.vertices - [Point(5, 0), Point(0, 5), Point(-5, 0), Point(0, -5)] - - """ - c = self._center - r = self._radius - rot = self._rot - v = 2*S.Pi/self._n - - return [Point(c.x + r*cos(k*v + rot), c.y + r*sin(k*v + rot)) - for k in range(self._n)] -
    - def __eq__(self, o): - if not isinstance(o, Polygon): - return False - elif not isinstance(o, RegularPolygon): - return Polygon.__eq__(o, self) - return self.args == o.args - - def __hash__(self): - return super(RegularPolygon, self).__hash__() - -
    -
    [docs]class Triangle(Polygon): - """ - A polygon with three vertices and three sides. - - Parameters - ========== - - points : sequence of Points - keyword: asa, sas, or sss to specify sides/angles of the triangle - - Attributes - ========== - - vertices - altitudes - orthocenter - circumcenter - circumradius - circumcircle - inradius - incircle - medians - medial - - Raises - ====== - - GeometryError - If the number of vertices is not equal to three, or one of the vertices - is not a Point, or a valid keyword is not given. - - See Also - ======== - - sympy.geometry.point.Point, Polygon - - Examples - ======== - - >>> from sympy.geometry import Triangle, Point - >>> Triangle(Point(0, 0), Point(4, 0), Point(4, 3)) - Triangle(Point(0, 0), Point(4, 0), Point(4, 3)) - - Keywords sss, sas, or asa can be used to give the desired - side lengths (in order) and interior angles (in degrees) that - define the triangle: - - >>> Triangle(sss=(3, 4, 5)) - Triangle(Point(0, 0), Point(3, 0), Point(3, 4)) - >>> Triangle(asa=(30, 1, 30)) - Triangle(Point(0, 0), Point(1, 0), Point(1/2, sqrt(3)/6)) - >>> Triangle(sas=(1, 45, 2)) - Triangle(Point(0, 0), Point(2, 0), Point(sqrt(2)/2, sqrt(2)/2)) - - """ - - def __new__(cls, *args, **kwargs): - if len(args) != 3: - if 'sss' in kwargs: - return _sss(*[nsimplify(a) for a in kwargs['sss']]) - if 'asa' in kwargs: - return _asa(*[nsimplify(a) for a in kwargs['asa']]) - if 'sas' in kwargs: - return _sas(*[nsimplify(a) for a in kwargs['sas']]) - msg = "Triangle instantiates with three points or a valid keyword." - raise GeometryError(msg) - - vertices = [Point(a) for a in args] - - # remove consecutive duplicates - nodup = [] - for p in vertices: - if nodup and p == nodup[-1]: - continue - nodup.append(p) - if len(nodup) > 1 and nodup[-1] == nodup[0]: - nodup.pop() # last point was same as first - - # remove collinear points - i = -3 - while i < len(nodup) - 3 and len(nodup) > 2: - a, b, c = sorted([nodup[i], nodup[i + 1], nodup[i + 2]]) - if Point.is_collinear(a, b, c): - nodup[i] = a - nodup[i + 1] = None - nodup.pop(i + 1) - i += 1 - - vertices = [x for x in nodup if x is not None] - - if len(vertices) == 3: - return GeometryEntity.__new__(cls, *vertices, **kwargs) - elif len(vertices) == 2: - return Segment(*vertices, **kwargs) - else: - return Point(*vertices, **kwargs) - - @property -
    [docs] def vertices(self): - """The triangle's vertices - - Returns - ======= - - vertices : tuple - Each element in the tuple is a Point - - See Also - ======== - - sympy.geometry.point.Point - - Examples - ======== - - >>> from sympy.geometry import Triangle, Point - >>> t = Triangle(Point(0, 0), Point(4, 0), Point(4, 3)) - >>> t.vertices - (Point(0, 0), Point(4, 0), Point(4, 3)) - - """ - return self.args -
    -
    [docs] def is_similar(t1, t2): - """Is another triangle similar to this one. - - Two triangles are similar if one can be uniformly scaled to the other. - - Parameters - ========== - - other: Triangle - - Returns - ======= - - is_similar : boolean - - See Also - ======== - - sympy.geometry.entity.GeometryEntity.is_similar - - Examples - ======== - - >>> from sympy.geometry import Triangle, Point - >>> t1 = Triangle(Point(0, 0), Point(4, 0), Point(4, 3)) - >>> t2 = Triangle(Point(0, 0), Point(-4, 0), Point(-4, -3)) - >>> t1.is_similar(t2) - True - - >>> t2 = Triangle(Point(0, 0), Point(-4, 0), Point(-4, -4)) - >>> t1.is_similar(t2) - False - - """ - if not isinstance(t2, Polygon): - return False - - s1_1, s1_2, s1_3 = [side.length for side in t1.sides] - s2 = [side.length for side in t2.sides] - - def _are_similar(u1, u2, u3, v1, v2, v3): - e1 = simplify(u1/v1) - e2 = simplify(u2/v2) - e3 = simplify(u3/v3) - return bool(e1 == e2) and bool(e2 == e3) - - # There's only 6 permutations, so write them out - return _are_similar(s1_1, s1_2, s1_3, *s2) or \ - _are_similar(s1_1, s1_3, s1_2, *s2) or \ - _are_similar(s1_2, s1_1, s1_3, *s2) or \ - _are_similar(s1_2, s1_3, s1_1, *s2) or \ - _are_similar(s1_3, s1_1, s1_2, *s2) or \ - _are_similar(s1_3, s1_2, s1_1, *s2) -
    -
    [docs] def is_equilateral(self): - """Are all the sides the same length? - - Returns - ======= - - is_equilateral : boolean - - See Also - ======== - - sympy.geometry.entity.GeometryEntity.is_similar, RegularPolygon - is_isosceles, is_right, is_scalene - - Examples - ======== - - >>> from sympy.geometry import Triangle, Point - >>> t1 = Triangle(Point(0, 0), Point(4, 0), Point(4, 3)) - >>> t1.is_equilateral() - False - - >>> from sympy import sqrt - >>> t2 = Triangle(Point(0, 0), Point(10, 0), Point(5, 5*sqrt(3))) - >>> t2.is_equilateral() - True - - """ - return not has_variety(s.length for s in self.sides) -
    -
    [docs] def is_isosceles(self): - """Are two or more of the sides the same length? - - Returns - ======= - - is_isosceles : boolean - - See Also - ======== - - is_equilateral, is_right, is_scalene - - Examples - ======== - - >>> from sympy.geometry import Triangle, Point - >>> t1 = Triangle(Point(0, 0), Point(4, 0), Point(2, 4)) - >>> t1.is_isosceles() - True - - """ - return has_dups(s.length for s in self.sides) -
    -
    [docs] def is_scalene(self): - """Are all the sides of the triangle of different lengths? - - Returns - ======= - - is_scalene : boolean - - See Also - ======== - - is_equilateral, is_isosceles, is_right - - Examples - ======== - - >>> from sympy.geometry import Triangle, Point - >>> t1 = Triangle(Point(0, 0), Point(4, 0), Point(1, 4)) - >>> t1.is_scalene() - True - - """ - return not has_dups(s.length for s in self.sides) -
    -
    [docs] def is_right(self): - """Is the triangle right-angled. - - Returns - ======= - - is_right : boolean - - See Also - ======== - - sympy.geometry.line.LinearEntity.is_perpendicular - is_equilateral, is_isosceles, is_scalene - - Examples - ======== - - >>> from sympy.geometry import Triangle, Point - >>> t1 = Triangle(Point(0, 0), Point(4, 0), Point(4, 3)) - >>> t1.is_right() - True - - """ - s = self.sides - return Segment.is_perpendicular(s[0], s[1]) or \ - Segment.is_perpendicular(s[1], s[2]) or \ - Segment.is_perpendicular(s[0], s[2]) -
    - @property -
    [docs] def altitudes(self): - """The altitudes of the triangle. - - An altitude of a triangle is a segment through a vertex, - perpendicular to the opposite side, with length being the - height of the vertex measured from the line containing the side. - - Returns - ======= - - altitudes : dict - The dictionary consists of keys which are vertices and values - which are Segments. - - See Also - ======== - - sympy.geometry.point.Point, sympy.geometry.line.Segment.length - - Examples - ======== - - >>> from sympy.geometry import Point, Triangle - >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1) - >>> t = Triangle(p1, p2, p3) - >>> t.altitudes[p1] - Segment(Point(0, 0), Point(1/2, 1/2)) - - """ - s = self.sides - v = self.vertices - return {v[0]: s[1].perpendicular_segment(v[0]), - v[1]: s[2].perpendicular_segment(v[1]), - v[2]: s[0].perpendicular_segment(v[2])} -
    - @property -
    [docs] def orthocenter(self): - """The orthocenter of the triangle. - - The orthocenter is the intersection of the altitudes of a triangle. - It may lie inside, outside or on the triangle. - - Returns - ======= - - orthocenter : Point - - See Also - ======== - - sympy.geometry.point.Point - - Examples - ======== - - >>> from sympy.geometry import Point, Triangle - >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1) - >>> t = Triangle(p1, p2, p3) - >>> t.orthocenter - Point(0, 0) - - """ - a = self.altitudes - v = self.vertices - return Line(a[v[0]]).intersection(Line(a[v[1]]))[0] -
    - @property -
    [docs] def circumcenter(self): - """The circumcenter of the triangle - - The circumcenter is the center of the circumcircle. - - Returns - ======= - - circumcenter : Point - - See Also - ======== - - sympy.geometry.point.Point - - Examples - ======== - - >>> from sympy.geometry import Point, Triangle - >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1) - >>> t = Triangle(p1, p2, p3) - >>> t.circumcenter - Point(1/2, 1/2) - """ - a, b, c = [x.perpendicular_bisector() for x in self.sides] - return a.intersection(b)[0] -
    - @property -
    [docs] def circumradius(self): - """The radius of the circumcircle of the triangle. - - Returns - ======= - - circumradius : number of Basic instance - - See Also - ======== - - sympy.geometry.ellipse.Circle.radius - - Examples - ======== - - >>> from sympy import Symbol - >>> from sympy.geometry import Point, Triangle - >>> a = Symbol('a') - >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, a) - >>> t = Triangle(p1, p2, p3) - >>> t.circumradius - sqrt(a**2/4 + 1/4) - """ - return Point.distance(self.circumcenter, self.vertices[0]) -
    - @property -
    [docs] def circumcircle(self): - """The circle which passes through the three vertices of the triangle. - - Returns - ======= - - circumcircle : Circle - - See Also - ======== - - sympy.geometry.ellipse.Circle - - Examples - ======== - - >>> from sympy.geometry import Point, Triangle - >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1) - >>> t = Triangle(p1, p2, p3) - >>> t.circumcircle - Circle(Point(1/2, 1/2), sqrt(2)/2) - - """ - return Circle(self.circumcenter, self.circumradius) -
    -
    [docs] def bisectors(self): - """The angle bisectors of the triangle. - - An angle bisector of a triangle is a straight line through a vertex - which cuts the corresponding angle in half. - - Returns - ======= - - bisectors : dict - Each key is a vertex (Point) and each value is the corresponding - bisector (Segment). - - See Also - ======== - - sympy.geometry.point.Point, sympy.geometry.line.Segment - - Examples - ======== - - >>> from sympy.geometry import Point, Triangle, Segment - >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1) - >>> t = Triangle(p1, p2, p3) - >>> from sympy import sqrt - >>> t.bisectors()[p2] == Segment(Point(0, sqrt(2) - 1), Point(1, 0)) - True - - """ - s = self.sides - v = self.vertices - c = self.incenter - l1 = Segment(v[0], Line(v[0], c).intersection(s[1])[0]) - l2 = Segment(v[1], Line(v[1], c).intersection(s[2])[0]) - l3 = Segment(v[2], Line(v[2], c).intersection(s[0])[0]) - return {v[0]: l1, v[1]: l2, v[2]: l3} -
    - @property -
    [docs] def incenter(self): - """The center of the incircle. - - The incircle is the circle which lies inside the triangle and touches - all three sides. - - Returns - ======= - - incenter : Point - - See Also - ======== - - incircle, sympy.geometry.point.Point - - Examples - ======== - - >>> from sympy.geometry import Point, Triangle - >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1) - >>> t = Triangle(p1, p2, p3) - >>> t.incenter - Point(-sqrt(2)/2 + 1, -sqrt(2)/2 + 1) - - """ - s = self.sides - l = Matrix([s[i].length for i in [1, 2, 0]]) - p = sum(l) - v = self.vertices - x = simplify(l.dot(Matrix([vi.x for vi in v]))/p) - y = simplify(l.dot(Matrix([vi.y for vi in v]))/p) - return Point(x, y) -
    - @property -
    [docs] def inradius(self): - """The radius of the incircle. - - Returns - ======= - - inradius : number of Basic instance - - See Also - ======== - - incircle, sympy.geometry.ellipse.Circle.radius - - Examples - ======== - - >>> from sympy.geometry import Point, Triangle - >>> p1, p2, p3 = Point(0, 0), Point(4, 0), Point(0, 3) - >>> t = Triangle(p1, p2, p3) - >>> t.inradius - 1 - - """ - return simplify(2 * self.area / self.perimeter) -
    - @property -
    [docs] def incircle(self): - """The incircle of the triangle. - - The incircle is the circle which lies inside the triangle and touches - all three sides. - - Returns - ======= - - incircle : Circle - - See Also - ======== - - sympy.geometry.ellipse.Circle - - Examples - ======== - - >>> from sympy.geometry import Point, Triangle - >>> p1, p2, p3 = Point(0, 0), Point(2, 0), Point(0, 2) - >>> t = Triangle(p1, p2, p3) - >>> t.incircle - Circle(Point(-sqrt(2) + 2, -sqrt(2) + 2), -sqrt(2) + 2) - - """ - return Circle(self.incenter, self.inradius) -
    - @property -
    [docs] def medians(self): - """The medians of the triangle. - - A median of a triangle is a straight line through a vertex and the - midpoint of the opposite side, and divides the triangle into two - equal areas. - - Returns - ======= - - medians : dict - Each key is a vertex (Point) and each value is the median (Segment) - at that point. - - See Also - ======== - - sympy.geometry.point.Point.midpoint, sympy.geometry.line.Segment.midpoint - - Examples - ======== - - >>> from sympy.geometry import Point, Triangle - >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1) - >>> t = Triangle(p1, p2, p3) - >>> t.medians[p1] - Segment(Point(0, 0), Point(1/2, 1/2)) - - """ - s = self.sides - v = self.vertices - return {v[0]: Segment(s[1].midpoint, v[0]), - v[1]: Segment(s[2].midpoint, v[1]), - v[2]: Segment(s[0].midpoint, v[2])} -
    - @property -
    [docs] def medial(self): - """The medial triangle of the triangle. - - The triangle which is formed from the midpoints of the three sides. - - Returns - ======= - - medial : Triangle - - See Also - ======== - - sympy.geometry.line.Segment.midpoint - - Examples - ======== - - >>> from sympy.geometry import Point, Triangle - >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1) - >>> t = Triangle(p1, p2, p3) - >>> t.medial - Triangle(Point(1/2, 0), Point(1/2, 1/2), Point(0, 1/2)) - - """ - s = self.sides - return Triangle(s[0].midpoint, s[1].midpoint, s[2].midpoint) - - #@property - #def excircles(self): - # """Returns a list of the three excircles for this triangle.""" - # pass - -
    -def rad(d): - """Return the radian value for the given degrees (pi = 180 degrees).""" - return d*pi/180 - - -def deg(r): - """Return the degree value for the given radians (pi = 180 degrees).""" - return r/pi*180 - - -def _slope(d): - rv = tan(rad(d)) - return rv - - -def _asa(d1, l, d2): - """Return triangle having side with length l on the x-axis.""" - xy = Line((0, 0), slope=_slope(d1)).intersection( - Line((l, 0), slope=_slope(180 - d2)))[0] - return Triangle((0, 0), (l, 0), xy) - - -def _sss(l1, l2, l3): - """Return triangle having side of length l1 on the x-axis.""" - c1 = Circle((0, 0), l3) - c2 = Circle((l1, 0), l2) - inter = [a for a in c1.intersection(c2) if a.y.is_nonnegative] - if not inter: - return None - pt = inter[0] - return Triangle((0, 0), (l1, 0), pt) - - -def _sas(l1, d, l2): - """Return triangle having side with length l2 on the x-axis.""" - p1 = Point(0, 0) - p2 = Point(l2, 0) - p3 = Point(cos(rad(d))*l1, sin(rad(d))*l1) - return Triangle(p1, p2, p3) -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/geometry/util.html b/dev-py3k/_modules/sympy/geometry/util.html deleted file mode 100644 index d202dbf825d..00000000000 --- a/dev-py3k/_modules/sympy/geometry/util.html +++ /dev/null @@ -1,509 +0,0 @@ - - - - - - - - - - sympy.geometry.util — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.geometry.util

    -"""Utility functions for geometrical entities.
    -
    -Contains
    -========
    -intersection
    -convex_hull
    -are_similar
    -
    -"""
    -from sympy import Symbol, Function, solve
    -
    -
    -def idiff(eq, y, x, dep=None):
    -    """Return dy/dx assuming that y and any other variables given in dep
    -    depend on x.
    -
    -    >>> from sympy.abc import x, y, a
    -    >>> from sympy.geometry.util import idiff
    -
    -    >>> idiff(x**2 + y**2 - 4, y, x)
    -    -x/y
    -    >>> idiff(x + a + y, y, x)
    -    -1
    -    >>> idiff(x + a + y, y, x, [a])
    -    -Derivative(a, x) - 1
    -
    -    See Also
    -    ========
    -
    -    sympy.core.function.Derivative
    -
    -    """
    -    if not dep:
    -        dep = []
    -    dep = set(dep)
    -    dep.add(y)
    -
    -    f = dict([(s, Function(
    -        s.name)(x)) for s in eq.atoms(Symbol) if s != x and s in dep])
    -    dydx = Function(y.name)(x).diff(x)
    -    return solve(eq.subs(f).diff(x), dydx)[0].subs(
    -        [(b, a) for a, b in f.items()])
    -
    -
    -def _symbol(s, matching_symbol=None):
    -    """Return s if s is a Symbol, else return either a new Symbol (real=True)
    -    with the same name s or the matching_symbol if s is a string and it matches
    -    the name of the matching_symbol.
    -
    -    >>> from sympy import Symbol
    -    >>> from sympy.geometry.util import _symbol
    -    >>> x = Symbol('x')
    -    >>> _symbol('y')
    -    y
    -    >>> _.is_real
    -    True
    -    >>> _symbol(x)
    -    x
    -    >>> _.is_real is None
    -    True
    -    >>> arb = Symbol('foo')
    -    >>> _symbol('arb', arb) # arb's name is foo so foo will not be returned
    -    arb
    -    >>> _symbol('foo', arb) # now it will
    -    foo
    -
    -    NB: the symbol here may not be the same as a symbol with the same
    -    name defined elsewhere as a result of different assumptions.
    -
    -    See Also
    -    ========
    -
    -    sympy.core.symbol.Symbol
    -
    -    """
    -    if isinstance(s, str):
    -        if matching_symbol and matching_symbol.name == s:
    -            return matching_symbol
    -        return Symbol(s, real=True)
    -    elif isinstance(s, Symbol):
    -        return s
    -    else:
    -        raise ValueError('symbol must be string for symbol name or Symbol')
    -
    -
    -
    [docs]def intersection(*entities): - """The intersection of a collection of GeometryEntity instances. - - Parameters - ========== - - entities : sequence of GeometryEntity - - Returns - ======= - - intersection : list of GeometryEntity - - Raises - ====== - - NotImplementedError - When unable to calculate intersection. - - Notes - ===== - - The intersection of any geometrical entity with itself should return - a list with one item: the entity in question. - An intersection requires two or more entities. If only a single - entity is given then the function will return an empty list. - It is possible for `intersection` to miss intersections that one - knows exists because the required quantities were not fully - simplified internally. - Reals should be converted to Rationals, e.g. Rational(str(real_num)) - or else failures due to floating point issues may result. - - See Also - ======== - - sympy.geometry.entity.GeometryEntity.intersection - - Examples - ======== - - >>> from sympy.geometry import Point, Line, Circle, intersection - >>> p1, p2, p3 = Point(0, 0), Point(1, 1), Point(-1, 5) - >>> l1, l2 = Line(p1, p2), Line(p3, p2) - >>> c = Circle(p2, 1) - >>> intersection(l1, p2) - [Point(1, 1)] - >>> intersection(l1, l2) - [Point(1, 1)] - >>> intersection(c, p2) - [] - >>> intersection(c, Point(1, 0)) - [Point(1, 0)] - >>> intersection(c, l2) - [Point(-sqrt(5)/5 + 1, 2*sqrt(5)/5 + 1), - Point(sqrt(5)/5 + 1, -2*sqrt(5)/5 + 1)] - - """ - from .entity import GeometryEntity - from .point import Point - - if len(entities) <= 1: - return [] - - for i, e in enumerate(entities): - if not isinstance(e, GeometryEntity): - try: - entities[i] = Point(e) - except NotImplementedError: - raise ValueError('%s is not a GeometryEntity and cannot be made into Point' % str(e)) - - res = entities[0].intersection(entities[1]) - for entity in entities[2:]: - newres = [] - for x in res: - newres.extend(x.intersection(entity)) - res = newres - return res - -
    -
    [docs]def convex_hull(*args): - """The convex hull surrounding the Points contained in the list of entities. - - Parameters - ========== - - args : a collection of Points, Segments and/or Polygons - - Returns - ======= - - convex_hull : Polygon - - Notes - ===== - - This can only be performed on a set of non-symbolic points. - - References - ========== - - [1] http://en.wikipedia.org/wiki/Graham_scan - - [2] Andrew's Monotone Chain Algorithm - (A.M. Andrew, - "Another Efficient Algorithm for Convex Hulls in Two Dimensions", 1979) - http://softsurfer.com/Archive/algorithm_0109/algorithm_0109.htm - - See Also - ======== - - sympy.geometry.point.Point, sympy.geometry.polygon.Polygon - - Examples - ======== - - >>> from sympy.geometry import Point, convex_hull - >>> points = [(1,1), (1,2), (3,1), (-5,2), (15,4)] - >>> convex_hull(*points) - Polygon(Point(-5, 2), Point(1, 1), Point(3, 1), Point(15, 4)) - - """ - from .entity import GeometryEntity - from .point import Point - from .line import Segment - from .polygon import Polygon - - p = set() - for e in args: - if not isinstance(e, GeometryEntity): - try: - e = Point(e) - except NotImplementedError: - raise ValueError('%s is not a GeometryEntity and cannot be made into Point' % str(e)) - if isinstance(e, Point): - p.add(e) - elif isinstance(e, Segment): - p.update(e.points) - elif isinstance(e, Polygon): - p.update(e.vertices) - else: - raise NotImplementedError( - 'Convex hull for %s not implemented.' % type(e)) - - p = list(p) - if len(p) == 1: - return p[0] - elif len(p) == 2: - return Segment(p[0], p[1]) - - def _orientation(p, q, r): - '''Return positive if p-q-r are clockwise, neg if ccw, zero if - collinear.''' - return (q.y - p.y)*(r.x - p.x) - (q.x - p.x)*(r.y - p.y) - - # scan to find upper and lower convex hulls of a set of 2d points. - U = [] - L = [] - p.sort(key=lambda x: x.args) - for p_i in p: - while len(U) > 1 and _orientation(U[-2], U[-1], p_i) <= 0: - U.pop() - while len(L) > 1 and _orientation(L[-2], L[-1], p_i) >= 0: - L.pop() - U.append(p_i) - L.append(p_i) - U.reverse() - convexHull = tuple(L + U[1:-1]) - - if len(convexHull) == 2: - return Segment(convexHull[0], convexHull[1]) - return Polygon(*convexHull) - -
    -
    [docs]def are_similar(e1, e2): - """Are two geometrical entities similar. - - Can one geometrical entity be uniformly scaled to the other? - - Parameters - ========== - - e1 : GeometryEntity - e2 : GeometryEntity - - Returns - ======= - - are_similar : boolean - - Raises - ====== - - GeometryError - When `e1` and `e2` cannot be compared. - - Notes - ===== - - If the two objects are equal then they are similar. - - See Also - ======== - - sympy.geometry.entity.GeometryEntity.is_similar - - Examples - ======== - - >>> from sympy import Point, Circle, Triangle, are_similar - >>> c1, c2 = Circle(Point(0, 0), 4), Circle(Point(1, 4), 3) - >>> t1 = Triangle(Point(0, 0), Point(1, 0), Point(0, 1)) - >>> t2 = Triangle(Point(0, 0), Point(2, 0), Point(0, 2)) - >>> t3 = Triangle(Point(0, 0), Point(3, 0), Point(0, 1)) - >>> are_similar(t1, t2) - True - >>> are_similar(t1, t3) - False - - """ - from .exceptions import GeometryError - - if e1 == e2: - return True - try: - return e1.is_similar(e2) - except AttributeError: - try: - return e2.is_similar(e1) - except AttributeError: - n1 = e1.__class__.__name__ - n2 = e2.__class__.__name__ - raise GeometryError( - "Cannot test similarity between %s and %s" % (n1, n2)) - -
    -
    [docs]def centroid(*args): - """Find the centroid (center of mass) of the collection containing only Points, - Segments or Polygons. The centroid is the weighted average of the individual centroid - where the weights are the lengths (of segments) or areas (of polygons). - Overlapping regions will add to the weight of that region. - - If there are no objects (or a mixture of objects) then None is returned. - - See Also - ======== - - sympy.geometry.point.Point, sympy.geometry.line.Segment, - sympy.geometry.polygon.Polygon - - Examples - ======== - - >>> from sympy import Point, Segment, Polygon - >>> from sympy.geometry.util import centroid - >>> p = Polygon((0, 0), (10, 0), (10, 10)) - >>> q = p.translate(0, 20) - >>> p.centroid, q.centroid - (Point(20/3, 10/3), Point(20/3, 70/3)) - >>> centroid(p, q) - Point(20/3, 40/3) - >>> p, q = Segment((0, 0), (2, 0)), Segment((0, 0), (2, 2)) - >>> centroid(p, q) - Point(1, -sqrt(2) + 2) - >>> centroid(Point(0, 0), Point(2, 0)) - Point(1, 0) - - Stacking 3 polygons on top of each other effectively triples the - weight of that polygon: - - >>> p = Polygon((0, 0), (1, 0), (1, 1), (0, 1)) - >>> q = Polygon((1, 0), (3, 0), (3, 1), (1, 1)) - >>> centroid(p, q) - Point(3/2, 1/2) - >>> centroid(p, p, p, q) # centroid x-coord shifts left - Point(11/10, 1/2) - - Stacking the squares vertically above and below p has the same - effect: - - >>> centroid(p, p.translate(0, 1), p.translate(0, -1), q) - Point(11/10, 1/2) - - """ - - from sympy.geometry import Polygon, Segment, Point - if args: - if all(isinstance(g, Point) for g in args): - c = Point(0, 0) - for g in args: - c += g - return c/len(args) - elif all(isinstance(g, Segment) for g in args): - c = Point(0, 0) - L = 0 - for g in args: - l = g.length - c += g.midpoint*l - L += l - return c/L - elif all(isinstance(g, Polygon) for g in args): - c = Point(0, 0) - A = 0 - for g in args: - a = g.area - c += g.centroid*a - A += a - return c/A
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/integrals.html b/dev-py3k/_modules/sympy/integrals.html deleted file mode 100644 index 76200aefaef..00000000000 --- a/dev-py3k/_modules/sympy/integrals.html +++ /dev/null @@ -1,141 +0,0 @@ - - - - - - - - - - sympy.integrals — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.integrals

    -"""Integration functions that integrates a sympy expression.
    -
    -    Examples
    -    --------
    -    >>> from sympy import integrate, sin
    -    >>> from sympy.abc import x
    -    >>> integrate(1/x,x)
    -    log(x)
    -    >>> integrate(sin(x),x)
    -    -cos(x)
    -"""
    -from .integrals import integrate, Integral, line_integrate
    -from .transforms import (mellin_transform, inverse_mellin_transform,
    -                        MellinTransform, InverseMellinTransform,
    -                        laplace_transform, inverse_laplace_transform,
    -                        LaplaceTransform, InverseLaplaceTransform,
    -                        fourier_transform, inverse_fourier_transform,
    -                        FourierTransform, InverseFourierTransform,
    -                        sine_transform, inverse_sine_transform,
    -                        SineTransform, InverseSineTransform,
    -                        cosine_transform, inverse_cosine_transform,
    -                        CosineTransform, InverseCosineTransform,
    -                        hankel_transform, inverse_hankel_transform,
    -                        HankelTransform, InverseHankelTransform)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/integrals/transforms.html b/dev-py3k/_modules/sympy/integrals/transforms.html deleted file mode 100644 index 5e156fbff62..00000000000 --- a/dev-py3k/_modules/sympy/integrals/transforms.html +++ /dev/null @@ -1,1938 +0,0 @@ - - - - - - - - - - sympy.integrals.transforms — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.integrals.transforms

    -""" Integral Transforms """
    -from sympy.integrals import integrate, Integral
    -from sympy.core.numbers import oo
    -from sympy.core.symbol import Dummy
    -from sympy.core.function import Function
    -from sympy.logic.boolalg import to_cnf, conjuncts, disjuncts, Or, And
    -from sympy.simplify import simplify
    -from sympy.core import S
    -
    -from sympy.integrals.meijerint import _dummy
    -from functools import reduce
    -
    -##########################################################################
    -# Helpers / Utilities
    -##########################################################################
    -
    -
    -class IntegralTransformError(NotImplementedError):
    -    """
    -    Exception raised in relation to problems computing transforms.
    -
    -    This class is mostly used internally; if integrals cannot be computed
    -    objects representing unevaluated transforms are usually returned.
    -
    -    The hint ``needeval=True`` can be used to disable returning transform
    -    objects, and instead raise this exception if an integral cannot be
    -    computed.
    -    """
    -    def __init__(self, transform, function, msg):
    -        super(IntegralTransformError, self).__init__(
    -            "%s Transform could not be computed: %s." % (transform, msg))
    -        self.function = function
    -
    -
    -class IntegralTransform(Function):
    -    """
    -    Base class for integral transforms.
    -
    -    This class represents unevaluated transforms.
    -
    -    To implement a concrete transform, derive from this class and implement
    -    the _compute_transform(f, x, s, **hints) and _as_integral(f, x, s)
    -    functions. If the transform cannot be computed, raise IntegralTransformError.
    -
    -    Also set cls._name.
    -
    -    Implement self._collapse_extra if your function returns more than just a
    -    number and possibly a convergence condition.
    -    """
    -
    -    nargs = 3
    -
    -    @property
    -    def function(self):
    -        """ The function to be transformed. """
    -        return self.args[0]
    -
    -    @property
    -    def function_variable(self):
    -        """ The dependent variable of the function to be transformed. """
    -        return self.args[1]
    -
    -    @property
    -    def transform_variable(self):
    -        """ The independent transform variable. """
    -        return self.args[2]
    -
    -    @property
    -    def free_symbols(self):
    -        """
    -        This method returns the symbols that will exist when the transform
    -        is evaluated.
    -        """
    -        return self.function.free_symbols.union(set([self.transform_variable])) \
    -            - set([self.function_variable])
    -
    -    def _compute_transform(self, f, x, s, **hints):
    -        raise NotImplementedError
    -
    -    def _as_integral(self, f, x, s):
    -        raise NotImplementedError
    -
    -    def _collapse_extra(self, extra):
    -        from sympy import And
    -        cond = And(*extra)
    -        if cond is False:
    -            raise IntegralTransformError(self.__class__.name, None, '')
    -
    -    def doit(self, **hints):
    -        """
    -        Try to evaluate the transform in closed form.
    -
    -        This general function handles linearity, but apart from that leaves
    -        pretty much everything to _compute_transform.
    -
    -        Standard hints are the following:
    -
    -        - ``simplify``: whether or not to simplify the result
    -        - ``noconds``: if True, don't return convergence conditions
    -        - ``needeval``: if True, raise IntegralTransformError instead of
    -                        returning IntegralTransform objects
    -
    -        The default values of these hints depend on the concrete transform,
    -        usually the default is
    -        ``(simplify, noconds, needeval) = (True, False, False)``.
    -        """
    -        from sympy import Add, expand_mul, Mul
    -        from sympy.core.function import AppliedUndef
    -        needeval = hints.pop('needeval', False)
    -        try_directly = not any(func.has(self.function_variable)
    -                               for func in self.function.atoms(AppliedUndef))
    -        if try_directly:
    -            try:
    -                return self._compute_transform(self.function,
    -                    self.function_variable, self.transform_variable, **hints)
    -            except IntegralTransformError:
    -                pass
    -
    -        fn = self.function
    -        if not fn.is_Add:
    -            fn = expand_mul(fn)
    -
    -        if fn.is_Add:
    -            hints['needeval'] = needeval
    -            res = [self.__class__(*([x] + list(self.args[1:]))).doit(**hints)
    -                   for x in fn.args]
    -            extra = []
    -            ress = []
    -            for x in res:
    -                if not isinstance(x, tuple):
    -                    x = [x]
    -                ress.append(x[0])
    -                if len(x) > 1:
    -                    extra += [x[1:]]
    -            res = Add(*ress)
    -            if not extra:
    -                return res
    -            try:
    -                extra = self._collapse_extra(extra)
    -                return tuple([res]) + tuple(extra)
    -            except IntegralTransformError:
    -                pass
    -
    -        if needeval:
    -            raise IntegralTransformError(
    -                self.__class__._name, self.function, 'needeval')
    -
    -        # TODO handle derivatives etc
    -
    -        # pull out constant coefficients
    -        coeff, rest = fn.as_coeff_mul(self.function_variable)
    -        return coeff*self.__class__(*([Mul(*rest)] + list(self.args[1:])))
    -
    -    @property
    -    def as_integral(self):
    -        return self._as_integral(self.function, self.function_variable,
    -                                 self.transform_variable)
    -
    -    def _eval_rewrite_as_Integral(self, *args):
    -        return self.as_integral
    -
    -from sympy.solvers.inequalities import _solve_inequality
    -
    -
    -def _simplify(expr, doit):
    -    from sympy import powdenest, piecewise_fold
    -    if doit:
    -        return simplify(powdenest(piecewise_fold(expr), polar=True))
    -    return expr
    -
    -
    -def _noconds_(default):
    -    """
    -    This is a decorator generator for dropping convergence conditions.
    -
    -    Suppose you define a function ``transform(*args)`` which returns a tuple of
    -    the form ``(result, cond1, cond2, ...)``.
    -
    -    Decorating it ``@_noconds_(default)`` will add a new keyword argument
    -    ``noconds`` to it. If ``noconds=True``, the return value will be altered to
    -    be only ``result``, whereas if ``noconds=False`` the return value will not
    -    be altered.
    -
    -    The default value of the ``noconds`` keyword will be ``default`` (i.e. the
    -    argument of this function).
    -    """
    -    def make_wrapper(func):
    -        from sympy.core.decorators import wraps
    -
    -        @wraps(func)
    -        def wrapper(*args, **kwargs):
    -            noconds = kwargs.pop('noconds', default)
    -            res = func(*args, **kwargs)
    -            if noconds:
    -                return res[0]
    -            return res
    -        return wrapper
    -    return make_wrapper
    -_noconds = _noconds_(False)
    -
    -
    -##########################################################################
    -# Mellin Transform
    -##########################################################################
    -
    -def _default_integrator(f, x):
    -    return integrate(f, (x, 0, oo))
    -
    -
    -@_noconds
    -def _mellin_transform(f, x, s_, integrator=_default_integrator, simplify=True):
    -    """ Backend function to compute Mellin transforms. """
    -    from sympy import re, Max, Min, count_ops
    -    # We use a fresh dummy, because assumptions on s might drop conditions on
    -    # convergence of the integral.
    -    s = _dummy('s', 'mellin-transform', f)
    -    F = integrator(x**(s - 1) * f, x)
    -
    -    if not F.has(Integral):
    -        return _simplify(F.subs(s, s_), simplify), (-oo, oo), True
    -
    -    if not F.is_Piecewise:
    -        raise IntegralTransformError('Mellin', f, 'could not compute integral')
    -
    -    F, cond = F.args[0]
    -    if F.has(Integral):
    -        raise IntegralTransformError(
    -            'Mellin', f, 'integral in unexpected form')
    -
    -    def process_conds(cond):
    -        """
    -        Turn ``cond`` into a strip (a, b), and auxiliary conditions.
    -        """
    -        a = -oo
    -        b = oo
    -        aux = True
    -        conds = conjuncts(to_cnf(cond))
    -        t = Dummy('t', real=True)
    -        for c in conds:
    -            a_ = oo
    -            b_ = -oo
    -            aux_ = []
    -            for d in disjuncts(c):
    -                d_ = d.replace(
    -                    re, lambda x: x.as_real_imag()[0]).subs(re(s), t)
    -                if not d.is_Relational or \
    -                    d.rel_op not in ('>', '>=', '<', '<=') \
    -                        or d_.has(s) or not d_.has(t):
    -                    aux_ += [d]
    -                    continue
    -                soln = _solve_inequality(d_, t)
    -                if not soln.is_Relational or \
    -                        soln.rel_op not in ('>', '>=', '<', '<='):
    -                    aux_ += [d]
    -                    continue
    -                if soln.lts == t:
    -                    b_ = Max(soln.gts, b_)
    -                else:
    -                    a_ = Min(soln.lts, a_)
    -            if a_ != oo and a_ != b:
    -                a = Max(a_, a)
    -            elif b_ != -oo and b_ != a:
    -                b = Min(b_, b)
    -            else:
    -                aux = And(aux, Or(*aux_))
    -        return a, b, aux
    -
    -    conds = [process_conds(c) for c in disjuncts(cond)]
    -    conds = [x for x in conds if x[2] is not False]
    -    conds.sort(key=lambda x: (x[0] - x[1], count_ops(x[2])))
    -
    -    if not conds:
    -        raise IntegralTransformError('Mellin', f, 'no convergence found')
    -
    -    a, b, aux = conds[0]
    -    return _simplify(F.subs(s, s_), simplify), (a, b), aux
    -
    -
    -class MellinTransform(IntegralTransform):
    -    """
    -    Class representing unevaluated Mellin transforms.
    -
    -    For usage of this class, see the :class:`IntegralTransform` docstring.
    -
    -    For how to compute Mellin transforms, see the :func:`mellin_transform`
    -    docstring.
    -    """
    -
    -    _name = 'Mellin'
    -
    -    def _compute_transform(self, f, x, s, **hints):
    -        return _mellin_transform(f, x, s, **hints)
    -
    -    def _as_integral(self, f, x, s):
    -        from sympy import Integral
    -        return Integral(f*x**(s - 1), (x, 0, oo))
    -
    -    def _collapse_extra(self, extra):
    -        from sympy import And, Max, Min
    -        a = []
    -        b = []
    -        cond = []
    -        for (sa, sb), c in extra:
    -            a += [sa]
    -            b += [sb]
    -            cond += [c]
    -        res = (Max(*a), Min(*b)), And(*cond)
    -        if (res[0][0] >= res[0][1]) is True or res[1] is False:
    -            raise IntegralTransformError(
    -                'Mellin', None, 'no combined convergence.')
    -        return res
    -
    -
    -
    [docs]def mellin_transform(f, x, s, **hints): - r""" - Compute the Mellin transform `F(s)` of `f(x)`, - - .. math :: F(s) = \int_0^\infty x^{s-1} f(x) \mathrm{d}x. - - For all "sensible" functions, this converges absolutely in a strip - `a < Re(s) < b`. - - The Mellin transform is related via change of variables to the Fourier - transform, and also to the (bilateral) Laplace transform. - - This function returns (F, (a, b), cond) - where `F` is the Mellin transform of `f`, `(a, b)` is the fundamental strip - (as above), and cond are auxiliary convergence conditions. - - If the integral cannot be computed in closed form, this function returns - an unevaluated MellinTransform object. - - For a description of possible hints, refer to the docstring of - :func:`sympy.integrals.transforms.IntegralTransform.doit`. If ``noconds=False``, - then only `F` will be returned (i.e. not ``cond``, and also not the strip - ``(a, b)``). - - >>> from sympy.integrals.transforms import mellin_transform - >>> from sympy import exp - >>> from sympy.abc import x, s - >>> mellin_transform(exp(-x), x, s) - (gamma(s), (0, oo), True) - - See Also - ======== - - inverse_mellin_transform, laplace_transform, fourier_transform - hankel_transform, inverse_hankel_transform - """ - return MellinTransform(f, x, s).doit(**hints) - -
    -def _rewrite_sin(xxx_todo_changeme, s, a, b): - """ - Re-write the sine function sin(m*s + n) as gamma functions, compatible - with the strip (a, b). - - Return (gamma1, gamma2, fac) so that f == fac/(gamma1 * gamma2). - - >>> from sympy.integrals.transforms import _rewrite_sin - >>> from sympy import pi, S - >>> from sympy.abc import s - >>> _rewrite_sin((pi, 0), s, 0, 1) - (gamma(s), gamma(-s + 1), pi) - >>> _rewrite_sin((pi, 0), s, 1, 0) - (gamma(s - 1), gamma(-s + 2), -pi) - >>> _rewrite_sin((pi, 0), s, -1, 0) - (gamma(s + 1), gamma(-s), -pi) - >>> _rewrite_sin((pi, pi/2), s, S(1)/2, S(3)/2) - (gamma(s - 1/2), gamma(-s + 3/2), -pi) - >>> _rewrite_sin((pi, pi), s, 0, 1) - (gamma(s), gamma(-s + 1), -pi) - >>> _rewrite_sin((2*pi, 0), s, 0, S(1)/2) - (gamma(2*s), gamma(-2*s + 1), pi) - >>> _rewrite_sin((2*pi, 0), s, S(1)/2, 1) - (gamma(2*s - 1), gamma(-2*s + 2), -pi) - """ - (m, n) = xxx_todo_changeme - from sympy import expand_mul, pi, ceiling, gamma, re - m = expand_mul(m/pi) - n = expand_mul(n/pi) - r = ceiling(-m*a - n.as_real_imag()[0]) # Don't use re(n), does not expand - return gamma(m*s + n + r), gamma(1 - n - r - m*s), (-1)**r*pi - - -class MellinTransformStripError(ValueError): - """ - Exception raised by _rewrite_gamma. Mainly for internal use. - """ - pass - - -def _rewrite_gamma(f, s, a, b): - """ - Try to rewrite the product f(s) as a product of gamma functions, - so that the inverse Mellin transform of f can be expressed as a meijer - G function. - - Return (an, ap), (bm, bq), arg, exp, fac such that - G((an, ap), (bm, bq), arg/z**exp)*fac is the inverse Mellin transform of f(s). - - Raises IntegralTransformError or MellinTransformStripError on failure. - - It is asserted that f has no poles in the fundamental strip designated by - (a, b). One of a and b is allowed to be None. The fundamental strip is - important, because it determines the inversion contour. - - This function can handle exponentials, linear factors, trigonometric - functions. - - This is a helper function for inverse_mellin_transform that will not - attempt any transformations on f. - - >>> from sympy.integrals.transforms import _rewrite_gamma - >>> from sympy.abc import s - >>> from sympy import oo - >>> _rewrite_gamma(s*(s+3)*(s-1), s, -oo, oo) - (([], [-3, 0, 1]), ([-2, 1, 2], []), 1, 1, -1) - >>> _rewrite_gamma((s-1)**2, s, -oo, oo) - (([], [1, 1]), ([2, 2], []), 1, 1, 1) - - Importance of the fundamental strip: - - >>> _rewrite_gamma(1/s, s, 0, oo) - (([1], []), ([], [0]), 1, 1, 1) - >>> _rewrite_gamma(1/s, s, None, oo) - (([1], []), ([], [0]), 1, 1, 1) - >>> _rewrite_gamma(1/s, s, 0, None) - (([1], []), ([], [0]), 1, 1, 1) - >>> _rewrite_gamma(1/s, s, -oo, 0) - (([], [1]), ([0], []), 1, 1, -1) - >>> _rewrite_gamma(1/s, s, None, 0) - (([], [1]), ([0], []), 1, 1, -1) - >>> _rewrite_gamma(1/s, s, -oo, None) - (([], [1]), ([0], []), 1, 1, -1) - - >>> _rewrite_gamma(2**(-s+3), s, -oo, oo) - (([], []), ([], []), 1/2, 1, 8) - """ - from itertools import repeat - from sympy import (Poly, gamma, Mul, re, RootOf, exp as exp_, E, expand, - roots, ilcm, pi, sin, cos, tan, cot, igcd, exp_polar) - # Our strategy will be as follows: - # 1) Guess a constant c such that the inversion integral should be - # performed wrt s'=c*s (instead of plain s). Write s for s'. - # 2) Process all factors, rewrite them independently as gamma functions in - # argument s, or exponentials of s. - # 3) Try to transform all gamma functions s.t. they have argument - # a+s or a-s. - # 4) Check that the resulting G function parameters are valid. - # 5) Combine all the exponentials. - - a_, b_ = S([a, b]) - - def left(c, is_numer): - """ - Decide whether pole at c lies to the left of the fundamental strip. - """ - # heuristically, this is the best chance for us to solve the inequalities - c = expand(re(c)) - if a_ is None: - return c < b_ - if b_ is None: - return c <= a_ - if (c >= b_) is True: - return False - if (c <= a_) is True: - return True - if is_numer: - return None - if a_.free_symbols or b_.free_symbols or c.free_symbols: - return None # XXX - #raise IntegralTransformError('Inverse Mellin', f, - # 'Could not determine position of singularity %s' - # ' relative to fundamental strip' % c) - raise MellinTransformStripError('Pole inside critical strip?') - - # 1) - s_multipliers = [] - for g in f.atoms(gamma): - if not g.has(s): - continue - arg = g.args[0] - if arg.is_Add: - arg = arg.as_independent(s)[1] - coeff, _ = arg.as_coeff_mul(s) - s_multipliers += [coeff] - for g in f.atoms(sin, cos, tan, cot): - if not g.has(s): - continue - arg = g.args[0] - if arg.is_Add: - arg = arg.as_independent(s)[1] - coeff, _ = arg.as_coeff_mul(s) - s_multipliers += [coeff/pi] - s_multipliers = [abs(x) for x in s_multipliers if x.is_real] - common_coefficient = S(1) - for x in s_multipliers: - if not x.is_Rational: - common_coefficient = x - break - s_multipliers = [x/common_coefficient for x in s_multipliers] - if any(not x.is_Rational for x in s_multipliers): - raise NotImplementedError - s_multiplier = common_coefficient/reduce(ilcm, [S(x.q) - for x in s_multipliers], S(1)) - if s_multiplier == common_coefficient: - if len(s_multipliers) == 0: - s_multiplier = common_coefficient - else: - s_multiplier = common_coefficient \ - *reduce(igcd, [S(x.p) for x in s_multipliers]) - - exponent = S(1) - fac = S(1) - f = f.subs(s, s/s_multiplier) - fac /= s_multiplier - exponent = 1/s_multiplier - if a_ is not None: - a_ *= s_multiplier - if b_ is not None: - b_ *= s_multiplier - - # 2) - numer, denom = f.as_numer_denom() - numer = Mul.make_args(numer) - denom = Mul.make_args(denom) - args = list(zip(numer, repeat(True))) + list(zip(denom, repeat(False))) - - facs = [] - dfacs = [] - # *_gammas will contain pairs (a, c) representing Gamma(a*s + c) - numer_gammas = [] - denom_gammas = [] - # exponentials will contain bases for exponentials of s - exponentials = [] - - def exception(fact): - return IntegralTransformError("Inverse Mellin", f, "Unrecognised form '%s'." % fact) - while args: - fact, is_numer = args.pop() - if is_numer: - ugammas, lgammas = numer_gammas, denom_gammas - ufacs, lfacs = facs, dfacs - else: - ugammas, lgammas = denom_gammas, numer_gammas - ufacs, lfacs = dfacs, facs - - def linear_arg(arg): - """ Test if arg is of form a*s+b, raise exception if not. """ - if not arg.is_polynomial(s): - raise exception(fact) - p = Poly(arg, s) - if p.degree() != 1: - raise exception(fact) - return p.all_coeffs() - - # constants - if not fact.has(s): - ufacs += [fact] - # exponentials - elif fact.is_Pow or isinstance(fact, exp_): - if fact.is_Pow: - base = fact.base - exp = fact.exp - else: - base = exp_polar(1) - exp = fact.args[0] - if exp.is_Integer: - cond = is_numer - if exp < 0: - cond = not cond - args += [(base, cond)]*abs(exp) - continue - elif not base.has(s): - a, b = linear_arg(exp) - if not is_numer: - base = 1/base - exponentials += [base**a] - facs += [base**b] - else: - raise exception(fact) - # linear factors - elif fact.is_polynomial(s): - p = Poly(fact, s) - if p.degree() != 1: - # We completely factor the poly. For this we need the roots. - # Now roots() only works in some cases (low degree), and RootOf - # only works without parameters. So try both... - coeff = p.LT()[1] - rs = roots(p, s) - if len(rs) != p.degree(): - rs = RootOf.all_roots(p) - ufacs += [coeff] - args += [(s - c, is_numer) for c in rs] - continue - a, c = p.all_coeffs() - ufacs += [a] - c /= -a - # Now need to convert s - c - if left(c, is_numer): - ugammas += [(S(1), -c + 1)] - lgammas += [(S(1), -c)] - else: - ufacs += [-1] - ugammas += [(S(-1), c + 1)] - lgammas += [(S(-1), c)] - elif isinstance(fact, gamma): - a, b = linear_arg(fact.args[0]) - if is_numer: - if (a > 0 and (left(-b/a, is_numer) is False)) or \ - (a < 0 and (left(-b/a, is_numer) is True)): - raise NotImplementedError( - 'Gammas partially over the strip.') - ugammas += [(a, b)] - elif isinstance(fact, sin): - # We try to re-write all trigs as gammas. This is not in - # general the best strategy, since sometimes this is impossible, - # but rewriting as exponentials would work. However trig functions - # in inverse mellin transforms usually all come from simplifying - # gamma terms, so this should work. - a = fact.args[0] - if is_numer: - # No problem with the poles. - gamma1, gamma2, fac_ = gamma(a/pi), gamma(1 - a/pi), pi - else: - gamma1, gamma2, fac_ = _rewrite_sin(linear_arg(a), s, a_, b_) - args += [(gamma1, not is_numer), (gamma2, not is_numer)] - ufacs += [fac_] - elif isinstance(fact, tan): - a = fact.args[0] - args += [(sin(a, evaluate=False), is_numer), - (sin(pi/2 - a, evaluate=False), not is_numer)] - elif isinstance(fact, cos): - a = fact.args[0] - args += [(sin(pi/2 - a, evaluate=False), is_numer)] - elif isinstance(fact, cot): - a = fact.args[0] - args += [(sin(pi/2 - a, evaluate=False), is_numer), - (sin(a, evaluate=False), not is_numer)] - else: - raise exception(fact) - - fac *= Mul(*facs)/Mul(*dfacs) - - # 3) - an, ap, bm, bq = [], [], [], [] - for gammas, plus, minus, is_numer in [(numer_gammas, an, bm, True), - (denom_gammas, bq, ap, False)]: - while gammas: - a, c = gammas.pop() - if a != -1 and a != +1: - # We use the gamma function multiplication theorem. - p = abs(S(a)) - newa = a/p - newc = c/p - assert a.is_Integer - for k in range(p): - gammas += [(newa, newc + k/p)] - if is_numer: - fac *= (2*pi)**((1 - p)/2) * p**(c - S(1)/2) - exponentials += [p**a] - else: - fac /= (2*pi)**((1 - p)/2) * p**(c - S(1)/2) - exponentials += [p**(-a)] - continue - if a == +1: - plus.append(1 - c) - else: - minus.append(c) - - # 4) - # TODO - - # 5) - arg = Mul(*exponentials) - - # for testability, sort the arguments - an.sort() - ap.sort() - bm.sort() - bq.sort() - - return (an, ap), (bm, bq), arg, exponent, fac - - -@_noconds_(True) -def _inverse_mellin_transform(F, s, x_, strip, as_meijerg=False): - """ A helper for the real inverse_mellin_transform function, this one here - assumes x to be real and positive. """ - from sympy import (expand, expand_mul, hyperexpand, meijerg, And, Or, - arg, pi, re, factor, Heaviside, gamma, Add) - x = _dummy('t', 'inverse-mellin-transform', F, positive=True) - # Actually, we won't try integration at all. Instead we use the definition - # of the Meijer G function as a fairly general inverse mellin transform. - F = F.rewrite(gamma) - for g in [factor(F), expand_mul(F), expand(F)]: - if g.is_Add: - # do all terms separately - ress = [_inverse_mellin_transform(G, s, x, strip, as_meijerg, - noconds=False) - for G in g.args] - conds = [p[1] for p in ress] - ress = [p[0] for p in ress] - res = Add(*ress) - if not as_meijerg: - res = factor(res, gens=res.atoms(Heaviside)) - return res.subs(x, x_), And(*conds) - - try: - a, b, C, e, fac = _rewrite_gamma(g, s, strip[0], strip[1]) - except IntegralTransformError: - continue - G = meijerg(a, b, C/x**e) - if as_meijerg: - h = G - else: - h = hyperexpand(G) - if h.is_Piecewise and len(h.args) == 3: - # XXX we break modularity here! - h = Heaviside(x - abs(C))*h.args[0].args[0] \ - + Heaviside(abs(C) - x)*h.args[1].args[0] - # We must ensure that the intgral along the line we want converges, - # and return that value. - # See [L], 5.2 - cond = [abs(arg(G.argument)) < G.delta*pi] - # Note: we allow ">=" here, this corresponds to convergence if we let - # limits go to oo symetrically. ">" corresponds to absolute convergence. - cond += [And(Or(len(G.ap) != len(G.bq), 0 >= re(G.nu) + 1), - abs(arg(G.argument)) == G.delta*pi)] - cond = Or(*cond) - if cond is False: - raise IntegralTransformError( - 'Inverse Mellin', F, 'does not converge') - return (h*fac).subs(x, x_), cond - - raise IntegralTransformError('Inverse Mellin', F, '') - -_allowed = None - - -class InverseMellinTransform(IntegralTransform): - """ - Class representing unevaluated inverse Mellin transforms. - - For usage of this class, see the :class:`IntegralTransform` docstring. - - For how to compute inverse Mellin transforms, see the - :func:`inverse_mellin_transform` docstring. - """ - - nargs = 5 - - _name = 'Inverse Mellin' - _none_sentinel = Dummy('None') - _c = Dummy('c') - - def __new__(cls, F, s, x, a, b, **opts): - if a is None: - a = InverseMellinTransform._none_sentinel - if b is None: - b = InverseMellinTransform._none_sentinel - return IntegralTransform.__new__(cls, F, s, x, a, b, **opts) - - @property - def fundamental_strip(self): - a, b = self.args[3], self.args[4] - if a is InverseMellinTransform._none_sentinel: - a = None - if b is InverseMellinTransform._none_sentinel: - b = None - return a, b - - def _compute_transform(self, F, s, x, **hints): - from sympy import postorder_traversal - global _allowed - if _allowed is None: - from sympy import ( - exp, gamma, sin, cos, tan, cot, cosh, sinh, tanh, - coth, factorial, rf) - _allowed = set( - [exp, gamma, sin, cos, tan, cot, cosh, sinh, tanh, coth, - factorial, rf]) - for f in postorder_traversal(F): - if f.is_Function and f.has(s) and f.func not in _allowed: - raise IntegralTransformError('Inverse Mellin', F, - 'Component %s not recognised.' % f) - strip = self.fundamental_strip - return _inverse_mellin_transform(F, s, x, strip, **hints) - - def _as_integral(self, F, s, x): - from sympy import Integral, I, oo - c = self.__class__._c - return Integral(F*x**(-s), (s, c - I*oo, c + I*oo)) - - -
    [docs]def inverse_mellin_transform(F, s, x, strip, **hints): - r""" - Compute the inverse Mellin transform of `F(s)` over the fundamental - strip given by ``strip=(a, b)``. - - This can be defined as - - .. math:: f(x) = \int_{c - i\infty}^{c + i\infty} x^{-s} F(s) \mathrm{d}s, - - for any `c` in the fundamental strip. Under certain regularity - conditions on `F` and/or `f`, - this recovers `f` from its Mellin transform `F` - (and vice versa), for positive real `x`. - - One of `a` or `b` may be passed as None; a suitable `c` will be - inferred. - - If the integral cannot be computed in closed form, this function returns - an unevaluated InverseMellinTransform object. - - Note that this function will assume x to be positive and real, regardless - of the sympy assumptions! - - For a description of possible hints, refer to the docstring of - :func:`sympy.integrals.transforms.IntegralTransform.doit`. - - >>> from sympy.integrals.transforms import inverse_mellin_transform - >>> from sympy import oo, gamma - >>> from sympy.abc import x, s - >>> inverse_mellin_transform(gamma(s), s, x, (0, oo)) - exp(-x) - - The fundamental strip matters: - - >>> f = 1/(s**2 - 1) - >>> inverse_mellin_transform(f, s, x, (-oo, -1)) - x*(1 - 1/x**2)*Heaviside(x - 1)/2 - >>> inverse_mellin_transform(f, s, x, (-1, 1)) - -x*Heaviside(-x + 1)/2 - Heaviside(x - 1)/(2*x) - >>> inverse_mellin_transform(f, s, x, (1, oo)) - (-x**2/2 + 1/2)*Heaviside(-x + 1)/x - - See Also - ======== - - mellin_transform - hankel_transform, inverse_hankel_transform - """ - return InverseMellinTransform(F, s, x, strip[0], strip[1]).doit(**hints) - - -########################################################################## -# Laplace Transform -########################################################################## -
    -def _simplifyconds(expr, s, a): - """ - Naively simplify some conditions occuring in ``expr``, given that Re(s) > a. - - >>> from sympy.integrals.transforms import _simplifyconds as simp - >>> from sympy.abc import x - >>> from sympy import sympify as S - >>> simp(abs(x**2) < 1, x, 1) - False - >>> simp(abs(x**2) < 1, x, 2) - False - >>> simp(abs(x**2) < 1, x, 0) - Abs(x**2) < 1 - >>> simp(abs(1/x**2) < 1, x, 1) - True - >>> simp(S(1) < abs(x), x, 1) - True - >>> simp(S(1) < abs(1/x), x, 1) - False - - >>> from sympy import Ne - >>> simp(Ne(1, x**3), x, 1) - True - >>> simp(Ne(1, x**3), x, 2) - True - >>> simp(Ne(1, x**3), x, 0) - 1 != x**3 - """ - from sympy.core.relational import ( StrictGreaterThan, StrictLessThan, - Unequality ) - from sympy import Abs - - def power(ex): - if ex == s: - return 1 - if ex.is_Pow and ex.base == s: - return ex.exp - return None - - def bigger(ex1, ex2): - """ Return True only if |ex1| > |ex2|, False only if |ex1| < |ex2|. - Else return None. """ - if ex1.has(s) and ex2.has(s): - return None - if ex1.func is Abs: - ex1 = ex1.args[0] - if ex2.func is Abs: - ex2 = ex2.args[0] - if ex1.has(s): - return bigger(1/ex2, 1/ex1) - n = power(ex2) - if n is None: - return None - if n > 0 and (abs(ex1) <= abs(a)**n) is True: - return False - if n < 0 and (abs(ex1) >= abs(a)**n) is True: - return True - - def replie(x, y): - """ simplify x < y """ - if not (x.is_positive or x.func is Abs) \ - or not (y.is_positive or y.func is Abs): - return (x < y) - r = bigger(x, y) - if r is not None: - return not r - return (x < y) - - def replue(x, y): - if bigger(x, y) in (True, False): - return True - return Unequality(x, y) - - def repl(ex, *args): - if isinstance(ex, bool): - return ex - return ex.replace(*args) - expr = repl(expr, StrictLessThan, replie) - expr = repl(expr, StrictGreaterThan, lambda x, y: replie(y, x)) - expr = repl(expr, Unequality, replue) - return expr - - -@_noconds -def _laplace_transform(f, t, s_, simplify=True): - """ The backend function for Laplace transforms. """ - from sympy import (re, Max, exp, pi, Abs, Min, periodic_argument as arg, - cos, Wild, symbols, polar_lift) - s = Dummy('s') - F = integrate(exp(-s*t) * f, (t, 0, oo)) - - if not F.has(Integral): - return _simplify(F.subs(s, s_), simplify), -oo, True - - if not F.is_Piecewise: - raise IntegralTransformError( - 'Laplace', f, 'could not compute integral') - - F, cond = F.args[0] - if F.has(Integral): - raise IntegralTransformError( - 'Laplace', f, 'integral in unexpected form') - - def process_conds(conds): - """ Turn ``conds`` into a strip and auxiliary conditions. """ - a = -oo - aux = True - conds = conjuncts(to_cnf(conds)) - u = Dummy('u', real=True) - p, q, w1, w2, w3, w4, w5 = symbols( - 'p q w1 w2 w3 w4 w5', cls=Wild, exclude=[s]) - for c in conds: - a_ = oo - aux_ = [] - for d in disjuncts(c): - m = d.match(abs(arg((s + w3)**p*q, w1)) < w2) - if not m: - m = d.match(abs(arg((s + w3)**p*q, w1)) <= w2) - if not m: - m = d.match(abs(arg((polar_lift(s + w3))**p*q, w1)) < w2) - if not m: - m = d.match(abs(arg((polar_lift(s + w3))**p*q, w1)) <= w2) - if m: - if m[q] > 0 and m[w2]/m[p] == pi/2: - d = re(s + m[w3]) > 0 - m = d.match( - 0 < cos(abs(arg(s**w1*w5, q))*w2)*abs(s**w3)**w4 - p) - if not m: - m = d.match(0 < cos(abs( - arg(polar_lift(s)**w1*w5, q))*w2)*abs(s**w3)**w4 - p) - if m and all(m[wild] > 0 for wild in [w1, w2, w3, w4, w5]): - d = re(s) > m[p] - d_ = d.replace( - re, lambda x: x.expand().as_real_imag()[0]).subs(re(s), t) - if not d.is_Relational or \ - d.rel_op not in ('>', '>=', '<', '<=') \ - or d_.has(s) or not d_.has(t): - aux_ += [d] - continue - soln = _solve_inequality(d_, t) - if not soln.is_Relational or \ - soln.rel_op not in ('>', '>=', '<', '<='): - aux_ += [d] - continue - if soln.lts == t: - raise IntegralTransformError('Laplace', f, - 'convergence not in half-plane?') - else: - a_ = Min(soln.lts, a_) - if a_ != oo: - a = Max(a_, a) - else: - aux = And(aux, Or(*aux_)) - return a, aux - - conds = [process_conds(c) for c in disjuncts(cond)] - conds2 = [x for x in conds if x[1] is not False and x[0] != -oo] - if not conds2: - conds2 = [x for x in conds if x[1] is not False] - conds = conds2 - - def cnt(expr): - if isinstance(expr, bool): - return 0 - return expr.count_ops() - conds.sort(key=lambda x: (-x[0], cnt(x[1]))) - - if not conds: - raise IntegralTransformError('Laplace', f, 'no convergence found') - a, aux = conds[0] - - def sbs(expr): - if isinstance(expr, bool): - return expr - return expr.subs(s, s_) - if simplify: - F = _simplifyconds(F, s, a) - aux = _simplifyconds(aux, s, a) - return _simplify(F.subs(s, s_), simplify), sbs(a), sbs(aux) - - -class LaplaceTransform(IntegralTransform): - """ - Class representing unevaluated Laplace transforms. - - For usage of this class, see the :class:`IntegralTransform` docstring. - - For how to compute Laplace transforms, see the :func:`laplace_transform` - docstring. - """ - - _name = 'Laplace' - - def _compute_transform(self, f, t, s, **hints): - return _laplace_transform(f, t, s, **hints) - - def _as_integral(self, f, t, s): - from sympy import Integral, exp - return Integral(f*exp(-s*t), (t, 0, oo)) - - """ - Class representing unevaluated Laplace transforms. - - For usage of this class, see the :class:`IntegralTransform` docstring. - For how to compute Laplace transforms, see the :func:`laplace_transform` - docstring. - """ - def _collapse_extra(self, extra): - from sympy import And, Max - conds = [] - planes = [] - for plane, cond in extra: - conds.append(cond) - planes.append(plane) - cond = And(*conds) - plane = Max(*planes) - if cond is False: - raise IntegralTransformError( - 'Laplace', None, 'No combined convergence.') - return plane, cond - - -
    [docs]def laplace_transform(f, t, s, **hints): - r""" - Compute the Laplace Transform `F(s)` of `f(t)`, - - .. math :: F(s) = \int_0^\infty e^{-st} f(t) \mathrm{d}t. - - For all "sensible" functions, this converges absolutely in a - half plane `a < Re(s)`. - - This function returns (F, a, cond) - where `F` is the Laplace transform of `f`, `Re(s) > a` is the half-plane - of convergence, and cond are auxiliary convergence conditions. - - If the integral cannot be computed in closed form, this function returns - an unevaluated LaplaceTransform object. - - For a description of possible hints, refer to the docstring of - :func:`sympy.integrals.transforms.IntegralTransform.doit`. If ``noconds=True``, - only `F` will be returned (i.e. not ``cond``, and also not the plane ``a``). - - >>> from sympy.integrals import laplace_transform - >>> from sympy.abc import t, s, a - >>> laplace_transform(t**a, t, s) - (s**(-a - 1)*gamma(a + 1), 0, -re(a) < 1) - - See Also - ======== - - inverse_laplace_transform, mellin_transform, fourier_transform - hankel_transform, inverse_hankel_transform - """ - return LaplaceTransform(f, t, s).doit(**hints) - -
    -@_noconds_(True) -def _inverse_laplace_transform(F, s, t_, plane, simplify=True): - """ The backend function for inverse Laplace transforms. """ - from sympy import exp, Heaviside, log, expand_complex, Integral, Piecewise - from sympy.integrals.meijerint import meijerint_inversion, _get_coeff_exp - # There are two strategies we can try: - # 1) Use inverse mellin transforms - related by a simple change of variables. - # 2) Use the inversion integral. - - t = Dummy('t', real=True) - - def pw_simp(*args): - """ Simplify a piecewise expression from hyperexpand. """ - # XXX we break modularity here! - if len(args) != 3: - return Piecewise(*args) - arg = args[2].args[0].argument - coeff, exponent = _get_coeff_exp(arg, t) - e1 = args[0].args[0] - e2 = args[1].args[0] - return Heaviside(1/abs(coeff) - t**exponent)*e1 \ - + Heaviside(t**exponent - 1/abs(coeff))*e2 - - try: - f, cond = inverse_mellin_transform(F, s, exp(-t), (None, oo), - needeval=True, noconds=False) - except IntegralTransformError: - f = None - if f is None: - f = meijerint_inversion(F, s, t) - if f is None: - raise IntegralTransformError('Inverse Laplace', f, '') - if f.is_Piecewise: - f, cond = f.args[0] - if f.has(Integral): - raise IntegralTransformError('Inverse Laplace', f, - 'inversion integral of unrecognised form.') - else: - cond = True - f = f.replace(Piecewise, pw_simp) - - if f.is_Piecewise: - # many of the functions called below can't work with piecewise - # (b/c it has a bool in args) - return f.subs(t, t_), cond - - u = Dummy('u') - - def simp_heaviside(arg): - a = arg.subs(exp(-t), u) - if a.has(t): - return Heaviside(arg) - rel = _solve_inequality(a > 0, u) - if rel.lts == u: - k = log(rel.gts) - return Heaviside(t + k) - else: - k = log(rel.lts) - return Heaviside(-(t + k)) - f = f.replace(Heaviside, simp_heaviside) - - def simp_exp(arg): - return expand_complex(exp(arg)) - f = f.replace(exp, simp_exp) - - # TODO it would be nice to fix cosh and sinh ... simplify messes these - # exponentials up - - return _simplify(f.subs(t, t_), simplify), cond - - -class InverseLaplaceTransform(IntegralTransform): - """ - Class representing unevaluated inverse Laplace transforms. - - For usage of this class, see the :class:`IntegralTransform` docstring. - - For how to compute inverse Laplace transforms, see the - :func:`inverse_laplace_transform` docstring. - """ - - nargs = 4 - - _name = 'Inverse Laplace' - _none_sentinel = Dummy('None') - _c = Dummy('c') - - def __new__(cls, F, s, x, plane, **opts): - if plane is None: - plane = InverseLaplaceTransform._none_sentinel - return IntegralTransform.__new__(cls, F, s, x, plane, **opts) - - @property - def fundamental_plane(self): - plane = self.args[3] - if plane is InverseLaplaceTransform._none_sentinel: - plane = None - return plane - - def _compute_transform(self, F, s, t, **hints): - return _inverse_laplace_transform(F, s, t, self.fundamental_plane, **hints) - - def _as_integral(self, F, s, t): - from sympy import I, Integral, exp - c = self.__class__._c - return Integral(exp(s*t)*F, (s, c - I*oo, c + I*oo)) - - -
    [docs]def inverse_laplace_transform(F, s, t, plane=None, **hints): - r""" - Compute the inverse Laplace transform of `F(s)`, defined as - - .. math :: f(t) = \int_{c-i\infty}^{c+i\infty} e^{st} F(s) \mathrm{d}s, - - for `c` so large that `F(s)` has no singularites in the - half-plane `Re(s) > c-\epsilon`. - - The plane can be specified by - argument ``plane``, but will be inferred if passed as None. - - Under certain regularity conditions, this recovers `f(t)` from its - Laplace Transform `F(s)`, for non-negative `t`, and vice - versa. - - If the integral cannot be computed in closed form, this function returns - an unevaluated InverseLaplaceTransform object. - - Note that this function will always assume `t` to be real, - regardless of the sympy assumption on `t`. - - For a description of possible hints, refer to the docstring of - :func:`sympy.integrals.transforms.IntegralTransform.doit`. - - >>> from sympy.integrals.transforms import inverse_laplace_transform - >>> from sympy import exp, Symbol - >>> from sympy.abc import s, t - >>> a = Symbol('a', positive=True) - >>> inverse_laplace_transform(exp(-a*s)/s, s, t) - Heaviside(-a + t) - - See Also - ======== - - laplace_transform - hankel_transform, inverse_hankel_transform - """ - return InverseLaplaceTransform(F, s, t, plane).doit(**hints) - - -########################################################################## -# Fourier Transform -########################################################################## -
    -@_noconds_(True) -def _fourier_transform(f, x, k, a, b, name, simplify=True): - """ - Compute a general Fourier-type transform - F(k) = a int_-oo^oo exp(b*I*x*k) f(x) dx. - - For suitable choice of a and b, this reduces to the standard Fourier - and inverse Fourier transforms. - """ - from sympy import exp, I, oo - F = integrate(a*f*exp(b*I*x*k), (x, -oo, oo)) - - if not F.has(Integral): - return _simplify(F, simplify), True - - if not F.is_Piecewise: - raise IntegralTransformError(name, f, 'could not compute integral') - - F, cond = F.args[0] - if F.has(Integral): - raise IntegralTransformError(name, f, 'integral in unexpected form') - - return _simplify(F, simplify), cond - - -class FourierTypeTransform(IntegralTransform): - """ Base class for Fourier transforms. - Specify cls._a and cls._b. - """ - - def _compute_transform(self, f, x, k, **hints): - return _fourier_transform(f, x, k, - self.__class__._a, self.__class__._b, - self.__class__._name, **hints) - - def _as_integral(self, f, x, k): - from sympy import Integral, exp, I - a = self.__class__._a - b = self.__class__._b - return Integral(a*f*exp(b*I*x*k), (x, -oo, oo)) - - -class FourierTransform(FourierTypeTransform): - """ - Class representing unevaluated Fourier transforms. - - For usage of this class, see the :class:`IntegralTransform` docstring. - - For how to compute Fourier transforms, see the :func:`fourier_transform` - docstring. - """ - - _name = 'Fourier' - _a = 1 - _b = -2*S.Pi - - -
    [docs]def fourier_transform(f, x, k, **hints): - r""" - Compute the unitary, ordinary-frequency Fourier transform of `f`, defined - as - - .. math:: F(k) = \int_{-\infty}^\infty f(x) e^{-2\pi i x k} \mathrm{d} x. - - If the transform cannot be computed in closed form, this - function returns an unevaluated FourierTransform object. - - For other Fourier transform conventions, see the function - :func:`sympy.integrals.transforms._fourier_transform`. - - For a description of possible hints, refer to the docstring of - :func:`sympy.integrals.transforms.IntegralTransform.doit`. - Note that for this transform, by default ``noconds=True``. - - >>> from sympy import fourier_transform, exp - >>> from sympy.abc import x, k - >>> fourier_transform(exp(-x**2), x, k) - sqrt(pi)*exp(-pi**2*k**2) - >>> fourier_transform(exp(-x**2), x, k, noconds=False) - (sqrt(pi)*exp(-pi**2*k**2), True) - - See Also - ======== - - inverse_fourier_transform - sine_transform, inverse_sine_transform - cosine_transform, inverse_cosine_transform - hankel_transform, inverse_hankel_transform - mellin_transform, laplace_transform - """ - return FourierTransform(f, x, k).doit(**hints) - -
    -class InverseFourierTransform(FourierTypeTransform): - """ - Class representing unevaluated inverse Fourier transforms. - - For usage of this class, see the :class:`IntegralTransform` docstring. - - For how to compute inverse Fourier transforms, see the - :func:`inverse_fourier_transform` docstring. - """ - - _name = 'Inverse Fourier' - _a = 1 - _b = 2*S.Pi - - -
    [docs]def inverse_fourier_transform(F, k, x, **hints): - r""" - Compute the unitary, ordinary-frequency inverse Fourier transform of `F`, - defined as - - .. math:: f(x) = \int_{-\infty}^\infty F(k) e^{2\pi i x k} \mathrm{d} k. - - If the transform cannot be computed in closed form, this - function returns an unevaluated InverseFourierTransform object. - - For other Fourier transform conventions, see the function - :func:`sympy.integrals.transforms._fourier_transform`. - - For a description of possible hints, refer to the docstring of - :func:`sympy.integrals.transforms.IntegralTransform.doit`. - Note that for this transform, by default ``noconds=True``. - - >>> from sympy import inverse_fourier_transform, exp, sqrt, pi - >>> from sympy.abc import x, k - >>> inverse_fourier_transform(sqrt(pi)*exp(-(pi*k)**2), k, x) - exp(-x**2) - >>> inverse_fourier_transform(sqrt(pi)*exp(-(pi*k)**2), k, x, noconds=False) - (exp(-x**2), True) - - See Also - ======== - - fourier_transform - sine_transform, inverse_sine_transform - cosine_transform, inverse_cosine_transform - hankel_transform, inverse_hankel_transform - mellin_transform, laplace_transform - """ - return InverseFourierTransform(F, k, x).doit(**hints) - - -########################################################################## -# Fourier Sine and Cosine Transform -########################################################################## -
    -from sympy import sin, cos, sqrt, pi, I, oo - - -@_noconds_(True) -def _sine_cosine_transform(f, x, k, a, b, K, name, simplify=True): - """ - Compute a general sine or cosine-type transform - F(k) = a int_0^oo b*sin(x*k) f(x) dx. - F(k) = a int_0^oo b*cos(x*k) f(x) dx. - - For suitable choice of a and b, this reduces to the standard sine/cosine - and inverse sine/cosine transforms. - """ - F = integrate(a*f*K(b*x*k), (x, 0, oo)) - - if not F.has(Integral): - return _simplify(F, simplify), True - - if not F.is_Piecewise: - raise IntegralTransformError(name, f, 'could not compute integral') - - F, cond = F.args[0] - if F.has(Integral): - raise IntegralTransformError(name, f, 'integral in unexpected form') - - return _simplify(F, simplify), cond - - -class SineCosineTypeTransform(IntegralTransform): - """ - Base class for sine and cosine transforms. - Specify cls._a and cls._b and cls._kern. - """ - - def _compute_transform(self, f, x, k, **hints): - return _sine_cosine_transform(f, x, k, - self.__class__._a, self.__class__._b, - self.__class__._kern, - self.__class__._name, **hints) - - def _as_integral(self, f, x, k): - from sympy import Integral, exp, I - a = self.__class__._a - b = self.__class__._b - K = self.__class__._kern - return Integral(a*f*K(b*x*k), (x, 0, oo)) - - -class SineTransform(SineCosineTypeTransform): - """ - Class representing unevaluated sine transforms. - - For usage of this class, see the :class:`IntegralTransform` docstring. - - For how to compute sine transforms, see the :func:`sine_transform` - docstring. - """ - - _name = 'Sine' - _kern = sin - _a = sqrt(2)/sqrt(pi) - _b = 1 - - -
    [docs]def sine_transform(f, x, k, **hints): - r""" - Compute the unitary, ordinary-frequency sine transform of `f`, defined - as - - .. math:: F(k) = \sqrt{\frac{2}{\pi}} \int_{0}^\infty f(x) \sin(2\pi x k) \mathrm{d} x. - - If the transform cannot be computed in closed form, this - function returns an unevaluated SineTransform object. - - For a description of possible hints, refer to the docstring of - :func:`sympy.integrals.transforms.IntegralTransform.doit`. - Note that for this transform, by default ``noconds=True``. - - >>> from sympy import sine_transform, exp - >>> from sympy.abc import x, k, a - >>> sine_transform(x*exp(-a*x**2), x, k) - sqrt(2)*k*exp(-k**2/(4*a))/(4*a**(3/2)) - >>> sine_transform(x**(-a), x, k) - 2**(-a + 1/2)*k**(a - 1)*gamma(-a/2 + 1)/gamma(a/2 + 1/2) - - See Also - ======== - - fourier_transform, inverse_fourier_transform - inverse_sine_transform - cosine_transform, inverse_cosine_transform - hankel_transform, inverse_hankel_transform - mellin_transform, laplace_transform - """ - return SineTransform(f, x, k).doit(**hints) - -
    -class InverseSineTransform(SineCosineTypeTransform): - """ - Class representing unevaluated inverse sine transforms. - - For usage of this class, see the :class:`IntegralTransform` docstring. - - For how to compute inverse sine transforms, see the - :func:`inverse_sine_transform` docstring. - """ - - _name = 'Inverse Sine' - _kern = sin - _a = sqrt(2)/sqrt(pi) - _b = 1 - - -
    [docs]def inverse_sine_transform(F, k, x, **hints): - r""" - Compute the unitary, ordinary-frequency inverse sine transform of `F`, - defined as - - .. math:: f(x) = \sqrt{\frac{2}{\pi}} \int_{0}^\infty F(k) \sin(2\pi x k) \mathrm{d} k. - - If the transform cannot be computed in closed form, this - function returns an unevaluated InverseSineTransform object. - - For a description of possible hints, refer to the docstring of - :func:`sympy.integrals.transforms.IntegralTransform.doit`. - Note that for this transform, by default ``noconds=True``. - - >>> from sympy import inverse_sine_transform, exp, sqrt, gamma, pi - >>> from sympy.abc import x, k, a - >>> inverse_sine_transform(2**((1-2*a)/2)*k**(a - 1)* - ... gamma(-a/2 + 1)/gamma((a+1)/2), k, x) - x**(-a) - >>> inverse_sine_transform(sqrt(2)*k*exp(-k**2/(4*a))/(4*sqrt(a)**3), k, x) - x*exp(-a*x**2) - - See Also - ======== - - fourier_transform, inverse_fourier_transform - sine_transform - cosine_transform, inverse_cosine_transform - hankel_transform, inverse_hankel_transform - mellin_transform, laplace_transform - """ - return InverseSineTransform(F, k, x).doit(**hints) - -
    -class CosineTransform(SineCosineTypeTransform): - """ - Class representing unevaluated cosine transforms. - - For usage of this class, see the :class:`IntegralTransform` docstring. - - For how to compute cosine transforms, see the :func:`cosine_transform` - docstring. - """ - - _name = 'Cosine' - _kern = cos - _a = sqrt(2)/sqrt(pi) - _b = 1 - - -
    [docs]def cosine_transform(f, x, k, **hints): - r""" - Compute the unitary, ordinary-frequency cosine transform of `f`, defined - as - - .. math:: F(k) = \sqrt{\frac{2}{\pi}} \int_{0}^\infty f(x) \cos(2\pi x k) \mathrm{d} x. - - If the transform cannot be computed in closed form, this - function returns an unevaluated CosineTransform object. - - For a description of possible hints, refer to the docstring of - :func:`sympy.integrals.transforms.IntegralTransform.doit`. - Note that for this transform, by default ``noconds=True``. - - >>> from sympy import cosine_transform, exp, sqrt, cos - >>> from sympy.abc import x, k, a - >>> cosine_transform(exp(-a*x), x, k) - sqrt(2)*a/(sqrt(pi)*(a**2 + k**2)) - >>> cosine_transform(exp(-a*sqrt(x))*cos(a*sqrt(x)), x, k) - a*(-sinh(a**2/(2*k)) + cosh(a**2/(2*k)))/(2*k**(3/2)) - - See Also - ======== - - fourier_transform, inverse_fourier_transform, - sine_transform, inverse_sine_transform - inverse_cosine_transform - hankel_transform, inverse_hankel_transform - mellin_transform, laplace_transform - """ - return CosineTransform(f, x, k).doit(**hints) - -
    -class InverseCosineTransform(SineCosineTypeTransform): - """ - Class representing unevaluated inverse cosine transforms. - - For usage of this class, see the :class:`IntegralTransform` docstring. - - For how to compute inverse cosine transforms, see the - :func:`inverse_cosine_transform` docstring. - """ - - _name = 'Inverse Cosine' - _kern = cos - _a = sqrt(2)/sqrt(pi) - _b = 1 - - -
    [docs]def inverse_cosine_transform(F, k, x, **hints): - r""" - Compute the unitary, ordinary-frequency inverse cosine transform of `F`, - defined as - - .. math:: f(x) = \sqrt{\frac{2}{\pi}} \int_{0}^\infty F(k) \cos(2\pi x k) \mathrm{d} k. - - If the transform cannot be computed in closed form, this - function returns an unevaluated InverseCosineTransform object. - - For a description of possible hints, refer to the docstring of - :func:`sympy.integrals.transforms.IntegralTransform.doit`. - Note that for this transform, by default ``noconds=True``. - - >>> from sympy import inverse_cosine_transform, exp, sqrt, pi - >>> from sympy.abc import x, k, a - >>> inverse_cosine_transform(sqrt(2)*a/(sqrt(pi)*(a**2 + k**2)), k, x) - -sinh(a*x) + cosh(a*x) - >>> inverse_cosine_transform(1/sqrt(k), k, x) - 1/sqrt(x) - - See Also - ======== - - fourier_transform, inverse_fourier_transform, - sine_transform, inverse_sine_transform - cosine_transform - hankel_transform, inverse_hankel_transform - mellin_transform, laplace_transform - """ - return InverseCosineTransform(F, k, x).doit(**hints) - - -########################################################################## -# Hankel Transform -########################################################################## -
    -@_noconds_(True) -def _hankel_transform(f, r, k, nu, name, simplify=True): - """ - Compute a general Hankel transform - - .. math:: F_\nu(k) = \int_{0}^\infty f(r) J_\nu(k r) r \mathrm{d} r. - """ - from sympy import besselj, oo - F = integrate(f*besselj(nu, k*r)*r, (r, 0, oo)) - - if not F.has(Integral): - return _simplify(F, simplify), True - - if not F.is_Piecewise: - raise IntegralTransformError(name, f, 'could not compute integral') - - F, cond = F.args[0] - if F.has(Integral): - raise IntegralTransformError(name, f, 'integral in unexpected form') - - return _simplify(F, simplify), cond - - -class HankelTypeTransform(IntegralTransform): - """ - Base class for Hankel transforms. - """ - - nargs = 4 - - def doit(self, **hints): - return self._compute_transform(self.function, - self.function_variable, - self.transform_variable, - self.args[3], - **hints) - - def _compute_transform(self, f, r, k, nu, **hints): - return _hankel_transform(f, r, k, nu, self._name, **hints) - - def _as_integral(self, f, r, k, nu): - from sympy import Integral, besselj, oo - return Integral(f*besselj(nu, k*r)*r, (r, 0, oo)) - - @property - def as_integral(self): - return self._as_integral(self.function, - self.function_variable, - self.transform_variable, - self.args[3]) - - -class HankelTransform(HankelTypeTransform): - """ - Class representing unevaluated Hankel transforms. - - For usage of this class, see the :class:`IntegralTransform` docstring. - - For how to compute Hankel transforms, see the :func:`hankel_transform` - docstring. - """ - - _name = 'Hankel' - - -
    [docs]def hankel_transform(f, r, k, nu, **hints): - r""" - Compute the Hankel transform of `f`, defined as - - .. math:: F_\nu(k) = \int_{0}^\infty f(r) J_\nu(k r) r \mathrm{d} r. - - If the transform cannot be computed in closed form, this - function returns an unevaluated :class:`HankelTransform` object. - - For a description of possible hints, refer to the docstring of - :func:`sympy.integrals.transforms.IntegralTransform.doit`. - Note that for this transform, by default ``noconds=True``. - - >>> from sympy import hankel_transform, inverse_hankel_transform - >>> from sympy import gamma, exp, sinh, cosh - >>> from sympy.abc import r, k, m, nu, a - - >>> ht = hankel_transform(1/r**m, r, k, nu) - >>> ht - 2**(-m + 1)*k**(m - 2)*gamma(-m/2 + nu/2 + 1)/gamma(m/2 + nu/2) - - >>> inverse_hankel_transform(ht, k, r, nu) - r**(-m) - - >>> ht = hankel_transform(exp(-a*r), r, k, 0) - >>> ht - a/(k**3*(a**2/k**2 + 1)**(3/2)) - - >>> inverse_hankel_transform(ht, k, r, 0) - -sinh(a*r) + cosh(a*r) - - See Also - ======== - - fourier_transform, inverse_fourier_transform - sine_transform, inverse_sine_transform - cosine_transform, inverse_cosine_transform - inverse_hankel_transform - mellin_transform, laplace_transform - """ - return HankelTransform(f, r, k, nu).doit(**hints) - -
    -class InverseHankelTransform(HankelTypeTransform): - """ - Class representing unevaluated inverse Hankel transforms. - - For usage of this class, see the :class:`IntegralTransform` docstring. - - For how to compute inverse Hankel transforms, see the - :func:`inverse_hankel_transform` docstring. - """ - - _name = 'Inverse Hankel' - - -
    [docs]def inverse_hankel_transform(F, k, r, nu, **hints): - r""" - Compute the inverse Hankel transform of `F` defined as - - .. math:: f(r) = \int_{0}^\infty F_\nu(k) J_\nu(k r) k \mathrm{d} k. - - If the transform cannot be computed in closed form, this - function returns an unevaluated :class:`InverseHankelTransform` object. - - For a description of possible hints, refer to the docstring of - :func:`sympy.integrals.transforms.IntegralTransform.doit`. - Note that for this transform, by default ``noconds=True``. - - >>> from sympy import hankel_transform, inverse_hankel_transform, gamma - >>> from sympy import gamma, exp, sinh, cosh - >>> from sympy.abc import r, k, m, nu, a - - >>> ht = hankel_transform(1/r**m, r, k, nu) - >>> ht - 2**(-m + 1)*k**(m - 2)*gamma(-m/2 + nu/2 + 1)/gamma(m/2 + nu/2) - - >>> inverse_hankel_transform(ht, k, r, nu) - r**(-m) - - >>> ht = hankel_transform(exp(-a*r), r, k, 0) - >>> ht - a/(k**3*(a**2/k**2 + 1)**(3/2)) - - >>> inverse_hankel_transform(ht, k, r, 0) - -sinh(a*r) + cosh(a*r) - - See Also - ======== - - fourier_transform, inverse_fourier_transform - sine_transform, inverse_sine_transform - cosine_transform, inverse_cosine_transform - hankel_transform - mellin_transform, laplace_transform - """ - return InverseHankelTransform(F, k, r, nu).doit(**hints)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/logic/boolalg.html b/dev-py3k/_modules/sympy/logic/boolalg.html deleted file mode 100644 index b883a2410a3..00000000000 --- a/dev-py3k/_modules/sympy/logic/boolalg.html +++ /dev/null @@ -1,1073 +0,0 @@ - - - - - - - - - - sympy.logic.boolalg — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.logic.boolalg

    -"""Boolean algebra module for SymPy"""
    -from collections import defaultdict
    -
    -from sympy.core.basic import Basic
    -from sympy.core.decorators import deprecated
    -from sympy.core.operations import LatticeOp
    -from sympy.core.function import Application, sympify
    -from sympy.core.compatibility import ordered, product
    -
    -
    -class Boolean(Basic):
    -    """A boolean object is an object for which logic operations make sense."""
    -
    -    __slots__ = []
    -
    -    def __and__(self, other):
    -        """Overloading for & operator"""
    -        return And(self, other)
    -
    -    def __or__(self, other):
    -        """Overloading for |"""
    -        return Or(self, other)
    -
    -    def __invert__(self):
    -        """Overloading for ~"""
    -        return Not(self)
    -
    -    def __rshift__(self, other):
    -        """Overloading for >>"""
    -        return Implies(self, other)
    -
    -    def __lshift__(self, other):
    -        """Overloading for <<"""
    -        return Implies(other, self)
    -
    -    def __xor__(self, other):
    -        return Xor(self, other)
    -
    -
    -class BooleanFunction(Application, Boolean):
    -    """Boolean function is a function that lives in a boolean space
    -    It is used as base class for And, Or, Not, etc.
    -    """
    -    is_Boolean = True
    -
    -    def __call__(self, *args):
    -        return self.func(*[arg(*args) for arg in self.args])
    -
    -    def _eval_simplify(self, ratio, measure):
    -        return simplify_logic(self)
    -
    -
    -
    [docs]class And(LatticeOp, BooleanFunction): - """ - Logical AND function. - - It evaluates its arguments in order, giving False immediately - if any of them are False, and True if they are all True. - - Examples - ======== - - >>> from sympy.core import symbols - >>> from sympy.abc import x, y - >>> x & y - And(x, y) - """ - zero = False - identity = True - -
    -
    [docs]class Or(LatticeOp, BooleanFunction): - """ - Logical OR function - - It evaluates its arguments in order, giving True immediately - if any of them are True, and False if they are all False. - """ - zero = True - identity = False - -
    -
    [docs]class Xor(BooleanFunction): - """ - Logical XOR (exclusive OR) function. - """ - @classmethod - def eval(cls, *args): - """ - Logical XOR (exclusive OR) function. - - Returns True if an odd number of the arguments are True - and the rest are False. - Returns False if an even number of the arguments are True - and the rest are False. - - Examples - ======== - - >>> from sympy.logic.boolalg import Xor - >>> Xor(True, False) - True - >>> Xor(True, True) - False - - >>> Xor(True, False, True, True, False) - True - >>> Xor(True, False, True, False) - False - """ - if not args: - return False - args = list(args) - A = args.pop() - while args: - B = args.pop() - A = Or(And(A, Not(B)), And(Not(A), B)) - return A - -
    -
    [docs]class Not(BooleanFunction): - """ - Logical Not function (negation) - - Note: De Morgan rules applied automatically - """ - - is_Not = True - - @classmethod - def eval(cls, *args): - """ - Logical Not function (negation) - - Returns True if the statement is False - Returns False if the statement is True - - Examples - ======== - - >>> from sympy.logic.boolalg import Not, And, Or - >>> from sympy.abc import x - >>> Not(True) - False - >>> Not(False) - True - >>> Not(And(True, False)) - True - >>> Not(Or(True, False)) - False - - If multiple statements are given, returns an array of each result - - >>> Not(True, False) - [False, True] - >>> Not(True and False, True or False, True) - [True, False, False] - - >>> Not(And(And(True, x), Or(x, False))) - Not(x) - """ - if len(args) > 1: - return list(map(cls, args)) - arg = args[0] - if arg in (0, 1): # includes True and False, too - return not bool(arg) - # apply De Morgan Rules - if arg.func is And: - return Or(*[Not(a) for a in arg.args]) - if arg.func is Or: - return And(*[Not(a) for a in arg.args]) - if arg.func is Not: - return arg.args[0] - -
    -
    [docs]class Nand(BooleanFunction): - """ - Logical NAND function. - - It evaluates its arguments in order, giving True immediately if any - of them are False, and False if they are all True. - """ - @classmethod - def eval(cls, *args): - """ - Logical NAND function. - - Returns True if any of the arguments are False - Returns False if all arguments are True - - Examples - ======== - - >>> from sympy.logic.boolalg import Nand - >>> Nand(False, True) - True - >>> Nand(True, True) - False - """ - return Not(And(*args)) - -
    -
    [docs]class Nor(BooleanFunction): - """ - Logical NOR function. - - It evaluates its arguments in order, giving False immediately if any - of them are True, and True if they are all False. - """ - @classmethod - def eval(cls, *args): - """ - Logical NOR function. - - Returns False if any argument is True - Returns True if all arguments are False - - Examples - ======== - - >>> from sympy.logic.boolalg import Nor - >>> Nor(True, False) - False - >>> Nor(True, True) - False - >>> Nor(False, True) - False - >>> Nor(False, False) - True - """ - return Not(Or(*args)) - -
    -
    [docs]class Implies(BooleanFunction): - """ - Logical implication. - - A implies B is equivalent to !A v B - """ - @classmethod - def eval(cls, *args): - """ - Logical implication. - - Accepts two Boolean arguments; A and B. - Returns False if A is True and B is False - Returns True otherwise. - - Examples - ======== - - >>> from sympy.logic.boolalg import Implies - >>> Implies(True, False) - False - >>> Implies(False, False) - True - >>> Implies(True, True) - True - >>> Implies(False, True) - True - """ - try: - A, B = args - except ValueError: - raise ValueError( - "%d operand(s) used for an Implies " - "(pairs are required): %s" % (len(args), str(args))) - if A is True or A is False or B is True or B is False: - return Or(Not(A), B) - else: - return Basic.__new__(cls, *args) - -
    -
    [docs]class Equivalent(BooleanFunction): - """ - Equivalence relation. - - Equivalent(A, B) is True iff A and B are both True or both False - """ - @classmethod - def eval(cls, *args): - """ - Equivalence relation. - - Returns True if all of the arguments are logically equivalent. - Returns False otherwise. - - Examples - ======== - - >>> from sympy.logic.boolalg import Equivalent, And - >>> from sympy.abc import x - >>> Equivalent(False, False, False) - True - >>> Equivalent(True, False, False) - False - >>> Equivalent(x, And(x, True)) - True - - """ - - argset = set(args) - if len(argset) <= 1: - return True - if True in argset: - argset.discard(True) - return And(*argset) - if False in argset: - argset.discard(False) - return Nor(*argset) - return Basic.__new__(cls, *set(args)) - -
    -
    [docs]class ITE(BooleanFunction): - """ - If then else clause. - """ - @classmethod - def eval(cls, *args): - """ - If then else clause - - ITE(A, B, C) evaluates and returns the result of B if A is true - else it returns the result of C - - Examples - ======== - - >>> from sympy.logic.boolalg import ITE, And, Xor, Or - >>> from sympy.abc import x, y, z - >>> x = True - >>> y = False - >>> z = True - >>> ITE(x, y, z) - False - >>> ITE(Or(x, y), And(x, z), Xor(z, x)) - True - """ - args = list(args) - if len(args) == 3: - return Or(And(args[0], args[1]), And(Not(args[0]), args[2])) - raise ValueError("ITE expects 3 arguments, but got %d: %s" % - (len(args), str(args))) - -### end class definitions. Some useful methods - -
    -def conjuncts(expr): - """Return a list of the conjuncts in the expr s. - - Examples - ======== - - >>> from sympy.logic.boolalg import conjuncts - >>> from sympy.abc import A, B - >>> conjuncts(A & B) - frozenset([A, B]) - >>> conjuncts(A | B) - frozenset([Or(A, B)]) - - """ - return And.make_args(expr) - - -def disjuncts(expr): - """Return a list of the disjuncts in the sentence s. - - Examples - ======== - - >>> from sympy.logic.boolalg import disjuncts - >>> from sympy.abc import A, B - >>> disjuncts(A | B) - frozenset([A, B]) - >>> disjuncts(A & B) - frozenset([And(A, B)]) - - """ - return Or.make_args(expr) - - -def distribute_and_over_or(expr): - """ - Given a sentence s consisting of conjunctions and disjunctions - of literals, return an equivalent sentence in CNF. - - Examples - ======== - - >>> from sympy.logic.boolalg import distribute_and_over_or, And, Or, Not - >>> from sympy.abc import A, B, C - >>> distribute_and_over_or(Or(A, And(Not(B), Not(C)))) - And(Or(A, Not(B)), Or(A, Not(C))) - """ - if expr.func is Or: - for arg in expr.args: - if arg.func is And: - conj = arg - break - else: - return expr - rest = Or(*[a for a in expr.args if a is not conj]) - return And(*list(map(distribute_and_over_or, - [Or(c, rest) for c in conj.args]))) - elif expr.func is And: - return And(*list(map(distribute_and_over_or, expr.args))) - else: - return expr - - -
    [docs]def to_cnf(expr): - """ - Convert a propositional logical sentence s to conjunctive normal form. - That is, of the form ((A | ~B | ...) & (B | C | ...) & ...) - - Examples - ======== - - >>> from sympy.logic.boolalg import to_cnf - >>> from sympy.abc import A, B, D - >>> to_cnf(~(A | B) | D) - And(Or(D, Not(A)), Or(D, Not(B))) - - """ - # Don't convert unless we have to - if is_cnf(expr): - return expr - - expr = sympify(expr) - if not isinstance(expr, BooleanFunction): - return expr - expr = eliminate_implications(expr) - return distribute_and_over_or(expr) - -
    -def is_cnf(expr): - """ - Test whether or not an expression is in conjunctive normal form. - - Examples - ======== - - >>> from sympy.logic.boolalg import is_cnf - >>> from sympy.abc import A, B, C - >>> is_cnf(A | B | C) - True - >>> is_cnf(A & B & C) - True - >>> is_cnf((A & B) | C) - False - - """ - expr = sympify(expr) - - # Special case of a single disjunction - if expr.func is Or: - for lit in expr.args: - if lit.func is Not: - if not lit.args[0].is_Atom: - return False - else: - if not lit.is_Atom: - return False - return True - - # Special case of a single negation - if expr.func is Not: - if not expr.args[0].is_Atom: - return False - - if expr.func is not And: - return False - - for cls in expr.args: - if cls.is_Atom: - continue - if cls.func is Not: - if not cls.args[0].is_Atom: - return False - elif cls.func is not Or: - return False - for lit in cls.args: - if lit.func is Not: - if not lit.args[0].is_Atom: - return False - else: - if not lit.is_Atom: - return False - - return True - - -def eliminate_implications(expr): - """ - Change >>, <<, and Equivalent into &, |, and ~. That is, return an - expression that is equivalent to s, but has only &, |, and ~ as logical - operators. - - Examples - ======== - - >>> from sympy.logic.boolalg import Implies, Equivalent, \ - eliminate_implications - >>> from sympy.abc import A, B, C - >>> eliminate_implications(Implies(A, B)) - Or(B, Not(A)) - >>> eliminate_implications(Equivalent(A, B)) - And(Or(A, Not(B)), Or(B, Not(A))) - """ - expr = sympify(expr) - if expr.is_Atom: - return expr # (Atoms are unchanged.) - args = list(map(eliminate_implications, expr.args)) - if expr.func is Implies: - a, b = args[0], args[-1] - return (~a) | b - elif expr.func is Equivalent: - a, b = args[0], args[-1] - return (a | Not(b)) & (b | Not(a)) - else: - return expr.func(*args) - - -@deprecated( - useinstead="sympify", issue=2947, deprecated_since_version="0.7.3") -def compile_rule(s): - """ - Transforms a rule into a SymPy expression - A rule is a string of the form "symbol1 & symbol2 | ..." - - Note: this is nearly the same as sympifying the expression, but - this function converts all variables to Symbols -- there are no - special function names recognized. - - Examples - ======== - - >>> from sympy.logic.boolalg import compile_rule - >>> compile_rule('A & B') - And(A, B) - >>> compile_rule('(~B & ~C)|A') - Or(A, And(Not(B), Not(C))) - """ - import re - return sympify(re.sub(r'([a-zA-Z_][a-zA-Z0-9_]*)', r'Symbol("\1")', s)) - - -def to_int_repr(clauses, symbols): - """ - Takes clauses in CNF format and puts them into an integer representation. - - Examples - ======== - - >>> from sympy.logic.boolalg import to_int_repr - >>> from sympy.abc import x, y - >>> to_int_repr([x | y, y], [x, y]) == [set([1, 2]), set([2])] - True - - """ - - # Convert the symbol list into a dict - symbols = dict(list(zip(symbols, list(range(1, len(symbols) + 1))))) - - def append_symbol(arg, symbols): - if arg.func is Not: - return -symbols[arg.args[0]] - else: - return symbols[arg] - - return [set(append_symbol(arg, symbols) for arg in Or.make_args(c)) - for c in clauses] - - -def _check_pair(minterm1, minterm2): - """ - Checks if a pair of minterms differs by only one bit. If yes, returns - index, else returns -1. - """ - index = -1 - for x, (i, j) in enumerate(list(zip(minterm1, minterm2))): - if i != j: - if index == -1: - index = x - else: - return -1 - return index - - -def _convert_to_varsSOP(minterm, variables): - """ - Converts a term in the expansion of a function from binary to it's - variable form (for SOP). - """ - temp = [] - for i, m in enumerate(minterm): - if m == 0: - temp.append(Not(variables[i])) - elif m == 1: - temp.append(variables[i]) - else: - pass # ignore the 3s - return And(*temp) - - -def _convert_to_varsPOS(maxterm, variables): - """ - Converts a term in the expansion of a function from binary to it's - variable form (for POS). - """ - temp = [] - for i, m in enumerate(maxterm): - if m == 1: - temp.append(Not(variables[i])) - elif m == 0: - temp.append(variables[i]) - else: - pass # ignore the 3s - return Or(*temp) - - -def _simplified_pairs(terms): - """ - Reduces a set of minterms, if possible, to a simplified set of minterms - with one less variable in the terms using QM method. - """ - simplified_terms = [] - todo = list(range(len(terms))) - for i, ti in enumerate(terms[:-1]): - for j_i, tj in enumerate(terms[(i + 1):]): - index = _check_pair(ti, tj) - if index != -1: - todo[i] = todo[j_i + i + 1] = None - newterm = ti[:] - newterm[index] = 3 - if newterm not in simplified_terms: - simplified_terms.append(newterm) - simplified_terms.extend( - [terms[i] for i in [_ for _ in todo if _ is not None]]) - return simplified_terms - - -def _compare_term(minterm, term): - """ - Return True if a binary term is satisfied by the given term. Used - for recognizing prime implicants. - """ - for i, x in enumerate(term): - if x != 3 and x != minterm[i]: - return False - return True - - -def _rem_redundancy(l1, terms): - """ - After the truth table has been sufficiently simplified, use the prime - implicant table method to recognize and eliminate redundant pairs, - and return the essential arguments. - """ - essential = [] - for x in terms: - temporary = [] - for y in l1: - if _compare_term(x, y): - temporary.append(y) - if len(temporary) == 1: - if temporary[0] not in essential: - essential.append(temporary[0]) - for x in terms: - for y in essential: - if _compare_term(x, y): - break - else: - for z in l1: - if _compare_term(x, z): - if z not in essential: - essential.append(z) - break - - return essential - - -def SOPform(variables, minterms, dontcares=None): - """ - The SOPform function uses simplified_pairs and a redundant group- - eliminating algorithm to convert the list of all input combos that - generate '1' (the minterms) into the smallest Sum of Products form. - - The variables must be given as the first argument. - - Return a logical Or function (i.e., the "sum of products" or "SOP" - form) that gives the desired outcome. If there are inputs that can - be ignored, pass them as a list, too. - - The result will be one of the (perhaps many) functions that satisfy - the conditions. - - Examples - ======== - - >>> from sympy.logic import SOPform - >>> minterms = [[0, 0, 0, 1], [0, 0, 1, 1], - ... [0, 1, 1, 1], [1, 0, 1, 1], [1, 1, 1, 1]] - >>> dontcares = [[0, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 1]] - >>> SOPform(['w','x','y','z'], minterms, dontcares) - Or(And(Not(w), z), And(y, z)) - - References - ========== - - .. [1] en.wikipedia.org/wiki/Quine-McCluskey_algorithm - - """ - from sympy.core.symbol import Symbol - - variables = [Symbol(v) if not isinstance(v, Symbol) else v - for v in variables] - if minterms == []: - return False - - minterms = [list(i) for i in minterms] - dontcares = [list(i) for i in (dontcares or [])] - for d in dontcares: - if d in minterms: - raise ValueError('%s in minterms is also in dontcares' % d) - - old = None - new = minterms + dontcares - while new != old: - old = new - new = _simplified_pairs(old) - essential = _rem_redundancy(new, minterms) - return Or(*[_convert_to_varsSOP(x, variables) for x in essential]) - - -def POSform(variables, minterms, dontcares=None): - """ - The POSform function uses simplified_pairs and a redundant-group - eliminating algorithm to convert the list of all input combinations - that generate '1' (the minterms) into the smallest Product of Sums form. - - The variables must be given as the first argument. - - Return a logical And function (i.e., the "product of sums" or "POS" - form) that gives the desired outcome. If there are inputs that can - be ignored, pass them as a list, too. - - The result will be one of the (perhaps many) functions that satisfy - the conditions. - - Examples - ======== - - >>> from sympy.logic import POSform - >>> minterms = [[0, 0, 0, 1], [0, 0, 1, 1], [0, 1, 1, 1], - ... [1, 0, 1, 1], [1, 1, 1, 1]] - >>> dontcares = [[0, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 1]] - >>> POSform(['w','x','y','z'], minterms, dontcares) - And(Or(Not(w), y), z) - - References - ========== - - .. [1] en.wikipedia.org/wiki/Quine-McCluskey_algorithm - - """ - from sympy.core.symbol import Symbol - - variables = [Symbol(v) if not isinstance(v, Symbol) else v - for v in variables] - if minterms == []: - return False - - minterms = [list(i) for i in minterms] - dontcares = [list(i) for i in (dontcares or [])] - for d in dontcares: - if d in minterms: - raise ValueError('%s in minterms is also in dontcares' % d) - - maxterms = [] - for t in product([0, 1], repeat=len(variables)): - t = list(t) - if (t not in minterms) and (t not in dontcares): - maxterms.append(t) - old = None - new = maxterms + dontcares - while new != old: - old = new - new = _simplified_pairs(old) - essential = _rem_redundancy(new, maxterms) - return And(*[_convert_to_varsPOS(x, variables) for x in essential]) - - -def simplify_logic(expr): - """ - This function simplifies a boolean function to its - simplified version in SOP or POS form. The return type is an - Or or And object in SymPy. The input can be a string or a boolean - expression. - - Examples - ======== - - >>> from sympy.logic import simplify_logic - >>> from sympy.abc import x, y, z - >>> from sympy import S - - >>> b = '(~x & ~y & ~z) | ( ~x & ~y & z)' - >>> simplify_logic(b) - And(Not(x), Not(y)) - - >>> S(b) - Or(And(Not(x), Not(y), Not(z)), And(Not(x), Not(y), z)) - >>> simplify_logic(_) - And(Not(x), Not(y)) - - """ - expr = sympify(expr) - if not isinstance(expr, BooleanFunction): - return expr - variables = list(expr.free_symbols) - truthtable = [] - for t in product([0, 1], repeat=len(variables)): - t = list(t) - if expr.subs(list(zip(variables, t))) == True: - truthtable.append(t) - if (len(truthtable) >= (2 ** (len(variables) - 1))): - return SOPform(variables, truthtable) - else: - return POSform(variables, truthtable) - - -def _finger(eq): - """ - Assign a 5-item fingerprint to each symbol in the equation: - [ - # of times it appeared as a Symbol, - # of times it appeared as a Not(symbol), - # of times it appeared as a Symbol in an And or Or, - # of times it appeared as a Not(Symbol) in an And or Or, - sum of the number of arguments with which it appeared, - counting Symbol as 1 and Not(Symbol) as 2 - ] - - >>> from sympy.logic.boolalg import _finger as finger - >>> from sympy import And, Or, Not - >>> from sympy.abc import a, b, x, y - >>> eq = Or(And(Not(y), a), And(Not(y), b), And(x, y)) - >>> dict(finger(eq)) - {(0, 0, 1, 0, 2): [x], (0, 0, 1, 0, 3): [a, b], (0, 0, 1, 2, 8): [y]} - - So y and x have unique fingerprints, but a and b do not. - """ - f = eq.free_symbols - d = dict(list(zip(f, [[0] * 5 for fi in f]))) - for a in eq.args: - if a.is_Symbol: - d[a][0] += 1 - elif a.is_Not: - d[a.args[0]][1] += 1 - else: - o = len(a.args) + sum(ai.func is Not for ai in a.args) - for ai in a.args: - if ai.is_Symbol: - d[ai][2] += 1 - d[ai][-1] += o - else: - d[ai.args[0]][3] += 1 - d[ai.args[0]][-1] += o - inv = defaultdict(list) - for k, v in ordered(iter(d.items())): - inv[tuple(v)].append(k) - return inv - - -def bool_equal(bool1, bool2, info=False): - """Return True if the two expressions represent the same logical - behavior for some correspondence between the variables of each - (which may be different). For example, And(x, y) is logically - equivalent to And(a, b) for {x: a, y: b} (or vice versa). If the - mapping is desired, then set ``info`` to True and the simplified - form of the functions and the mapping of variables will be - returned. - - Examples - ======== - - >>> from sympy import SOPform, bool_equal, Or, And, Not, Xor - >>> from sympy.abc import w, x, y, z, a, b, c, d - >>> function1 = SOPform(['x','z','y'],[[1, 0, 1], [0, 0, 1]]) - >>> function2 = SOPform(['a','b','c'],[[1, 0, 1], [1, 0, 0]]) - >>> bool_equal(function1, function2, info=True) - (And(Not(z), y), {y: a, z: b}) - - The results are not necessarily unique, but they are canonical. Here, - ``(w, z)`` could be ``(a, d)`` or ``(d, a)``: - - >>> eq = Or(And(Not(y), w), And(Not(y), z), And(x, y)) - >>> eq2 = Or(And(Not(c), a), And(Not(c), d), And(b, c)) - >>> bool_equal(eq, eq2) - True - >>> bool_equal(eq, eq2, info=True) - (Or(And(Not(y), w), And(Not(y), z), And(x, y)), {w: a, x: b, y: c, z: d}) - >>> eq = And(Xor(a, b), c, And(c,d)) - >>> bool_equal(eq, eq.subs(c, x), info=True) - (And(Or(Not(a), Not(b)), Or(a, b), c, d), {a: a, b: b, c: d, d: x}) - - """ - - def match(function1, function2): - """Return the mapping that equates variables between two - simplified boolean expressions if possible. - - By "simplified" we mean that a function has been denested - and is either an And (or an Or) whose arguments are either - symbols (x), negated symbols (Not(x)), or Or (or an And) whose - arguments are only symbols or negated symbols. For example, - And(x, Not(y), Or(w, Not(z))). - - Basic.match is not robust enough (see issue 1736) so this is - a workaround that is valid for simplified boolean expressions - """ - - # do some quick checks - if function1.__class__ != function2.__class__: - return None - if len(function1.args) != len(function2.args): - return None - if function1.is_Symbol: - return {function1: function2} - - # get the fingerprint dictionaries - f1 = _finger(function1) - f2 = _finger(function2) - - # more quick checks - if len(f1) != len(f2): - return False - - # assemble the match dictionary if possible - matchdict = {} - for k in list(f1.keys()): - if k not in f2: - return False - if len(f1[k]) != len(f2[k]): - return False - for i, x in enumerate(f1[k]): - matchdict[x] = f2[k][i] - return matchdict - - a = simplify_logic(bool1) - b = simplify_logic(bool2) - m = match(a, b) - if m and info: - return a, m - return m is not None -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/logic/inference.html b/dev-py3k/_modules/sympy/logic/inference.html deleted file mode 100644 index dbde0f61e66..00000000000 --- a/dev-py3k/_modules/sympy/logic/inference.html +++ /dev/null @@ -1,335 +0,0 @@ - - - - - - - - - - sympy.logic.inference — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.logic.inference

    -"""Inference in propositional logic"""
    -from sympy.logic.boolalg import And, Or, Not, Implies, Equivalent, \
    -    conjuncts, to_cnf
    -from sympy.core.basic import C
    -from sympy.core.sympify import sympify
    -
    -
    -def literal_symbol(literal):
    -    """
    -    The symbol in this literal (without the negation).
    -
    -    Examples:
    -
    -    >>> from sympy import Symbol
    -    >>> from sympy.abc import A
    -    >>> from sympy.logic.inference import literal_symbol
    -    >>> literal_symbol(A)
    -    A
    -    >>> literal_symbol(~A)
    -    A
    -
    -    """
    -
    -    if literal.func is Not:
    -        return literal.args[0]
    -    else:
    -        return literal
    -
    -
    -
    [docs]def satisfiable(expr, algorithm="dpll2"): - """ - Check satisfiability of a propositional sentence. - Returns a model when it succeeds - - Examples: - - >>> from sympy.abc import A, B - >>> from sympy.logic.inference import satisfiable - >>> satisfiable(A & ~B) - {A: True, B: False} - >>> satisfiable(A & ~A) - False - - """ - expr = to_cnf(expr) - if algorithm == "dpll": - from sympy.logic.algorithms.dpll import dpll_satisfiable - return dpll_satisfiable(expr) - elif algorithm == "dpll2": - from sympy.logic.algorithms.dpll2 import dpll_satisfiable - return dpll_satisfiable(expr) - raise NotImplementedError - -
    -def pl_true(expr, model={}): - """ - Return True if the propositional logic expression is true in the model, - and False if it is false. If the model does not specify the value for - every proposition, this may return None to indicate 'not obvious'; - this may happen even when the expression is tautological. - - The model is implemented as a dict containing the pair symbol, boolean value. - - Examples - ======== - - >>> from sympy.abc import A, B - >>> from sympy.logic.inference import pl_true - >>> pl_true( A & B, {A: True, B : True}) - True - - """ - - if isinstance(expr, bool): - return expr - - expr = sympify(expr) - - if expr.is_Symbol: - return model.get(expr) - - args = expr.args - func = expr.func - - if func is Not: - p = pl_true(args[0], model) - if p is None: - return None - else: - return not p - elif func is Or: - result = False - for arg in args: - p = pl_true(arg, model) - if p is True: - return True - if p is None: - result = None - return result - elif func is And: - result = True - for arg in args: - p = pl_true(arg, model) - if p is False: - return False - if p is None: - result = None - return result - - elif func is Implies: - p, q = args - return pl_true(Or(Not(p), q), model) - - elif func is Equivalent: - p, q = args - pt = pl_true(p, model) - if pt is None: - return None - qt = pl_true(q, model) - if qt is None: - return None - return pt == qt - else: - raise ValueError("Illegal operator in logic expression" + str(expr)) - - -class KB(object): - """Base class for all knowledge bases""" - def __init__(self, sentence=None): - self.clauses = [] - if sentence: - self.tell(sentence) - - def tell(self, sentence): - raise NotImplementedError - - def ask(self, query): - raise NotImplementedError - - def retract(self, sentence): - raise NotImplementedError - - -class PropKB(KB): - """A KB for Propositional Logic. Inefficient, with no indexing.""" - - def tell(self, sentence): - """Add the sentence's clauses to the KB - - Examples - ======== - - >>> from sympy.logic.inference import PropKB - >>> from sympy.abc import x, y - >>> l = PropKB() - >>> l.clauses - [] - - >>> l.tell(x | y) - >>> l.clauses - [Or(x, y)] - - >>> l.tell(y) - >>> l.clauses - [Or(x, y), y] - """ - for c in conjuncts(to_cnf(sentence)): - if not c in self.clauses: - self.clauses.append(c) - - def ask(self, query): - """Checks if the query is true given the set of clauses. - - Examples - ======== - - >>> from sympy.logic.inference import PropKB - >>> from sympy.abc import x, y - >>> l = PropKB() - >>> l.tell(x & ~y) - >>> l.ask(x) - True - >>> l.ask(y) - False - """ - if len(self.clauses) == 0: - return False - from sympy.logic.algorithms.dpll import dpll - query_conjuncts = self.clauses[:] - query_conjuncts.extend(conjuncts(to_cnf(query))) - s = set() - for q in query_conjuncts: - s = s.union(q.atoms(C.Symbol)) - return bool(dpll(query_conjuncts, list(s), {})) - - def retract(self, sentence): - """Remove the sentence's clauses from the KB - - Examples - ======== - - >>> from sympy.logic.inference import PropKB - >>> from sympy.abc import x, y - >>> l = PropKB() - >>> l.clauses - [] - - >>> l.tell(x | y) - >>> l.clauses - [Or(x, y)] - - >>> l.retract(x | y) - >>> l.clauses - [] - """ - for c in conjuncts(to_cnf(sentence)): - if c in self.clauses: - self.clauses.remove(c) -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/matrices/dense.html b/dev-py3k/_modules/sympy/matrices/dense.html deleted file mode 100644 index 4bee54d73e4..00000000000 --- a/dev-py3k/_modules/sympy/matrices/dense.html +++ /dev/null @@ -1,1712 +0,0 @@ - - - - - - - - - - sympy.matrices.dense — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.matrices.dense

    -import random
    -
    -from sympy.core.basic import Basic
    -from sympy.core.compatibility import is_sequence, as_int
    -from sympy.core.function import count_ops
    -from sympy.core.decorators import call_highest_priority
    -from sympy.core.singleton import S
    -from sympy.core.symbol import Symbol
    -from sympy.core.sympify import sympify
    -from sympy.functions.elementary.trigonometric import cos, sin
    -from sympy.functions.elementary.miscellaneous import sqrt
    -from sympy.simplify import simplify as _simplify
    -from sympy.utilities.exceptions import SymPyDeprecationWarning
    -from sympy.utilities.misc import filldedent
    -
    -from sympy.matrices.matrices import (MatrixBase,
    -    ShapeError, a2idx, classof)
    -import collections
    -
    -
    -def _iszero(x):
    -    """Returns True if x is zero."""
    -    return x.is_zero
    -
    -
    -class DenseMatrix(MatrixBase):
    -
    -    is_MatrixExpr = False
    -
    -    _op_priority = 10.01
    -    _class_priority = 4
    -
    -    def __getitem__(self, key):
    -        """Return portion of self defined by key. If the key involves a slice
    -        then a list will be returned (if key is a single slice) or a matrix
    -        (if key was a tuple involving a slice).
    -
    -        Examples
    -        ========
    -
    -        >>> from sympy import Matrix, I
    -        >>> m = Matrix([
    -        ... [1, 2 + I],
    -        ... [3, 4    ]])
    -
    -        If the key is a tuple that doesn't involve a slice then that element
    -        is returned:
    -
    -        >>> m[1, 0]
    -        3
    -
    -        When a tuple key involves a slice, a matrix is returned. Here, the
    -        first column is selected (all rows, column 0):
    -
    -        >>> m[:, 0]
    -        [1]
    -        [3]
    -
    -        If the slice is not a tuple then it selects from the underlying
    -        list of elements that are arranged in row order and a list is
    -        returned if a slice is involved:
    -
    -        >>> m[0]
    -        1
    -        >>> m[::2]
    -        [1, 3]
    -        """
    -        if type(key) is tuple:
    -            i, j = key
    -            if type(i) is slice or type(j) is slice:
    -                return self.submatrix(key)
    -            else:
    -                i, j = self.key2ij(key)
    -                return self._mat[i*self.cols + j]
    -        else:
    -            # row-wise decomposition of matrix
    -            if type(key) is slice:
    -                return self._mat[key]
    -            return self._mat[a2idx(key)]
    -
    -    def __setitem__(self, key, value):
    -        raise NotImplementedError()
    -
    -    def __hash__(self):
    -        # issue 880 suggests that there should be no hash for a mutable
    -        # object...but at least we aren't caching the result
    -        return hash((type(self).__name__,) + (self.shape, tuple(self._mat)))
    -
    -    @property
    -    def is_Identity(self):
    -        if not self.is_square:
    -            return False
    -        if not all(self[i, i] == 1 for i in range(self.rows)):
    -            return False
    -        for i in range(self.rows):
    -            for j in range(i + 1, self.cols):
    -                if self[i, j] or self[j, i]:
    -                    return False
    -        return True
    -
    -    def tolist(self):
    -        """Return the Matrix as a nested Python list.
    -
    -        Examples
    -        ========
    -
    -        >>> from sympy import Matrix, ones
    -        >>> m = Matrix(3, 3, list(range(9)))
    -        >>> m
    -        [0, 1, 2]
    -        [3, 4, 5]
    -        [6, 7, 8]
    -        >>> m.tolist()
    -        [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
    -        >>> ones(3, 0).tolist()
    -        [[], [], []]
    -
    -        When there are no rows then it will not be possible to tell how
    -        many columns were in the original matrix:
    -
    -        >>> ones(0, 3).tolist()
    -        []
    -
    -        """
    -        if not self.rows:
    -            return []
    -        if not self.cols:
    -            return [[] for i in range(self.rows)]
    -        return [self._mat[i: i + self.cols]
    -            for i in range(0, len(self), self.cols)]
    -
    -    def row(self, i, f=None):
    -        """Elementary row selector.
    -
    -        Examples
    -        ========
    -
    -        >>> from sympy import eye
    -        >>> eye(2).row(0)
    -        [1, 0]
    -
    -        See Also
    -        ========
    -
    -        col
    -        row_op
    -        row_swap
    -        row_del
    -        row_join
    -        row_insert
    -        """
    -        if f is None:
    -            return self[i, :]
    -        SymPyDeprecationWarning(
    -            feature="calling .row(i, f)",
    -            useinstead=".row_op(i, f)",
    -            deprecated_since_version="0.7.2",
    -        ).warn()
    -        self.row_op(i, f)
    -
    -    def col(self, j, f=None):
    -        """Elementary column selector.
    -
    -        Examples
    -        ========
    -
    -        >>> from sympy import eye
    -        >>> eye(2).col(0)
    -        [1]
    -        [0]
    -
    -        See Also
    -        ========
    -
    -        row
    -        col_op
    -        col_swap
    -        col_del
    -        col_join
    -        col_insert
    -        """
    -        if f is None:
    -            return self[:, j]
    -        SymPyDeprecationWarning(
    -            feature="calling .col(j, f)",
    -            useinstead=".col_op(j, f)",
    -            deprecated_since_version="0.7.2",
    -        ).warn()
    -        self.col_op(j, f)
    -
    -    def _eval_trace(self):
    -        """Calculate the trace of a square matrix.
    -
    -        Examples
    -        ========
    -
    -        >>> from sympy.matrices import eye
    -        >>> eye(3).trace()
    -        3
    -
    -        """
    -        trace = 0
    -        for i in range(self.cols):
    -            trace += self._mat[i*self.cols + i]
    -        return trace
    -
    -    def _eval_transpose(self):
    -        """Matrix transposition.
    -
    -        Examples
    -        ========
    -
    -        >>> from sympy import Matrix, I
    -        >>> m=Matrix(((1, 2+I), (3, 4)))
    -        >>> m
    -        [1, 2 + I]
    -        [3,     4]
    -        >>> m.transpose()
    -        [    1, 3]
    -        [2 + I, 4]
    -        >>> m.T == m.transpose()
    -        True
    -
    -        See Also
    -        ========
    -
    -        conjugate: By-element conjugation
    -        """
    -        a = []
    -        for i in range(self.cols):
    -            a.extend(self._mat[i::self.cols])
    -        return self._new(self.cols, self.rows, a)
    -
    -    def _eval_conjugate(self):
    -        """By-element conjugation.
    -
    -        See Also
    -        ========
    -
    -        transpose: Matrix transposition
    -        H: Hermite conjugation
    -        D: Dirac conjugation
    -        """
    -        out = self._new(self.rows, self.cols,
    -                lambda i, j: self[i, j].conjugate())
    -        return out
    -
    -    def _eval_adjoint(self):
    -        return self.T.C
    -
    -    def _eval_inverse(self, **kwargs):
    -        """Return the matrix inverse using the method indicated (default
    -        is Gauss elimination).
    -
    -        kwargs
    -        ======
    -
    -        method : ('GE', 'LU', or 'ADJ')
    -        iszerofunc
    -        try_block_diag
    -
    -        Notes
    -        =====
    -
    -        According to the ``method`` keyword, it calls the appropriate method:
    -
    -          GE .... inverse_GE(); default
    -          LU .... inverse_LU()
    -          ADJ ... inverse_ADJ()
    -
    -        According to the ``try_block_diag`` keyword, it will try to form block
    -        diagonal matrices using the method get_diag_blocks(), invert these
    -        individually, and then reconstruct the full inverse matrix.
    -
    -        Note, the GE and LU methods may require the matrix to be simplified
    -        before it is inverted in order to properly detect zeros during
    -        pivoting. In difficult cases a custom zero detection function can
    -        be provided by setting the ``iszerosfunc`` argument to a function that
    -        should return True if its argument is zero. The ADJ routine computes
    -        the determinant and uses that to detect singular matrices in addition
    -        to testing for zeros on the diagonal.
    -
    -        See Also
    -        ========
    -
    -        inverse_LU
    -        inverse_GE
    -        inverse_ADJ
    -        """
    -        from sympy.matrices import diag
    -
    -        method = kwargs.get('method', 'GE')
    -        iszerofunc = kwargs.get('iszerofunc', _iszero)
    -        if kwargs.get('try_block_diag', False):
    -            blocks = self.get_diag_blocks()
    -            r = []
    -            for block in blocks:
    -                r.append(block.inv(method=method, iszerofunc=iszerofunc))
    -            return diag(*r)
    -
    -        M = self.as_mutable()
    -        if method == "GE":
    -            rv = M.inverse_GE(iszerofunc=iszerofunc)
    -        elif method == "LU":
    -            rv = M.inverse_LU(iszerofunc=iszerofunc)
    -        elif method == "ADJ":
    -            rv = M.inverse_ADJ(iszerofunc=iszerofunc)
    -        else:
    -            # make sure to add an invertibility check (as in inverse_LU)
    -            # if a new method is added.
    -            raise ValueError("Inversion method unrecognized")
    -        return self._new(rv)
    -
    -    def equals(self, other, failing_expression=False):
    -        """Applies ``equals`` to corresponding elements of the matrices,
    -        trying to prove that the elements are equivalent, returning True
    -        if they are, False if any pair is not, and None (or the first
    -        failing expression if failing_expression is True) if it cannot
    -        be decided if the expressions are equivalent or not. This is, in
    -        general, an expensive operation.
    -
    -        Examples
    -        ========
    -
    -        >>> from sympy.matrices import Matrix
    -        >>> from sympy.abc import x
    -        >>> from sympy import cos
    -        >>> A = Matrix([x*(x - 1), 0])
    -        >>> B = Matrix([x**2 - x, 0])
    -        >>> A == B
    -        False
    -        >>> A.simplify() == B.simplify()
    -        True
    -        >>> A.equals(B)
    -        True
    -        >>> A.equals(2)
    -        False
    -
    -        See Also
    -        ========
    -        sympy.core.expr.equals
    -        """
    -        try:
    -            if self.shape != other.shape:
    -                return False
    -            rv = True
    -            for i in range(self.rows):
    -                for j in range(self.cols):
    -                    ans = self[i, j].equals(other[i, j], failing_expression)
    -                    if ans is False:
    -                        return False
    -                    elif ans is not True and rv is True:
    -                        rv = ans
    -            return rv
    -        except AttributeError:
    -            return False
    -
    -    def __eq__(self, other):
    -        try:
    -            if self.shape != other.shape:
    -                return False
    -            if isinstance(other, Matrix):
    -                return self._mat == other._mat
    -            elif isinstance(other, MatrixBase):
    -                return self._mat == Matrix(other)._mat
    -        except AttributeError:
    -            return False
    -
    -    def __ne__(self, other):
    -        return not self == other
    -
    -    def _cholesky(self):
    -        """Helper function of cholesky.
    -        Without the error checks.
    -        To be used privately. """
    -        L = zeros(self.rows, self.rows)
    -        for i in range(self.rows):
    -            for j in range(i):
    -                L[i, j] = (1 / L[j, j])*(self[i, j] -
    -                    sum(L[i, k]*L[j, k] for k in range(j)))
    -            L[i, i] = sqrt(self[i, i] -
    -                    sum(L[i, k]**2 for k in range(i)))
    -        return self._new(L)
    -
    -    def _LDLdecomposition(self):
    -        """Helper function of LDLdecomposition.
    -        Without the error checks.
    -        To be used privately.
    -        """
    -        D = zeros(self.rows, self.rows)
    -        L = eye(self.rows)
    -        for i in range(self.rows):
    -            for j in range(i):
    -                L[i, j] = (1 / D[j, j])*(self[i, j] - sum(
    -                    L[i, k]*L[j, k]*D[k, k] for k in range(j)))
    -            D[i, i] = self[i, i] - sum(L[i, k]**2*D[k, k]
    -                for k in range(i))
    -        return self._new(L), self._new(D)
    -
    -    def _lower_triangular_solve(self, rhs):
    -        """Helper function of function lower_triangular_solve.
    -        Without the error checks.
    -        To be used privately.
    -        """
    -        X = zeros(self.rows, rhs.cols)
    -        for j in range(rhs.cols):
    -            for i in range(self.rows):
    -                if self[i, i] == 0:
    -                    raise TypeError("Matrix must be non-singular.")
    -                X[i, j] = (rhs[i, j] - sum(self[i, k]*X[k, j]
    -                    for k in range(i))) / self[i, i]
    -        return self._new(X)
    -
    -    def _upper_triangular_solve(self, rhs):
    -        """Helper function of function upper_triangular_solve.
    -        Without the error checks, to be used privately. """
    -        X = zeros(self.rows, rhs.cols)
    -        for j in range(rhs.cols):
    -            for i in reversed(list(range(self.rows))):
    -                if self[i, i] == 0:
    -                    raise ValueError("Matrix must be non-singular.")
    -                X[i, j] = (rhs[i, j] - sum(self[i, k]*X[k, j]
    -                    for k in range(i + 1, self.rows))) / self[i, i]
    -        return self._new(X)
    -
    -    def _diagonal_solve(self, rhs):
    -        """Helper function of function diagonal_solve,
    -        without the error checks, to be used privately.
    -        """
    -        return self._new(rhs.rows, rhs.cols, lambda i, j: rhs[i, j] / self[i, i])
    -
    -    def applyfunc(self, f):
    -        """Apply a function to each element of the matrix.
    -
    -        Examples
    -        ========
    -
    -        >>> from sympy import Matrix
    -        >>> m = Matrix(2, 2, lambda i, j: i*2+j)
    -        >>> m
    -        [0, 1]
    -        [2, 3]
    -        >>> m.applyfunc(lambda i: 2*i)
    -        [0, 2]
    -        [4, 6]
    -
    -        """
    -        if not isinstance(f, collections.Callable):
    -            raise TypeError("`f` must be callable.")
    -
    -        out = self._new(self.rows, self.cols, list(map(f, self._mat)))
    -        return out
    -
    -    def reshape(self, rows, cols):
    -        """Reshape the matrix. Total number of elements must remain the same.
    -
    -        Examples
    -        ========
    -
    -        >>> from sympy import Matrix
    -        >>> m = Matrix(2, 3, lambda i, j: 1)
    -        >>> m
    -        [1, 1, 1]
    -        [1, 1, 1]
    -        >>> m.reshape(1, 6)
    -        [1, 1, 1, 1, 1, 1]
    -        >>> m.reshape(3, 2)
    -        [1, 1]
    -        [1, 1]
    -        [1, 1]
    -
    -        """
    -        if len(self) != rows*cols:
    -            raise ValueError("Invalid reshape parameters %d %d" % (rows, cols))
    -        return self._new(rows, cols, lambda i, j: self._mat[i*cols + j])
    -
    -    def as_mutable(self):
    -        """Returns a mutable version of this matrix
    -
    -        Examples
    -        ========
    -
    -        >>> from sympy import ImmutableMatrix
    -        >>> X = ImmutableMatrix([[1, 2], [3, 4]])
    -        >>> Y = X.as_mutable()
    -        >>> Y[1, 1] = 5 # Can set values in Y
    -        >>> Y
    -        [1, 2]
    -        [3, 5]
    -        """
    -        return Matrix(self)
    -
    -    def as_immutable(self):
    -        """Returns an Immutable version of this Matrix
    -        """
    -        from .immutable import ImmutableMatrix as cls
    -        if self.rows:
    -            return cls._new(self.tolist())
    -        return cls._new(0, self.cols, [])
    -
    -    @classmethod
    -    def zeros(cls, r, c=None):
    -        """Return an r x c matrix of zeros, square if c is omitted."""
    -        if is_sequence(r):
    -            SymPyDeprecationWarning(
    -                feature="The syntax zeros([%i, %i])" % tuple(r),
    -                useinstead="zeros(%i, %i)." % tuple(r),
    -                issue=3381, deprecated_since_version="0.7.2",
    -            ).warn()
    -            r, c = r
    -        else:
    -            c = r if c is None else c
    -        r = as_int(r)
    -        c = as_int(c)
    -        return cls._new(r, c, [S.Zero]*r*c)
    -
    -    @classmethod
    -    def eye(cls, n):
    -        """Return an n x n identity matrix."""
    -        n = as_int(n)
    -        mat = [S.Zero]*n*n
    -        mat[::n + 1] = [S.One]*n
    -        return cls._new(n, n, mat)
    -
    -    ############################
    -    # Mutable matrix operators #
    -    ############################
    -
    -    @call_highest_priority('__radd__')
    -    def __add__(self, other):
    -        return MatrixBase.__add__(self, _force_mutable(other))
    -
    -    @call_highest_priority('__add__')
    -    def __radd__(self, other):
    -        return MatrixBase.__radd__(self, _force_mutable(other))
    -
    -    @call_highest_priority('__rsub__')
    -    def __sub__(self, other):
    -        return MatrixBase.__sub__(self, _force_mutable(other))
    -
    -    @call_highest_priority('__sub__')
    -    def __rsub__(self, other):
    -        return MatrixBase.__rsub__(self, _force_mutable(other))
    -
    -    @call_highest_priority('__rmul__')
    -    def __mul__(self, other):
    -        return MatrixBase.__mul__(self, _force_mutable(other))
    -
    -    @call_highest_priority('__mul__')
    -    def __rmul__(self, other):
    -        return MatrixBase.__rmul__(self, _force_mutable(other))
    -
    -    @call_highest_priority('__div__')
    -    def __div__(self, other):
    -        return MatrixBase.__div__(self, _force_mutable(other))
    -
    -    @call_highest_priority('__truediv__')
    -    def __truediv__(self, other):
    -        return MatrixBase.__truediv__(self, _force_mutable(other))
    -
    -    @call_highest_priority('__rpow__')
    -    def __pow__(self, other):
    -        return MatrixBase.__pow__(self, other)
    -
    -    @call_highest_priority('__pow__')
    -    def __rpow__(self, other):
    -        raise NotImplementedError("Matrix Power not defined")
    -
    -
    -def _force_mutable(x):
    -    """Return a matrix as a Matrix, otherwise return x."""
    -    if getattr(x, 'is_Matrix', False):
    -        return x.as_mutable()
    -    elif isinstance(x, Basic):
    -        return x
    -    elif hasattr(x, '__array__'):
    -        a = x.__array__()
    -        if len(a.shape) == 0:
    -            return sympify(a)
    -        return Matrix(x)
    -    return x
    -
    -
    -
    [docs]class MutableDenseMatrix(DenseMatrix, MatrixBase): - @classmethod - def _new(cls, *args, **kwargs): - rows, cols, flat_list = MatrixBase._handle_creation_inputs( - *args, **kwargs) - self = object.__new__(cls) - self.rows = rows - self.cols = cols - self._mat = list(flat_list) # create a shallow copy - return self - - def __new__(cls, *args, **kwargs): - return cls._new(*args, **kwargs) - - def __setitem__(self, key, value): - """ - - Examples - ======== - - >>> from sympy import Matrix, I, zeros, ones - >>> m = Matrix(((1, 2+I), (3, 4))) - >>> m - [1, 2 + I] - [3, 4] - >>> m[1, 0] = 9 - >>> m - [1, 2 + I] - [9, 4] - >>> m[1, 0] = [[0, 1]] - - To replace row r you assign to position r*m where m - is the number of columns: - - >>> M = zeros(4) - >>> m = M.cols - >>> M[3*m] = ones(1, m)*2; M - [0, 0, 0, 0] - [0, 0, 0, 0] - [0, 0, 0, 0] - [2, 2, 2, 2] - - And to replace column c you can assign to position c: - - >>> M[2] = ones(m, 1)*4; M - [0, 0, 4, 0] - [0, 0, 4, 0] - [0, 0, 4, 0] - [2, 2, 4, 2] - """ - rv = self._setitem(key, value) - if rv is not None: - i, j, value = rv - self._mat[i*self.cols + j] = value - -
    [docs] def copyin_matrix(self, key, value): - """Copy in values from a matrix into the given bounds. - - Parameters - ========== - - key : slice - The section of this matrix to replace. - value : Matrix - The matrix to copy values from. - - Examples - ======== - - >>> from sympy.matrices import Matrix, eye - >>> M = Matrix([[0, 1], [2, 3], [4, 5]]) - >>> I = eye(3) - >>> I[:3, :2] = M - >>> I - [0, 1, 0] - [2, 3, 0] - [4, 5, 1] - >>> I[0, 1] = M - >>> I - [0, 0, 1] - [2, 2, 3] - [4, 4, 5] - - See Also - ======== - - copyin_list - """ - rlo, rhi, clo, chi = self.key2bounds(key) - shape = value.shape - dr, dc = rhi - rlo, chi - clo - if shape != (dr, dc): - raise ShapeError(filldedent("The Matrix `value` doesn't have the " - "same dimensions " - "as the in sub-Matrix given by `key`.")) - - for i in range(value.rows): - for j in range(value.cols): - self[i + rlo, j + clo] = value[i, j] -
    -
    [docs] def copyin_list(self, key, value): - """Copy in elements from a list. - - Parameters - ========== - - key : slice - The section of this matrix to replace. - value : iterable - The iterable to copy values from. - - Examples - ======== - - >>> from sympy.matrices import eye - >>> I = eye(3) - >>> I[:2, 0] = [1, 2] # col - >>> I - [1, 0, 0] - [2, 1, 0] - [0, 0, 1] - >>> I[1, :2] = [[3, 4]] - >>> I - [1, 0, 0] - [3, 4, 0] - [0, 0, 1] - - See Also - ======== - - copyin_matrix - """ - if not is_sequence(value): - raise TypeError( - "`value` must be an ordered iterable, not %s." % type(value)) - return self.copyin_matrix(key, Matrix(value)) -
    -
    [docs] def row_op(self, i, f): - """In-place operation on row i using two-arg functor whose args are - interpreted as (self[i, j], j). - - Examples - ======== - - >>> from sympy.matrices import eye - >>> M = eye(3) - >>> M.row_op(1, lambda v, j: v + 2*M[0, j]); M - [1, 0, 0] - [2, 1, 0] - [0, 0, 1] - - See Also - ======== - row - col_op - """ - i0 = i*self.cols - self._mat[i0: i0 + self.cols] = [f(*t) for t in zip(self._mat[i0: i0 + self.cols], list(range(self.cols)))] -
    -
    [docs] def col_op(self, j, f): - """In-place operation on col j using two-arg functor whose args are - interpreted as (self[i, j], i). - - Examples - ======== - - >>> from sympy.matrices import eye - >>> M = eye(3) - >>> M.col_op(1, lambda v, i: v + 2*M[i, 0]); M - [1, 2, 0] - [0, 1, 0] - [0, 0, 1] - - See Also - ======== - col - row_op - """ - self._mat[j::self.cols] = [f(*t) for t in zip(self._mat[j::self.cols], list(range(self.rows)))] -
    -
    [docs] def row_swap(self, i, j): - """Swap the two given rows of the matrix in-place. - - Examples - ======== - - >>> from sympy.matrices import Matrix - >>> M = Matrix([[0, 1], [1, 0]]) - >>> M - [0, 1] - [1, 0] - >>> M.row_swap(0, 1) - >>> M - [1, 0] - [0, 1] - - See Also - ======== - - row - col_swap - """ - for k in range(0, self.cols): - self[i, k], self[j, k] = self[j, k], self[i, k] -
    -
    [docs] def col_swap(self, i, j): - """Swap the two given columns of the matrix in-place. - - Examples - ======== - - >>> from sympy.matrices import Matrix - >>> M = Matrix([[1, 0], [1, 0]]) - >>> M - [1, 0] - [1, 0] - >>> M.col_swap(0, 1) - >>> M - [0, 1] - [0, 1] - - See Also - ======== - - col - row_swap - """ - for k in range(0, self.rows): - self[k, i], self[k, j] = self[k, j], self[k, i] -
    -
    [docs] def row_del(self, i): - """Delete the given row. - - Examples - ======== - - >>> from sympy.matrices import eye - >>> M = eye(3) - >>> M.row_del(1) - >>> M - [1, 0, 0] - [0, 0, 1] - - See Also - ======== - - row - col_del - """ - self._mat = self._mat[:i*self.cols] + self._mat[(i + 1)*self.cols:] - self.rows -= 1 -
    -
    [docs] def col_del(self, i): - """Delete the given column. - - Examples - ======== - - >>> from sympy.matrices import eye - >>> M = eye(3) - >>> M.col_del(1) - >>> M - [1, 0] - [0, 0] - [0, 1] - - See Also - ======== - - col - row_del - """ - for j in range(self.rows - 1, -1, -1): - del self._mat[i + j*self.cols] - self.cols -= 1 - - # Utility functions
    -
    [docs] def simplify(self, ratio=1.7, measure=count_ops): - """Applies simplify to the elements of a matrix in place. - - This is a shortcut for M.applyfunc(lambda x: simplify(x, ratio, measure)) - - See Also - ======== - - sympy.simplify.simplify.simplify - """ - for i in range(len(self._mat)): - self._mat[i] = _simplify(self._mat[i], ratio=ratio, - measure=measure) -
    -
    [docs] def fill(self, value): - """Fill the matrix with the scalar value. - - See Also - ======== - - zeros - ones - """ - self._mat = [value]*len(self) -
    -MutableMatrix = Matrix = MutableDenseMatrix - -########### -# Numpy Utility Functions: -# list2numpy, matrix2numpy, symmarray, rot_axis[123] -########### - - -
    [docs]def list2numpy(l): # pragma: no cover - """Converts python list of SymPy expressions to a NumPy array. - - See Also - ======== - - matrix2numpy - """ - from numpy import empty - a = empty(len(l), dtype=object) - for i, s in enumerate(l): - a[i] = s - return a - -
    -
    [docs]def matrix2numpy(m): # pragma: no cover - """Converts SymPy's matrix to a NumPy array. - - See Also - ======== - - list2numpy - """ - from numpy import empty - a = empty(m.shape, dtype=object) - for i in range(m.rows): - for j in range(m.cols): - a[i, j] = m[i, j] - return a - -
    -
    [docs]def symarray(prefix, shape): # pragma: no cover - """Create a numpy ndarray of symbols (as an object array). - - The created symbols are named ``prefix_i1_i2_``... You should thus provide a - non-empty prefix if you want your symbols to be unique for different output - arrays, as SymPy symbols with identical names are the same object. - - Parameters - ---------- - - prefix : string - A prefix prepended to the name of every symbol. - - shape : int or tuple - Shape of the created array. If an int, the array is one-dimensional; for - more than one dimension the shape must be a tuple. - - Examples - -------- - These doctests require numpy. - - >>> from sympy import symarray - >>> symarray('', 3) #doctest: +SKIP - [_0, _1, _2] - - If you want multiple symarrays to contain distinct symbols, you *must* - provide unique prefixes: - - >>> a = symarray('', 3) #doctest: +SKIP - >>> b = symarray('', 3) #doctest: +SKIP - >>> a[0] is b[0] #doctest: +SKIP - True - >>> a = symarray('a', 3) #doctest: +SKIP - >>> b = symarray('b', 3) #doctest: +SKIP - >>> a[0] is b[0] #doctest: +SKIP - False - - Creating symarrays with a prefix: - - >>> symarray('a', 3) #doctest: +SKIP - [a_0, a_1, a_2] - - For more than one dimension, the shape must be given as a tuple: - - >>> symarray('a', (2, 3)) #doctest: +SKIP - [[a_0_0, a_0_1, a_0_2], - [a_1_0, a_1_1, a_1_2]] - >>> symarray('a', (2, 3, 2)) #doctest: +SKIP - [[[a_0_0_0, a_0_0_1], - [a_0_1_0, a_0_1_1], - [a_0_2_0, a_0_2_1]], - <BLANKLINE> - [[a_1_0_0, a_1_0_1], - [a_1_1_0, a_1_1_1], - [a_1_2_0, a_1_2_1]]] - - """ - from numpy import empty, ndindex - arr = empty(shape, dtype=object) - for index in ndindex(shape): - arr[index] = Symbol('%s_%s' % (prefix, '_'.join(map(str, index)))) - return arr - -
    -
    [docs]def rot_axis3(theta): - """Returns a rotation matrix for a rotation of theta (in radians) about - the 3-axis. - - Examples - ======== - - >>> from sympy import pi - >>> from sympy.matrices import rot_axis3 - - A rotation of pi/3 (60 degrees): - - >>> theta = pi/3 - >>> rot_axis3(theta) - [ 1/2, sqrt(3)/2, 0] - [-sqrt(3)/2, 1/2, 0] - [ 0, 0, 1] - - If we rotate by pi/2 (90 degrees): - - >>> rot_axis3(pi/2) - [ 0, 1, 0] - [-1, 0, 0] - [ 0, 0, 1] - - See Also - ======== - - rot_axis1: Returns a rotation matrix for a rotation of theta (in radians) - about the 1-axis - rot_axis2: Returns a rotation matrix for a rotation of theta (in radians) - about the 2-axis - """ - ct = cos(theta) - st = sin(theta) - lil = ((ct, st, 0), - (-st, ct, 0), - (0, 0, 1)) - return Matrix(lil) - -
    -
    [docs]def rot_axis2(theta): - """Returns a rotation matrix for a rotation of theta (in radians) about - the 2-axis. - - Examples - ======== - - >>> from sympy import pi - >>> from sympy.matrices import rot_axis2 - - A rotation of pi/3 (60 degrees): - - >>> theta = pi/3 - >>> rot_axis2(theta) - [ 1/2, 0, -sqrt(3)/2] - [ 0, 1, 0] - [sqrt(3)/2, 0, 1/2] - - If we rotate by pi/2 (90 degrees): - - >>> rot_axis2(pi/2) - [0, 0, -1] - [0, 1, 0] - [1, 0, 0] - - See Also - ======== - - rot_axis1: Returns a rotation matrix for a rotation of theta (in radians) - about the 1-axis - rot_axis3: Returns a rotation matrix for a rotation of theta (in radians) - about the 3-axis - """ - ct = cos(theta) - st = sin(theta) - lil = ((ct, 0, -st), - (0, 1, 0), - (st, 0, ct)) - return Matrix(lil) - -
    -
    [docs]def rot_axis1(theta): - """Returns a rotation matrix for a rotation of theta (in radians) about - the 1-axis. - - Examples - ======== - - >>> from sympy import pi - >>> from sympy.matrices import rot_axis1 - - A rotation of pi/3 (60 degrees): - - >>> theta = pi/3 - >>> rot_axis1(theta) - [1, 0, 0] - [0, 1/2, sqrt(3)/2] - [0, -sqrt(3)/2, 1/2] - - If we rotate by pi/2 (90 degrees): - - >>> rot_axis1(pi/2) - [1, 0, 0] - [0, 0, 1] - [0, -1, 0] - - See Also - ======== - - rot_axis2: Returns a rotation matrix for a rotation of theta (in radians) - about the 2-axis - rot_axis3: Returns a rotation matrix for a rotation of theta (in radians) - about the 3-axis - """ - ct = cos(theta) - st = sin(theta) - lil = ((1, 0, 0), - (0, ct, st), - (0, -st, ct)) - return Matrix(lil) - -############### -# Functions -############### - -
    -def matrix_add(A, B): - SymPyDeprecationWarning( - feature="matrix_add(A, B)", - useinstead="A + B", - deprecated_since_version="0.7.2", - ).warn() - return A + B - - -def matrix_multiply(A, B): - SymPyDeprecationWarning( - feature="matrix_multiply(A, B)", - useinstead="A*B", - deprecated_since_version="0.7.2", - ).warn() - return A*B - - -
    [docs]def matrix_multiply_elementwise(A, B): - """Return the Hadamard product (elementwise product) of A and B - - >>> from sympy.matrices import matrix_multiply_elementwise - >>> from sympy.matrices import Matrix - >>> A = Matrix([[0, 1, 2], [3, 4, 5]]) - >>> B = Matrix([[1, 10, 100], [100, 10, 1]]) - >>> matrix_multiply_elementwise(A, B) - [ 0, 10, 200] - [300, 40, 5] - - See Also - ======== - - __mul__ - """ - if A.shape != B.shape: - raise ShapeError() - shape = A.shape - return classof(A, B)._new(shape[0], shape[1], - lambda i, j: A[i, j]*B[i, j]) - -
    -
    [docs]def ones(r, c=None): - """Returns a matrix of ones with ``r`` rows and ``c`` columns; - if ``c`` is omitted a square matrix will be returned. - - See Also - ======== - - zeros - eye - diag - """ - from .dense import Matrix - - if is_sequence(r): - SymPyDeprecationWarning( - feature="The syntax ones([%i, %i])" % tuple(r), - useinstead="ones(%i, %i)." % tuple(r), - issue=3381, deprecated_since_version="0.7.2", - ).warn() - r, c = r - else: - c = r if c is None else c - r = as_int(r) - c = as_int(c) - return Matrix(r, c, [S.One]*r*c) - -
    -
    [docs]def zeros(r, c=None, cls=None): - """Returns a matrix of zeros with ``r`` rows and ``c`` columns; - if ``c`` is omitted a square matrix will be returned. - - See Also - ======== - - ones - eye - diag - """ - if cls is None: - from .dense import Matrix as cls - return cls.zeros(r, c) - -
    -
    [docs]def eye(n, cls=None): - """Create square identity matrix n x n - - See Also - ======== - - diag - zeros - ones - """ - if cls is None: - from sympy.matrices import Matrix as cls - return cls.eye(n) -
    -
    [docs]def diag(*values, **kwargs): - """Create a sparse, diagonal matrix from a list of diagonal values. - - Notes - ===== - - When arguments are matrices they are fitted in resultant matrix. - - The returned matrix is a mutable, dense matrix. To make it a different - type, send the desired class for keyword ``cls``. - - Examples - ======== - - >>> from sympy.matrices import diag, Matrix, ones - >>> diag(1, 2, 3) - [1, 0, 0] - [0, 2, 0] - [0, 0, 3] - >>> diag(*[1, 2, 3]) - [1, 0, 0] - [0, 2, 0] - [0, 0, 3] - - The diagonal elements can be matrices; diagonal filling will - continue on the diagonal from the last element of the matrix: - - >>> from sympy.abc import x, y, z - >>> a = Matrix([x, y, z]) - >>> b = Matrix([[1, 2], [3, 4]]) - >>> c = Matrix([[5, 6]]) - >>> diag(a, 7, b, c) - [x, 0, 0, 0, 0, 0] - [y, 0, 0, 0, 0, 0] - [z, 0, 0, 0, 0, 0] - [0, 7, 0, 0, 0, 0] - [0, 0, 1, 2, 0, 0] - [0, 0, 3, 4, 0, 0] - [0, 0, 0, 0, 5, 6] - - When diagonal elements are lists, they will be treated as arguments - to Matrix: - - >>> diag([1, 2, 3], 4) - [1, 0] - [2, 0] - [3, 0] - [0, 4] - >>> diag([[1, 2, 3]], 4) - [1, 2, 3, 0] - [0, 0, 0, 4] - - A given band off the diagonal can be made by padding with a - vertical or horizontal "kerning" vector: - - >>> hpad = ones(0, 2) - >>> vpad = ones(2, 0) - >>> diag(vpad, 1, 2, 3, hpad) + diag(hpad, 4, 5, 6, vpad) - [0, 0, 4, 0, 0] - [0, 0, 0, 5, 0] - [1, 0, 0, 0, 6] - [0, 2, 0, 0, 0] - [0, 0, 3, 0, 0] - - - The type is mutable by default but can be made immutable by setting - the ``mutable`` flag to False: - - >>> type(diag(1)) - <class 'sympy.matrices.dense.MutableDenseMatrix'> - >>> from sympy.matrices import ImmutableMatrix - >>> type(diag(1, cls=ImmutableMatrix)) - <class 'sympy.matrices.immutable.ImmutableMatrix'> - - See Also - ======== - - eye - """ - from .sparse import MutableSparseMatrix - - cls = kwargs.pop('cls', None) - if cls is None: - from .dense import Matrix as cls - - if kwargs: - raise ValueError('unrecognized keyword%s: %s' % ( - 's' if len(kwargs) > 1 else '', - ', '.join(list(kwargs.keys())))) - rows = 0 - cols = 0 - values = list(values) - for i in range(len(values)): - m = values[i] - if isinstance(m, MatrixBase): - rows += m.rows - cols += m.cols - elif is_sequence(m): - m = values[i] = Matrix(m) - rows += m.rows - cols += m.cols - else: - rows += 1 - cols += 1 - res = MutableSparseMatrix.zeros(rows, cols) - i_row = 0 - i_col = 0 - for m in values: - if isinstance(m, MatrixBase): - res[i_row:i_row + m.rows, i_col:i_col + m.cols] = m - i_row += m.rows - i_col += m.cols - else: - res[i_row, i_col] = m - i_row += 1 - i_col += 1 - return cls._new(res) - -
    -
    [docs]def jordan_cell(eigenval, n): - """ - Create matrix of Jordan cell kind: - - Examples - ======== - - >>> from sympy.matrices import jordan_cell - >>> from sympy.abc import x - >>> jordan_cell(x, 4) - [x, 1, 0, 0] - [0, x, 1, 0] - [0, 0, x, 1] - [0, 0, 0, x] - """ - n = as_int(n) - out = zeros(n) - for i in range(n - 1): - out[i, i] = eigenval - out[i, i + 1] = S.One - out[n - 1, n - 1] = eigenval - return out - -
    -
    [docs]def hessian(f, varlist, constraints=[]): - """Compute Hessian matrix for a function f wrt parameters in varlist - which may be given as a sequence or a row/column vector. A list of - constraints may optionally be given. - - Examples - ======== - - >>> from sympy import Function, hessian, pprint - >>> from sympy.abc import x, y - >>> f = Function('f')(x, y) - >>> g1 = Function('g')(x, y) - >>> g2 = x**2 + 3*y - >>> pprint(hessian(f, (x, y), [g1, g2])) - [ d d ] - [ 0 0 --(g(x, y)) --(g(x, y)) ] - [ dx dy ] - [ ] - [ 0 0 2*x 3 ] - [ ] - [ 2 2 ] - [d d d ] - [--(g(x, y)) 2*x ---(f(x, y)) -----(f(x, y))] - [dx 2 dy dx ] - [ dx ] - [ ] - [ 2 2 ] - [d d d ] - [--(g(x, y)) 3 -----(f(x, y)) ---(f(x, y)) ] - [dy dy dx 2 ] - [ dy ] - - References - ========== - - http://en.wikipedia.org/wiki/Hessian_matrix - - See Also - ======== - - sympy.matrices.mutable.Matrix.jacobian - wronskian - """ - # f is the expression representing a function f, return regular matrix - if isinstance(varlist, MatrixBase): - if 1 not in varlist.shape: - raise ShapeError("`varlist` must be a column or row vector.") - if varlist.cols == 1: - varlist = varlist.T - varlist = varlist.tolist()[0] - if is_sequence(varlist): - n = len(varlist) - if not n: - raise ShapeError("`len(varlist)` must not be zero.") - else: - raise ValueError("Improper variable list in hessian function") - if not getattr(f, 'diff'): - # check differentiability - raise ValueError("Function `f` (%s) is not differentiable" % f) - m = len(constraints) - N = m + n - out = zeros(N) - for k, g in enumerate(constraints): - if not getattr(g, 'diff'): - # check differentiability - raise ValueError("Function `f` (%s) is not differentiable" % f) - for i in range(n): - out[k, i + m] = g.diff(varlist[i]) - for i in range(n): - for j in range(i, n): - out[i + m, j + m] = f.diff(varlist[i]).diff(varlist[j]) - for i in range(N): - for j in range(i + 1, N): - out[j, i] = out[i, j] - return out - -
    -
    [docs]def GramSchmidt(vlist, orthog=False): - """ - Apply the Gram-Schmidt process to a set of vectors. - - see: http://en.wikipedia.org/wiki/Gram%E2%80%93Schmidt_process - """ - out = [] - m = len(vlist) - for i in range(m): - tmp = vlist[i] - for j in range(i): - tmp -= vlist[i].project(out[j]) - if not list(tmp.values()): - raise ValueError( - "GramSchmidt: vector set not linearly independent") - out.append(tmp) - if orthog: - for i in range(len(out)): - out[i] = out[i].normalized() - return out - -
    -
    [docs]def wronskian(functions, var, method='bareis'): - """ - Compute Wronskian for [] of functions - - :: - - | f1 f2 ... fn | - | f1' f2' ... fn' | - | . . . . | - W(f1, ..., fn) = | . . . . | - | . . . . | - | (n) (n) (n) | - | D (f1) D (f2) ... D (fn) | - - see: http://en.wikipedia.org/wiki/Wronskian - - See Also - ======== - - sympy.matrices.mutable.Matrix.jacobian - hessian - """ - from .dense import Matrix - - for index in range(0, len(functions)): - functions[index] = sympify(functions[index]) - n = len(functions) - if n == 0: - return 1 - W = Matrix(n, n, lambda i, j: functions[i].diff(var, j)) - return W.det(method) - -
    -
    [docs]def casoratian(seqs, n, zero=True): - """Given linear difference operator L of order 'k' and homogeneous - equation Ly = 0 we want to compute kernel of L, which is a set - of 'k' sequences: a(n), b(n), ... z(n). - - Solutions of L are linearly independent iff their Casoratian, - denoted as C(a, b, ..., z), do not vanish for n = 0. - - Casoratian is defined by k x k determinant:: - - + a(n) b(n) . . . z(n) + - | a(n+1) b(n+1) . . . z(n+1) | - | . . . . | - | . . . . | - | . . . . | - + a(n+k-1) b(n+k-1) . . . z(n+k-1) + - - It proves very useful in rsolve_hyper() where it is applied - to a generating set of a recurrence to factor out linearly - dependent solutions and return a basis: - - >>> from sympy import Symbol, casoratian, factorial - >>> n = Symbol('n', integer=True) - - Exponential and factorial are linearly independent: - - >>> casoratian([2**n, factorial(n)], n) != 0 - True - - """ - from .dense import Matrix - - seqs = list(map(sympify, seqs)) - - if not zero: - f = lambda i, j: seqs[j].subs(n, n + i) - else: - f = lambda i, j: seqs[j].subs(n, i) - - k = len(seqs) - - return Matrix(k, k, f).det() - -
    -
    [docs]def randMatrix(r, c=None, min=0, max=99, seed=None, symmetric=False, percent=100): - """Create random matrix with dimensions ``r`` x ``c``. If ``c`` is omitted - the matrix will be square. If ``symmetric`` is True the matrix must be - square. If ``percent`` is less than 100 then only approximately the given - percentage of elements will be non-zero. - - Examples - ======== - - >>> from sympy.matrices import randMatrix - >>> randMatrix(3) # doctest:+SKIP - [25, 45, 27] - [44, 54, 9] - [23, 96, 46] - >>> randMatrix(3, 2) # doctest:+SKIP - [87, 29] - [23, 37] - [90, 26] - >>> randMatrix(3, 3, 0, 2) # doctest:+SKIP - [0, 2, 0] - [2, 0, 1] - [0, 0, 1] - >>> randMatrix(3, symmetric=True) # doctest:+SKIP - [85, 26, 29] - [26, 71, 43] - [29, 43, 57] - >>> A = randMatrix(3, seed=1) - >>> B = randMatrix(3, seed=2) - >>> A == B # doctest:+SKIP - False - >>> A == randMatrix(3, seed=1) - True - >>> randMatrix(3, symmetric=True, percent=50) # doctest:+SKIP - [0, 68, 43] - [0, 68, 0] - [0, 91, 34] - """ - if c is None: - c = r - if seed is None: - prng = random.Random() # use system time - else: - prng = random.Random(seed) - if symmetric and r != c: - raise ValueError( - 'For symmetric matrices, r must equal c, but %i != %i' % (r, c)) - if not symmetric: - m = Matrix._new(r, c, lambda i, j: prng.randint(min, max)) - else: - m = zeros(r) - for i in range(r): - for j in range(i, r): - m[i, j] = prng.randint(min, max) - for i in range(r): - for j in range(i): - m[i, j] = m[j, i] - if percent == 100: - return m - else: - z = int(r*c*percent // 100) - m._mat[:z] = [S.Zero]*z - random.shuffle(m._mat) - return m
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/matrices/expressions.html b/dev-py3k/_modules/sympy/matrices/expressions.html deleted file mode 100644 index ecda60ac239..00000000000 --- a/dev-py3k/_modules/sympy/matrices/expressions.html +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - - - - - sympy.matrices.expressions — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.matrices.expressions

    -""" A module which handles Matrix Expressions """
    -
    -from .blockmatrix import BlockMatrix, BlockDiagMatrix, block_collapse
    -from .funcmatrix import FunctionMatrix
    -from .inverse import Inverse
    -from .matadd import MatAdd
    -from .matexpr import (Identity, MatrixExpr, MatrixSymbol, ZeroMatrix,
    -     matrix_symbols)
    -from .matmul import MatMul
    -from .matpow import MatPow
    -from .trace import Trace
    -from .transpose import Transpose
    -from .adjoint import Adjoint
    -from .hadamard import hadamard_product, HadamardProduct
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/matrices/expressions/blockmatrix.html b/dev-py3k/_modules/sympy/matrices/expressions/blockmatrix.html deleted file mode 100644 index 4c9055e6c41..00000000000 --- a/dev-py3k/_modules/sympy/matrices/expressions/blockmatrix.html +++ /dev/null @@ -1,481 +0,0 @@ - - - - - - - - - - sympy.matrices.expressions.blockmatrix — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.matrices.expressions.blockmatrix

    -from sympy.core import Tuple, Basic, Add
    -from sympy.rules import typed, canon, debug, do_one, unpack
    -from sympy.functions import transpose
    -from sympy.utilities import sift
    -
    -from sympy.matrices.expressions.matexpr import MatrixExpr, ZeroMatrix, Identity
    -from sympy.matrices.expressions.matmul import MatMul
    -from sympy.matrices.expressions.matadd import MatAdd
    -from sympy.matrices.expressions.matpow import MatPow
    -from sympy.matrices.expressions.transpose import Transpose
    -from sympy.matrices.expressions.trace import Trace
    -from sympy.matrices.expressions.inverse import Inverse
    -from sympy.matrices import Matrix, eye
    -
    -
    -
    [docs]class BlockMatrix(MatrixExpr): - """A BlockMatrix is a Matrix composed of other smaller, submatrices - - The submatrices are stored in a SymPy Matrix object but accessed as part of - a Matrix Expression - - >>> from sympy import (MatrixSymbol, BlockMatrix, symbols, - ... Identity, ZeroMatrix, block_collapse) - >>> n,m,l = symbols('n m l') - >>> X = MatrixSymbol('X', n, n) - >>> Y = MatrixSymbol('Y', m ,m) - >>> Z = MatrixSymbol('Z', n, m) - >>> B = BlockMatrix([[X, Z], [ZeroMatrix(m,n), Y]]) - >>> print(B) - [X, Z] - [0, Y] - - >>> C = BlockMatrix([[Identity(n), Z]]) - >>> print(C) - [I, Z] - - >>> print(block_collapse(C*B)) - [X, Z + Z*Y] - - """ - def __new__(cls, *args): - from sympy.matrices.immutable import ImmutableMatrix - mat = ImmutableMatrix(*args) - - obj = Basic.__new__(cls, mat) - return obj - - @property - def shape(self): - numrows = numcols = 0 - M = self.blocks - for i in range(M.shape[0]): - numrows += M[i, 0].shape[0] - for i in range(M.shape[1]): - numcols += M[0, i].shape[1] - return (numrows, numcols) - - @property - def blockshape(self): - return self.blocks.shape - - @property - def blocks(self): - return self.args[0] - - @property - def rowblocksizes(self): - return [self.blocks[i, 0].rows for i in range(self.blockshape[0])] - - @property - def colblocksizes(self): - return [self.blocks[0, i].cols for i in range(self.blockshape[1])] - - def structurally_equal(self, other): - return (isinstance(other, BlockMatrix) - and self.shape == other.shape - and self.blockshape == other.blockshape - and self.rowblocksizes == other.rowblocksizes - and self.colblocksizes == other.colblocksizes) - - def _blockmul(self, other): - if (isinstance(other, BlockMatrix) and - self.colblocksizes == other.rowblocksizes): - return BlockMatrix(self.blocks*other.blocks) - - return self * other - - def _blockadd(self, other): - if (isinstance(other, BlockMatrix) - and self.structurally_equal(other)): - return BlockMatrix(self.blocks + other.blocks) - - return self + other - - def _eval_transpose(self): - # Flip all the individual matrices - matrices = [transpose(matrix) for matrix in self.blocks] - # Make a copy - M = Matrix(self.blockshape[0], self.blockshape[1], matrices) - # Transpose the block structure - M = M.transpose() - return BlockMatrix(M) - - def _eval_trace(self): - if self.rowblocksizes == self.colblocksizes: - return Add(*[Trace(self.blocks[i, i]) - for i in range(self.blockshape[0])]) - raise NotImplementedError( - "Can't perform trace of irregular blockshape") - -
    [docs] def transpose(self): - """Return transpose of matrix. - - Examples - ======== - - >>> from sympy import MatrixSymbol, BlockMatrix, ZeroMatrix - >>> from sympy.abc import l, m, n - >>> X = MatrixSymbol('X', n, n) - >>> Y = MatrixSymbol('Y', m ,m) - >>> Z = MatrixSymbol('Z', n, m) - >>> B = BlockMatrix([[X, Z], [ZeroMatrix(m,n), Y]]) - >>> B.transpose() - [X', 0] - [Z', Y'] - >>> _.transpose() - [X, Z] - [0, Y] - """ - return self._eval_transpose() -
    - def _eval_inverse(self, expand=False): - # Inverse of one by one block matrix is easy - if self.blockshape == (1, 1): - mat = Matrix(1, 1, (self.blocks[0].inverse(),)) - return BlockMatrix(mat) - # Inverse of a two by two block matrix is known - elif expand and self.blockshape == (2, 2): - # Cite: The Matrix Cookbook Section 9.1.3 - A11, A12, A21, A22 = (self.blocks[0, 0], self.blocks[0, 1], - self.blocks[1, 0], self.blocks[1, 1]) - C1 = A11 - A12*A22.I*A21 - C2 = A22 - A21*A11.I*A12 - mat = Matrix([[C1.I, (-A11).I*A12*C2.I], - [-C2.I*A21*A11.I, C2.I]]) - return BlockMatrix(mat) - else: - return Inverse(self) - -
    [docs] def inverse(self, expand=False): - """Return inverse of matrix. - - Examples - ======== - - >>> from sympy import MatrixSymbol, BlockMatrix, ZeroMatrix - >>> from sympy.abc import l, m, n - >>> X = MatrixSymbol('X', n, n) - >>> BlockMatrix([[X]]).inverse() - [X^-1] - - >>> Y = MatrixSymbol('Y', m ,m) - >>> Z = MatrixSymbol('Z', n, m) - >>> B = BlockMatrix([[X, Z], [ZeroMatrix(m,n), Y]]) - >>> B - [X, Z] - [0, Y] - >>> B.inverse(expand=True) - [X^-1, (-1)*X^-1*Z*Y^-1] - [ 0, Y^-1] - - """ - return self._eval_inverse(expand) -
    - def _entry(self, i, j): - # Find row entry - for row_block, numrows in enumerate(self.rowblocksizes): - if i < numrows: - break - else: - i -= numrows - for col_block, numcols in enumerate(self.colblocksizes): - if j < numcols: - break - else: - j -= numcols - return self.blocks[row_block, col_block][i, j] - - @property - def is_Identity(self): - if self.blockshape[0] != self.blockshape[1]: - return False - for i in range(self.blockshape[0]): - for j in range(self.blockshape[1]): - if i==j and not self.blocks[i, j].is_Identity: - return False - if i!=j and not self.blocks[i, j].is_ZeroMatrix: - return False - return True - - @property - def is_structurally_symmetric(self): - return self.rowblocksizes == self.colblocksizes - - def equals(self, other): - if self == other: - return True - if (isinstance(other, BlockMatrix) and self.blocks == other.blocks): - return True - return super(BlockMatrix, self).equals(other) -
    -
    [docs]class BlockDiagMatrix(BlockMatrix): - """ - A BlockDiagMatrix is a BlockMatrix with matrices only along the diagonal - - >>> from sympy import MatrixSymbol, BlockDiagMatrix, symbols, Identity - >>> n,m,l = symbols('n m l') - >>> X = MatrixSymbol('X', n, n) - >>> Y = MatrixSymbol('Y', m ,m) - >>> BlockDiagMatrix(X, Y) - [X, 0] - [0, Y] - - """ - def __new__(cls, *mats): - return Basic.__new__(BlockDiagMatrix, *mats) - - @property - def diag(self): - return self.args - - @property - def blocks(self): - from sympy.matrices.immutable import ImmutableMatrix - mats = self.args - data = [[mats[i] if i == j else ZeroMatrix(mats[i].rows, mats[j].cols) - for j in range(len(mats))] - for i in range(len(mats))] - return ImmutableMatrix(data) - - @property - def shape(self): - return (sum(block.rows for block in self.args), - sum(block.cols for block in self.args)) - - @property - def blockshape(self): - n = len(self.args) - return (n, n) - - @property - def rowblocksizes(self): - return [block.rows for block in self.args] - - @property - def colblocksizes(self): - return [block.cols for block in self.args] - - def _eval_inverse(self, expand='ignored'): - return BlockDiagMatrix(*[mat.inverse() for mat in self.args]) - - def _blockmul(self, other): - if (isinstance(other, BlockDiagMatrix) and - self.colblocksizes == other.rowblocksizes): - return BlockDiagMatrix(*[a*b for a, b in zip(self.args, other.args)]) - else: - return BlockMatrix._blockmul(self, other) - - def _blockadd(self, other): - if (isinstance(other, BlockDiagMatrix) and - self.blockshape == other.blockshape and - self.rowblocksizes == other.rowblocksizes and - self.colblocksizes == other.colblocksizes): - return BlockDiagMatrix(*[a + b for a, b in zip(self.args, other.args)]) - else: - return BlockMatrix._blockadd(self, other) - -
    -
    [docs]def block_collapse(expr): - """Evaluates a block matrix expression - - >>> from sympy import MatrixSymbol, BlockMatrix, symbols, \ - Identity, Matrix, ZeroMatrix, block_collapse - >>> n,m,l = symbols('n m l') - >>> X = MatrixSymbol('X', n, n) - >>> Y = MatrixSymbol('Y', m ,m) - >>> Z = MatrixSymbol('Z', n, m) - >>> B = BlockMatrix([[X, Z], [ZeroMatrix(m, n), Y]]) - >>> print(B) - [X, Z] - [0, Y] - - >>> C = BlockMatrix([[Identity(n), Z]]) - >>> print(C) - [I, Z] - - >>> print(block_collapse(C*B)) - [X, Z + Z*Y] - """ - rule = canon(typed({MatAdd: do_one(bc_matadd, bc_block_plus_ident), - MatMul: do_one(bc_matmul, bc_dist), - BlockMatrix: bc_unpack})) - result = rule(expr) - try: - return result.doit() - except AttributeError: - return result -
    -def bc_unpack(expr): - if expr.blockshape == (1, 1): - return expr.blocks[0, 0] - return expr - -def bc_matadd(expr): - args = sift(expr.args, lambda M: isinstance(M, BlockMatrix)) - blocks = args[True] - if not blocks: - return expr - - nonblocks = args[False] - block = blocks[0] - for b in blocks[1:]: - block = block._blockadd(b) - if nonblocks: - return MatAdd(*nonblocks) + block - else: - return block - -def bc_block_plus_ident(expr): - idents = [arg for arg in expr.args if arg.is_Identity] - if not idents: - return expr - - blocks = [arg for arg in expr.args if isinstance(arg, BlockMatrix)] - if (blocks and all(b.structurally_equal(blocks[0]) for b in blocks) - and blocks[0].is_structurally_symmetric): - block_id = BlockDiagMatrix(*[Identity(k) - for k in blocks[0].rowblocksizes]) - return MatAdd(block_id * len(idents), *blocks).doit() - - return expr - -def bc_dist(expr): - """ Turn a*[X, Y] into [a*X, a*Y] """ - factor, mat = expr.as_coeff_mmul() - if factor != 1 and isinstance(unpack(mat), BlockMatrix): - B = unpack(mat).blocks - return BlockMatrix([[factor * B[i, j] for j in range(B.cols)] - for i in range(B.rows)]) - return expr - -def bc_matmul(expr): - factor, matrices = expr.as_coeff_matrices() - i = 0 - while (i+1 < len(matrices)): - A, B = matrices[i:i+2] - if isinstance(A, BlockMatrix) and isinstance(B, BlockMatrix): - matrices[i] = A._blockmul(B) - matrices.pop(i+1) - else: - i+=1 - return MatMul(factor, *matrices).doit() -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/matrices/immutable.html b/dev-py3k/_modules/sympy/matrices/immutable.html deleted file mode 100644 index 95373a344e4..00000000000 --- a/dev-py3k/_modules/sympy/matrices/immutable.html +++ /dev/null @@ -1,252 +0,0 @@ - - - - - - - - - - sympy.matrices.immutable — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.matrices.immutable

    -from sympy.core import Basic, Integer, Tuple, Dict
    -from sympy.core.sympify import converter as sympify_converter
    -
    -from sympy.matrices.matrices import MatrixBase
    -from sympy.matrices.dense import DenseMatrix
    -from sympy.matrices.sparse import SparseMatrix, MutableSparseMatrix
    -from sympy.matrices.expressions import MatrixExpr
    -
    -
    -def sympify_matrix(arg):
    -    return ImmutableMatrix(arg)
    -sympify_converter[MatrixBase] = sympify_matrix
    -
    -
    [docs]class ImmutableMatrix(MatrixExpr, DenseMatrix): - """Create an immutable version of a matrix. - - Examples - ======== - - >>> from sympy import eye - >>> from sympy.matrices import ImmutableMatrix - >>> ImmutableMatrix(eye(3)) - [1, 0, 0] - [0, 1, 0] - [0, 0, 1] - >>> _[0, 0] = 42 - Traceback (most recent call last): - ... - TypeError: Cannot set values of ImmutableDenseMatrix - """ - - _class_priority = 8 - - @classmethod - def _new(cls, *args, **kwargs): - if len(args) == 1 and isinstance(args[0], ImmutableMatrix): - return args[0] - rows, cols, flat_list = MatrixBase._handle_creation_inputs( - *args, **kwargs) - rows = Integer(rows) - cols = Integer(cols) - mat = Tuple(*flat_list) - return Basic.__new__(cls, rows, cols, mat) - - def __new__(cls, *args, **kwargs): - return cls._new(*args, **kwargs) - - @property - def shape(self): - return tuple([int(i) for i in self.args[:2]]) - - @property - def _mat(self): - return list(self.args[2]) - - def _entry(self, i, j): - return DenseMatrix.__getitem__(self, (i, j)) - - __getitem__ = DenseMatrix.__getitem__ - - def __setitem__(self, *args): - raise TypeError("Cannot set values of ImmutableMatrix") - - adjoint = MatrixBase.adjoint - conjugate = MatrixBase.conjugate - # C and T are defined in MatrixExpr...I don't know why C alone - # needs to be defined here - C = MatrixBase.C - - as_mutable = DenseMatrix.as_mutable - _eval_trace = DenseMatrix._eval_trace - _eval_transpose = DenseMatrix._eval_transpose - _eval_conjugate = DenseMatrix._eval_conjugate - _eval_adjoint = DenseMatrix._eval_adjoint - _eval_inverse = DenseMatrix._eval_inverse - _eval_simplify = DenseMatrix._eval_simplify - - equals = DenseMatrix.equals - is_Identity = DenseMatrix.is_Identity - - __add__ = MatrixBase.__add__ - __radd__ = MatrixBase.__radd__ - __mul__ = MatrixBase.__mul__ - __rmul__ = MatrixBase.__rmul__ - __pow__ = MatrixBase.__pow__ - __sub__ = MatrixBase.__sub__ - __rsub__ = MatrixBase.__rsub__ - __neg__ = MatrixBase.__neg__ - __div__ = MatrixBase.__div__ - __truediv__ = MatrixBase.__truediv__ - -
    -
    [docs]class ImmutableSparseMatrix(Basic, SparseMatrix): - """Create an immutable version of a sparse matrix. - - Examples - ======== - - >>> from sympy import eye - >>> from sympy.matrices.immutable import ImmutableSparseMatrix - >>> ImmutableSparseMatrix(1, 1, {}) - [0] - >>> ImmutableSparseMatrix(eye(3)) - [1, 0, 0] - [0, 1, 0] - [0, 0, 1] - >>> _[0, 0] = 42 - Traceback (most recent call last): - ... - TypeError: Cannot set values of ImmutableSparseMatrix - >>> _.shape - (3, 3) - """ - - _class_priority = 9 - - @classmethod - def _new(cls, *args, **kwargs): - s = MutableSparseMatrix(*args) - rows = Integer(s.rows) - cols = Integer(s.cols) - mat = Dict(s._smat) - obj = Basic.__new__(cls, rows, cols, mat) - obj.rows = s.rows - obj.cols = s.cols - obj._smat = s._smat - return obj - - def __new__(cls, *args, **kwargs): - return cls._new(*args, **kwargs) - - def __setitem__(self, *args): - raise TypeError("Cannot set values of ImmutableSparseMatrix") - - subs = MatrixBase.subs
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/matrices/matrices.html b/dev-py3k/_modules/sympy/matrices/matrices.html deleted file mode 100644 index ef314056b05..00000000000 --- a/dev-py3k/_modules/sympy/matrices/matrices.html +++ /dev/null @@ -1,3659 +0,0 @@ - - - - - - - - - - sympy.matrices.matrices — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.matrices.matrices

    -from sympy.core.add import Add
    -from sympy.core.basic import Basic, C
    -from sympy.core.expr import Expr
    -from sympy.core.function import count_ops
    -from sympy.core.power import Pow
    -from sympy.core.symbol import Symbol, Dummy
    -from sympy.core.numbers import Integer, ilcm, Rational, Float
    -from sympy.core.singleton import S
    -from sympy.core.sympify import sympify, SympifyError
    -from sympy.core.compatibility import is_sequence, default_sort_key
    -
    -from sympy.polys import PurePoly, roots, cancel
    -from sympy.simplify import simplify as _simplify, signsimp, nsimplify
    -from sympy.utilities.iterables import flatten
    -from sympy.functions.elementary.miscellaneous import sqrt, Max, Min
    -from sympy.printing import sstr
    -from sympy.core.compatibility import callable, reduce, as_int
    -from sympy.utilities.exceptions import SymPyDeprecationWarning
    -
    -from types import FunctionType
    -import collections
    -from functools import reduce
    -
    -
    -def _iszero(x):
    -    """Returns True if x is zero."""
    -    return x.is_zero
    -
    -
    -
    [docs]class MatrixError(Exception): - pass - -
    -
    [docs]class ShapeError(ValueError, MatrixError): - """Wrong matrix shape""" - pass - -
    -
    [docs]class NonSquareMatrixError(ShapeError): - pass - -
    -class DeferredVector(Symbol): - """A vector whose components are deferred (e.g. for use with lambdify) - - Examples - ======== - - >>> from sympy import DeferredVector, lambdify - >>> X = DeferredVector( 'X' ) - >>> X - X - >>> expr = (X[0] + 2, X[2] + 3) - >>> func = lambdify( X, expr ) - >>> func( [1, 2, 3] ) - (3, 6) - """ - def __getitem__(self, i): - if i == -0: - i = 0 - if i < 0: - raise IndexError('DeferredVector index out of range') - component_name = '%s[%d]' % (self.name, i) - return Symbol(component_name) - - def __str__(self): - return sstr(self) - - def __repr__(self): - return "DeferredVector('%s')" % (self.name) - - -
    [docs]class MatrixBase(object): - - # Added just for numpy compatibility - # TODO: investigate about __array_priority__ - __array_priority__ = 10.0 - - is_Matrix = True - is_Identity = None - _class_priority = 3 - - @classmethod - def _handle_creation_inputs(cls, *args, **kwargs): - """Return the number of rows, cols and flat matrix elements. - - Examples - ======== - - >>> from sympy import Matrix, I - - Matrix can be constructed as follows: - - * from a nested list of iterables - - >>> Matrix( ((1, 2+I), (3, 4)) ) - [1, 2 + I] - [3, 4] - - * from un-nested iterable (interpreted as a column) - - >>> Matrix( [1, 2] ) - [1] - [2] - - * from un-nested iterable with dimensions - - >>> Matrix(1, 2, [1, 2] ) - [1, 2] - - * from no arguments (a 0 x 0 matrix) - - >>> Matrix() - [] - - * from a rule - - >>> Matrix(2, 2, lambda i, j: i/(j + 1) ) - [0, 0] - [1, 1/2] - - """ - from sympy.matrices.sparse import SparseMatrix - - # Matrix(SparseMatrix(...)) - if len(args) == 1 and isinstance(args[0], SparseMatrix): - return args[0].rows, args[0].cols, flatten(args[0].tolist()) - - # Matrix(Matrix(...)) - if len(args) == 1 and isinstance(args[0], MatrixBase): - return args[0].rows, args[0].cols, args[0]._mat - - # Matrix(MatrixSymbol('X', 2, 2)) - if len(args) == 1 and isinstance(args[0], Basic) and args[0].is_Matrix: - return args[0].rows, args[0].cols, args[0].as_explicit()._mat - - if len(args) == 3: - rows = as_int(args[0]) - cols = as_int(args[1]) - - # Matrix(2, 2, lambda i, j: i+j) - if len(args) == 3 and isinstance(args[2], collections.Callable): - operation = args[2] - flat_list = [] - for i in range(rows): - flat_list.extend([sympify(operation(sympify(i), j)) - for j in range(cols)]) - - # Matrix(2, 2, [1, 2, 3, 4]) - elif len(args) == 3 and is_sequence(args[2]): - flat_list = args[2] - if len(flat_list) != rows*cols: - raise ValueError('List length should be equal to rows*columns') - flat_list = [sympify(i) for i in flat_list] - - # Matrix(numpy.ones((2, 2))) - elif len(args) == 1 and hasattr(args[0], "__array__"): # pragma: no cover - # NumPy array or matrix or some other object that implements - # __array__. So let's first use this method to get a - # numpy.array() and then make a python list out of it. - arr = args[0].__array__() - if len(arr.shape) == 2: - rows, cols = arr.shape[0], arr.shape[1] - flat_list = [sympify(i) for i in arr.ravel()] - return rows, cols, flat_list - elif len(arr.shape) == 1: - rows, cols = 1, arr.shape[0] - flat_list = [S.Zero]*cols - for i in range(len(arr)): - flat_list[i] = sympify(arr[i]) - return rows, cols, flat_list - else: - raise NotImplementedError( - "SymPy supports just 1D and 2D matrices") - - # Matrix([1, 2, 3]) or Matrix([[1, 2], [3, 4]]) - elif len(args) == 1 and is_sequence(args[0]): - in_mat = [] - ncol = set() - for row in args[0]: - if isinstance(row, MatrixBase): - in_mat.extend(row.tolist()) - if row.cols or row.rows: # only pay attention if it's not 0x0 - ncol.add(row.cols) - else: - in_mat.append(row) - try: - ncol.add(len(row)) - except TypeError: - ncol.add(1) - if len(ncol) > 1: - raise ValueError("Got rows of variable lengths: %s" % - sorted(list(ncol))) - rows = len(in_mat) - if rows: - if not is_sequence(in_mat[0]): - cols = 1 - flat_list = [sympify(i) for i in in_mat] - return rows, cols, flat_list - cols = ncol.pop() - else: - cols = 0 - flat_list = [] - for j in range(rows): - for i in range(cols): - flat_list.append(sympify(in_mat[j][i])) - - # Matrix() - elif len(args) == 0: - # Empty Matrix - rows = cols = 0 - flat_list = [] - - else: - raise TypeError("Data type not understood") - - return rows, cols, flat_list - - def _setitem(self, key, value): - """Helper to set value at location given by key. - - Examples - ======== - - >>> from sympy import Matrix, I, zeros, ones - >>> m = Matrix(((1, 2+I), (3, 4))) - >>> m - [1, 2 + I] - [3, 4] - >>> m[1, 0] = 9 - >>> m - [1, 2 + I] - [9, 4] - >>> m[1, 0] = [[0, 1]] - - To replace row r you assign to position r*m where m - is the number of columns: - - >>> M = zeros(4) - >>> m = M.cols - >>> M[3*m] = ones(1, m)*2; M - [0, 0, 0, 0] - [0, 0, 0, 0] - [0, 0, 0, 0] - [2, 2, 2, 2] - - And to replace column c you can assign to position c: - - >>> M[2] = ones(m, 1)*4; M - [0, 0, 4, 0] - [0, 0, 4, 0] - [0, 0, 4, 0] - [2, 2, 4, 2] - """ - from .dense import Matrix - - is_slice = isinstance(key, slice) - i, j = key = self.key2ij(key) - is_mat = isinstance(value, MatrixBase) - if type(i) is slice or type(j) is slice: - if is_mat: - self.copyin_matrix(key, value) - return - if not isinstance(value, Expr) and is_sequence(value): - self.copyin_list(key, value) - return - raise ValueError('unexpected value: %s' % value) - else: - if not is_mat and \ - not isinstance(value, Expr) and is_sequence(value): - value = Matrix(value) - is_mat = True - if is_mat: - if is_slice: - key = (slice(*divmod(i, self.cols)), - slice(*divmod(j, self.cols))) - else: - key = (slice(i, i + value.rows), - slice(j, j + value.cols)) - self.copyin_matrix(key, value) - else: - return i, j, sympify(value) - return - - def copy(self): - return self._new(self.rows, self.cols, self._mat) - - def trace(self): - if not self.is_square: - raise NonSquareMatrixError() - return self._eval_trace() - - def inv(self, method=None, **kwargs): - if not self.is_square: - raise NonSquareMatrixError() - if method is not None: - kwargs['method'] = method - return self._eval_inverse(**kwargs) - - def transpose(self): - return self._eval_transpose() - - T = property(transpose, None, None, "Matrix transposition.") - - def conjugate(self): - return self._eval_conjugate() - - C = property(conjugate, None, None, "By-element conjugation.") - -
    [docs] def adjoint(self): - """Conjugate transpose or Hermitian conjugation.""" - return self.T.C -
    - @property -
    [docs] def H(self): - """Return Hermite conjugate. - - Examples - ======== - - >>> from sympy import Matrix, I, eye - >>> m = Matrix((0, 1 + I, 2, 3)) - >>> m - [ 0] - [1 + I] - [ 2] - [ 3] - >>> m.H - [0, 1 - I, 2, 3] - - See Also - ======== - - conjugate: By-element conjugation - D: Dirac conjugation - """ - return self.T.C -
    - @property -
    [docs] def D(self): - """Return Dirac conjugate (if self.rows == 4). - - Examples - ======== - - >>> from sympy import Matrix, I, eye - >>> m = Matrix((0, 1 + I, 2, 3)) - >>> m.D - [0, 1 - I, -2, -3] - >>> m = (eye(4) + I*eye(4)) - >>> m[0, 3] = 2 - >>> m.D - [1 - I, 0, 0, 0] - [ 0, 1 - I, 0, 0] - [ 0, 0, -1 + I, 0] - [ 2, 0, 0, -1 + I] - - If the matrix does not have 4 rows an AttributeError will be raised - because this property is only defined for matrices with 4 rows. - - >>> Matrix(eye(2)).D - Traceback (most recent call last): - ... - AttributeError: Matrix has no attribute D. - - See Also - ======== - - conjugate: By-element conjugation - H: Hermite conjugation - """ - from sympy.physics.matrices import mgamma - if self.rows != 4: - # In Python 3.2, properties can only return an AttributeError - # so we can't raise a ShapeError -- see commit which added the - # first line of this inline comment. Also, there is no need - # for a message since MatrixBase will raise the AttributeError - raise AttributeError - return self.H*mgamma(0) -
    - def __array__(self): - from .dense import matrix2numpy - return matrix2numpy(self) - - def __len__(self): - """Return the number of elements of self. - - Implemented mainly so bool(Matrix()) == False. - """ - return self.rows*self.cols - - @property -
    [docs] def shape(self): - """The shape (dimensions) of the matrix as the 2-tuple (rows, cols). - - Examples - ======== - - >>> from sympy.matrices import zeros - >>> M = zeros(2, 3) - >>> M.shape - (2, 3) - >>> M.rows - 2 - >>> M.cols - 3 - """ - return (self.rows, self.cols) -
    - def __sub__(self, a): - return self + (-a) - - def __rsub__(self, a): - return (-self) + a - - def __mul__(self, other): - """Return self*other where other is either a scalar or a matrix - of compatible dimensions. - - Examples - ======== - - >>> from sympy.matrices import Matrix - >>> A = Matrix([[1, 2, 3], [4, 5, 6]]) - >>> 2*A == A*2 == Matrix([[2, 4, 6], [8, 10, 12]]) - True - >>> B = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - >>> A*B - [30, 36, 42] - [66, 81, 96] - >>> B*A - Traceback (most recent call last): - ... - ShapeError: Matrices size mismatch. - >>> - - See Also - ======== - - matrix_multiply_elementwise - """ - if getattr(other, 'is_Matrix', False): - # The following implmentation is equivalent, but about 5% slower - #ma, na = A.shape - #mb, nb = B.shape - # - #if na != mb: - # raise ShapeError() - #product = Matrix(ma, nb, lambda i, j: 0) - #for i in range(ma): - # for j in range(nb): - # s = 0 - # for k in range(na): - # s += A[i, k]*B[k, j] - # product[i, j] = s - #return product - A = self - B = other - if A.cols != B.rows: - raise ShapeError("Matrices size mismatch.") - if A.cols == 0: - return classof(A, B)._new(A.rows, B.cols, lambda i, j: 0) - blst = B.T.tolist() - alst = A.tolist() - return classof(A, B)._new(A.rows, B.cols, lambda i, j: - reduce(lambda k, l: k + l, - [a_ik * b_kj for a_ik, b_kj in zip(alst[i], blst[j])])) - else: - return self._new(self.rows, self.cols, - [i*other for i in self._mat]) - - def __rmul__(self, a): - if getattr(a, 'is_Matrix', False): - return self._new(a)*self - return self*a - - def __pow__(self, num): - from sympy.matrices import eye - - if not self.is_square: - raise NonSquareMatrixError() - if isinstance(num, int) or isinstance(num, Integer): - n = int(num) - if n < 0: - return self.inv()**-n # A**-2 = (A**-1)**2 - a = eye(self.cols) - s = self - while n: - if n % 2: - a *= s - n -= 1 - if not n: - break - s *= s - n //= 2 - return self._new(a) - elif isinstance(num, Rational): - try: - P, D = self.diagonalize() - except MatrixError: - raise NotImplementedError( - "Implemented only for diagonalizable matrices") - for i in range(D.rows): - D[i, i] = D[i, i]**num - return self._new(P*D*P.inv()) - else: - raise NotImplementedError( - "Only integer and rational values are supported") - - def __add__(self, other): - """Return self + other, raising ShapeError if shapes don't match.""" - if getattr(other, 'is_Matrix', False): - A = self - B = other - if A.shape != B.shape: - raise ShapeError("Matrices size mismatch.") - alst = A.tolist() - blst = B.tolist() - ret = [S.Zero]*A.rows - for i in range(A.shape[0]): - ret[i] = list(map(lambda j, k: j + k, alst[i], blst[i])) - return classof(A, B)._new(ret) - raise TypeError('cannot add matrix and %s' % type(other)) - - def __radd__(self, other): - return self + other - - def __div__(self, other): - return self*(S.One / other) - - def __truediv__(self, other): - return self.__div__(other) - - def __neg__(self): - return -1*self - -
    [docs] def multiply(self, b): - """Returns self*b - - See Also - ======== - - dot - cross - multiply_elementwise - """ - return self*b -
    -
    [docs] def add(self, b): - """Return self + b """ - return self + b -
    - def _format_str(self, strfunc, rowsep='\n'): - # Handle zero dimensions: - if self.rows == 0 or self.cols == 0: - return '[]' - # Build table of string representations of the elements - res = [] - # Track per-column max lengths for pretty alignment - maxlen = [0]*self.cols - for i in range(self.rows): - res.append([]) - for j in range(self.cols): - string = strfunc(self[i, j]) - res[-1].append(string) - maxlen[j] = max(len(string), maxlen[j]) - # Patch strings together - for i, row in enumerate(res): - for j, elem in enumerate(row): - # Pad each element up to maxlen so the columns line up - row[j] = elem.rjust(maxlen[j]) - res[i] = "[" + ", ".join(row) + "]" - return rowsep.join(res) - - def __str__(self): - return sstr(self) - - def __repr__(self): - return sstr(self) - -
    [docs] def cholesky(self): - """Returns the Cholesky decomposition L of a matrix A - such that L * L.T = A - - A must be a square, symmetric, positive-definite - and non-singular matrix. - - Examples - ======== - - >>> from sympy.matrices import Matrix - >>> A = Matrix(((25, 15, -5), (15, 18, 0), (-5, 0, 11))) - >>> A.cholesky() - [ 5, 0, 0] - [ 3, 3, 0] - [-1, 1, 3] - >>> A.cholesky() * A.cholesky().T - [25, 15, -5] - [15, 18, 0] - [-5, 0, 11] - - See Also - ======== - - LDLdecomposition - LUdecomposition - QRdecomposition - """ - - if not self.is_square: - raise NonSquareMatrixError("Matrix must be square.") - if not self.is_symmetric(): - raise ValueError("Matrix must be symmetric.") - return self._cholesky() -
    -
    [docs] def LDLdecomposition(self): - """Returns the LDL Decomposition (L, D) of matrix A, - such that L * D * L.T == A - This method eliminates the use of square root. - Further this ensures that all the diagonal entries of L are 1. - A must be a square, symmetric, positive-definite - and non-singular matrix. - - Examples - ======== - - >>> from sympy.matrices import Matrix, eye - >>> A = Matrix(((25, 15, -5), (15, 18, 0), (-5, 0, 11))) - >>> L, D = A.LDLdecomposition() - >>> L - [ 1, 0, 0] - [ 3/5, 1, 0] - [-1/5, 1/3, 1] - >>> D - [25, 0, 0] - [ 0, 9, 0] - [ 0, 0, 9] - >>> L * D * L.T * A.inv() == eye(A.rows) - True - - See Also - ======== - - cholesky - LUdecomposition - QRdecomposition - """ - if not self.is_square: - raise NonSquareMatrixError("Matrix must be square.") - if not self.is_symmetric(): - raise ValueError("Matrix must be symmetric.") - return self._LDLdecomposition() -
    -
    [docs] def lower_triangular_solve(self, rhs): - """Solves Ax = B, where A is a lower triangular matrix. - - See Also - ======== - - upper_triangular_solve - cholesky_solve - diagonal_solve - LDLsolve - LUsolve - QRsolve - """ - - if not self.is_square: - raise NonSquareMatrixError("Matrix must be square.") - if rhs.rows != self.rows: - raise ShapeError("Matrices size mismatch.") - if not self.is_lower: - raise ValueError("Matrix must be lower triangular.") - return self._lower_triangular_solve(rhs) -
    -
    [docs] def upper_triangular_solve(self, rhs): - """Solves Ax = B, where A is an upper triangular matrix. - - See Also - ======== - - lower_triangular_solve - cholesky_solve - diagonal_solve - LDLsolve - LUsolve - QRsolve - """ - if not self.is_square: - raise NonSquareMatrixError("Matrix must be square.") - if rhs.rows != self.rows: - raise TypeError("Matrix size mismatch.") - if not self.is_upper: - raise TypeError("Matrix is not upper triangular.") - return self._upper_triangular_solve(rhs) -
    -
    [docs] def cholesky_solve(self, rhs): - """Solves Ax = B using Cholesky decomposition, - for a general square non-singular matrix. - For a non-square matrix with rows > cols, - the least squares solution is returned. - - See Also - ======== - - lower_triangular_solve - upper_triangular_solve - diagonal_solve - LDLsolve - LUsolve - QRsolve - """ - if self.is_symmetric(): - L = self._cholesky() - elif self.rows >= self.cols: - L = (self.T*self)._cholesky() - rhs = self.T*rhs - else: - raise NotImplementedError("Under-determined System.") - Y = L._lower_triangular_solve(rhs) - return (L.T)._upper_triangular_solve(Y) -
    -
    [docs] def diagonal_solve(self, rhs): - """Solves Ax = B efficiently, where A is a diagonal Matrix, - with non-zero diagonal entries. - - Examples - ======== - - >>> from sympy.matrices import Matrix, eye - >>> A = eye(2)*2 - >>> B = Matrix([[1, 2], [3, 4]]) - >>> A.diagonal_solve(B) == B/2 - True - - See Also - ======== - - lower_triangular_solve - upper_triangular_solve - cholesky_solve - LDLsolve - LUsolve - QRsolve - """ - if not self.is_diagonal: - raise TypeError("Matrix should be diagonal") - if rhs.rows != self.rows: - raise TypeError("Size mis-match") - return self._diagonal_solve(rhs) -
    -
    [docs] def LDLsolve(self, rhs): - """Solves Ax = B using LDL decomposition, - for a general square and non-singular matrix. - - For a non-square matrix with rows > cols, - the least squares solution is returned. - - Examples - ======== - - >>> from sympy.matrices import Matrix, eye - >>> A = eye(2)*2 - >>> B = Matrix([[1, 2], [3, 4]]) - >>> A.LDLsolve(B) == B/2 - True - - See Also - ======== - - LDLdecomposition - lower_triangular_solve - upper_triangular_solve - cholesky_solve - diagonal_solve - LUsolve - QRsolve - """ - if self.is_symmetric(): - L, D = self.LDLdecomposition() - elif self.rows >= self.cols: - L, D = (self.T*self).LDLdecomposition() - rhs = self.T*rhs - else: - raise NotImplementedError("Under-determined System.") - Y = L._lower_triangular_solve(rhs) - Z = D._diagonal_solve(Y) - return (L.T)._upper_triangular_solve(Z) -
    -
    [docs] def solve_least_squares(self, rhs, method='CH'): - """Return the least-square fit to the data. - - By default the cholesky_solve routine is used (method='CH'); other - methods of matrix inversion can be used. To find out which are - available, see the docstring of the .inv() method. - - Examples - ======== - - >>> from sympy.matrices import Matrix, Matrix, ones - >>> A = Matrix([1, 2, 3]) - >>> B = Matrix([2, 3, 4]) - >>> S = Matrix(A.row_join(B)) - >>> S - [1, 2] - [2, 3] - [3, 4] - - If each line of S represent coefficients of Ax + By - and x and y are [2, 3] then S*xy is: - - >>> r = S*Matrix([2, 3]); r - [ 8] - [13] - [18] - - But let's add 1 to the middle value and then solve for the - least-squares value of xy: - - >>> xy = S.solve_least_squares(Matrix([8, 14, 18])); xy - [ 5/3] - [10/3] - - The error is given by S*xy - r: - - >>> S*xy - r - [1/3] - [1/3] - [1/3] - >>> _.norm().n(2) - 0.58 - - If a different xy is used, the norm will be higher: - - >>> xy += ones(2, 1)/10 - >>> (S*xy - r).norm().n(2) - 1.5 - - """ - if method == 'CH': - return self.cholesky_solve(rhs) - t = self.T - return (t*self).inv(method=method)*t*rhs -
    -
    [docs] def solve(self, rhs, method='GE'): - """Return solution to self*soln = rhs using given inversion method. - - For a list of possible inversion methods, see the .inv() docstring. - """ - if not self.is_square: - if self.rows < self.cols: - raise ValueError('Under-determined system.') - elif self.rows > self.cols: - raise ValueError('For over-determined system, M, having ' - 'more rows than columns, try M.solve_least_squares(rhs).') - else: - return self.inv(method=method)*rhs -
    - def __mathml__(self): - mml = "" - for i in range(self.rows): - mml += "<matrixrow>" - for j in range(self.cols): - mml += self[i, j].__mathml__() - mml += "</matrixrow>" - return "<matrix>" + mml + "</matrix>" - -
    [docs] def submatrix(self, keys): - """ - Get a slice/submatrix of the matrix using the given slice. - - Examples - ======== - - >>> from sympy import Matrix - >>> m = Matrix(4, 4, lambda i, j: i+j) - >>> m - [0, 1, 2, 3] - [1, 2, 3, 4] - [2, 3, 4, 5] - [3, 4, 5, 6] - >>> m[:1, 1] - [1] - >>> m[:2, :1] - [0] - [1] - >>> m[2:4, 2:4] - [4, 5] - [5, 6] - - See Also - ======== - - extract - """ - rlo, rhi, clo, chi = self.key2bounds(keys) - rows, cols = rhi - rlo, chi - clo - mat = [S.Zero]*rows*cols - for i in range(rows): - mat[i*cols:(i + 1)*cols] = \ - self._mat[(i + rlo)*self.cols + clo:(i + rlo)*self.cols + chi] - return self._new(rows, cols, mat) -
    -
    [docs] def extract(self, rowsList, colsList): - """Return a submatrix by specifying a list of rows and columns. - Negative indices can be given. All indices must be in the range - -n <= i < n where n is the number of rows or columns. - - Examples - ======== - - >>> from sympy import Matrix - >>> m = Matrix(4, 3, list(range(12))) - >>> m - [0, 1, 2] - [3, 4, 5] - [6, 7, 8] - [9, 10, 11] - >>> m.extract([0, 1, 3], [0, 1]) - [0, 1] - [3, 4] - [9, 10] - - Rows or columns can be repeated: - - >>> m.extract([0, 0, 1], [-1]) - [2] - [2] - [5] - - Every other row can be taken by using range to provide the indices: - - >>> m.extract(list(range(0, m.rows, 2)), [-1]) - [2] - [8] - - See Also - ======== - - submatrix - """ - cols = self.cols - flat_list = self._mat - rowsList = [a2idx(k, self.rows) for k in rowsList] - colsList = [a2idx(k, self.cols) for k in colsList] - return self._new(len(rowsList), len(colsList), - lambda i, j: flat_list[rowsList[i]*cols + colsList[j]]) -
    -
    [docs] def key2bounds(self, keys): - """Converts a key with potentially mixed types of keys (integer and slice) - into a tuple of ranges and raises an error if any index is out of self's - range. - - See Also - ======== - - key2ij - """ - - islice, jslice = [isinstance(k, slice) for k in keys] - if islice: - if not self.rows: - rlo = rhi = 0 - else: - rlo, rhi = keys[0].indices(self.rows)[:2] - else: - rlo = a2idx(keys[0], self.rows) - rhi = rlo + 1 - if jslice: - if not self.cols: - clo = chi = 0 - else: - clo, chi = keys[1].indices(self.cols)[:2] - else: - clo = a2idx(keys[1], self.cols) - chi = clo + 1 - return rlo, rhi, clo, chi -
    -
    [docs] def key2ij(self, key): - """Converts key into canonical form, converting integers or indexable - items into valid integers for self's range or returning slices - unchanged. - - See Also - ======== - - key2bounds - """ - if is_sequence(key): - if not len(key) == 2: - raise TypeError('key must be a sequence of length 2') - return [a2idx(i, n) if not isinstance(i, slice) else i - for i, n in zip(key, self.shape)] - elif isinstance(key, slice): - return key.indices(len(self))[:2] - else: - return divmod(a2idx(key, len(self)), self.cols) -
    -
    [docs] def evalf(self, prec=None, **options): - """Apply evalf() to each element of self.""" - if prec is None: - return self.applyfunc(lambda i: i.evalf(**options)) - else: - return self.applyfunc(lambda i: i.evalf(prec, **options)) -
    - n = evalf - -
    [docs] def subs(self, *args, **kwargs): # should mirror core.basic.subs - """Return a new matrix with subs applied to each entry. - - Examples - ======== - - >>> from sympy.abc import x, y - >>> from sympy.matrices import SparseMatrix, Matrix - >>> SparseMatrix(1, 1, [x]) - [x] - >>> _.subs(x, y) - [y] - >>> Matrix(_).subs(y, x) - [x] - """ - return self.applyfunc(lambda x: x.subs(*args, **kwargs)) -
    -
    [docs] def expand(self, deep=True, modulus=None, power_base=True, power_exp=True, - mul=True, log=True, multinomial=True, basic=True, **hints): - """Apply core.function.expand to each entry of the matrix. - - Examples - ======== - - >>> from sympy.abc import x - >>> from sympy.matrices import Matrix - >>> Matrix(1, 1, [x*(x+1)]) - [x*(x + 1)] - >>> _.expand() - [x**2 + x] - >>> - """ - return self.applyfunc(lambda x: x.expand( - deep, modulus, power_base, power_exp, mul, log, multinomial, basic, - **hints)) -
    -
    [docs] def simplify(self, ratio=1.7, measure=count_ops): - """Apply simplify to each element of the matrix. - - Examples - ======== - - >>> from sympy.abc import x, y - >>> from sympy import sin, cos - >>> from sympy.matrices import SparseMatrix - >>> SparseMatrix(1, 1, [x*sin(y)**2 + x*cos(y)**2]) - [x*sin(y)**2 + x*cos(y)**2] - >>> _.simplify() - [x] - """ - return self.applyfunc(lambda x: x.simplify(ratio, measure))
    - _eval_simplify = simplify - -
    [docs] def print_nonzero(self, symb="X"): - """Shows location of non-zero entries for fast shape lookup. - - Examples - ======== - - >>> from sympy.matrices import Matrix, eye - >>> m = Matrix(2, 3, lambda i, j: i*3+j) - >>> m - [0, 1, 2] - [3, 4, 5] - >>> m.print_nonzero() - [ XX] - [XXX] - >>> m = eye(4) - >>> m.print_nonzero("x") - [x ] - [ x ] - [ x ] - [ x] - - """ - s = [] - for i in range(self.rows): - line = [] - for j in range(self.cols): - if self[i, j] == 0: - line.append(" ") - else: - line.append(str(symb)) - s.append("[%s]" % ''.join(line)) - print('\n'.join(s)) -
    -
    [docs] def LUsolve(self, rhs, iszerofunc=_iszero): - """Solve the linear system Ax = rhs for x where A = self. - - This is for symbolic matrices, for real or complex ones use - sympy.mpmath.lu_solve or sympy.mpmath.qr_solve. - - See Also - ======== - - lower_triangular_solve - upper_triangular_solve - cholesky_solve - diagonal_solve - LDLsolve - QRsolve - LUdecomposition - """ - if rhs.rows != self.rows: - raise ShapeError( - "`self` and `rhs` must have the same number of rows.") - - A, perm = self.LUdecomposition_Simple(iszerofunc=_iszero) - n = self.rows - b = rhs.permuteFwd(perm).as_mutable() - # forward substitution, all diag entries are scaled to 1 - for i in range(n): - for j in range(i): - b.row_op(i, lambda x, k: x - b[j, k]*A[i, j]) - # backward substitution - for i in range(n - 1, -1, -1): - for j in range(i + 1, n): - b.row_op(i, lambda x, k: x - b[j, k]*A[i, j]) - b.row_op(i, lambda x, k: x / A[i, i]) - return rhs.__class__(b) -
    -
    [docs] def LUdecomposition(self, iszerofunc=_iszero): - """Returns the decomposition LU and the row swaps p. - - Examples - ======== - - >>> from sympy import Matrix - >>> a = Matrix([[4, 3], [6, 3]]) - >>> L, U, _ = a.LUdecomposition() - >>> L - [ 1, 0] - [3/2, 1] - >>> U - [4, 3] - [0, -3/2] - - See Also - ======== - - cholesky - LDLdecomposition - QRdecomposition - LUdecomposition_Simple - LUdecompositionFF - LUsolve - """ - combined, p = self.LUdecomposition_Simple(iszerofunc=_iszero) - L = self.zeros(self.rows) - U = self.zeros(self.rows) - for i in range(self.rows): - for j in range(self.rows): - if i > j: - L[i, j] = combined[i, j] - else: - if i == j: - L[i, i] = 1 - U[i, j] = combined[i, j] - return L, U, p -
    -
    [docs] def LUdecomposition_Simple(self, iszerofunc=_iszero): - """Returns A comprised of L, U (L's diag entries are 1) and - p which is the list of the row swaps (in order). - - See Also - ======== - - LUdecomposition - LUdecompositionFF - LUsolve - """ - if not self.is_square: - raise NonSquareMatrixError("A Matrix must be square to apply LUdecomposition_Simple().") - n = self.rows - A = self.as_mutable() - p = [] - # factorization - for j in range(n): - for i in range(j): - for k in range(i): - A[i, j] = A[i, j] - A[i, k]*A[k, j] - pivot = -1 - for i in range(j, n): - for k in range(j): - A[i, j] = A[i, j] - A[i, k]*A[k, j] - # find the first non-zero pivot, includes any expression - if pivot == -1 and not iszerofunc(A[i, j]): - pivot = i - if pivot < 0: - # this result is based on iszerofunc's analysis of the possible pivots, so even though - # the element may not be strictly zero, the supplied iszerofunc's evaluation gave True - raise ValueError("No nonzero pivot found; inversion failed.") - if pivot != j: # row must be swapped - A.row_swap(pivot, j) - p.append([pivot, j]) - scale = 1 / A[j, j] - for i in range(j + 1, n): - A[i, j] = A[i, j]*scale - return A, p -
    -
    [docs] def LUdecompositionFF(self): - """Compute a fraction-free LU decomposition. - - Returns 4 matrices P, L, D, U such that PA = L D**-1 U. - If the elements of the matrix belong to some integral domain I, then all - elements of L, D and U are guaranteed to belong to I. - - **Reference** - - W. Zhou & D.J. Jeffrey, "Fraction-free matrix factors: new forms - for LU and QR factors". Frontiers in Computer Science in China, - Vol 2, no. 1, pp. 67-80, 2008. - - See Also - ======== - - LUdecomposition - LUdecomposition_Simple - LUsolve - """ - from sympy.matrices import SparseMatrix - zeros = SparseMatrix.zeros - eye = SparseMatrix.eye - - n, m = self.rows, self.cols - U, L, P = self.as_mutable(), eye(n), eye(n) - DD = zeros(n, n) - oldpivot = 1 - - for k in range(n - 1): - if U[k, k] == 0: - for kpivot in range(k + 1, n): - if U[kpivot, k]: - break - else: - raise ValueError("Matrix is not full rank") - U[k, k:], U[kpivot, k:] = U[kpivot, k:], U[k, k:] - L[k, :k], L[kpivot, :k] = L[kpivot, :k], L[k, :k] - P[k, :], P[kpivot, :] = P[kpivot, :], P[k, :] - L[k, k] = Ukk = U[k, k] - DD[k, k] = oldpivot*Ukk - for i in range(k + 1, n): - L[i, k] = Uik = U[i, k] - for j in range(k + 1, m): - U[i, j] = (Ukk*U[i, j] - U[k, j]*Uik) / oldpivot - U[i, k] = 0 - oldpivot = Ukk - DD[n - 1, n - 1] = oldpivot - return P, L, DD, U -
    -
    [docs] def cofactorMatrix(self, method="berkowitz"): - """Return a matrix containing the cofactor of each element. - - See Also - ======== - - cofactor - minorEntry - minorMatrix - adjugate - """ - out = self._new(self.rows, self.cols, lambda i, j: - self.cofactor(i, j, method)) - return out -
    -
    [docs] def minorEntry(self, i, j, method="berkowitz"): - """Calculate the minor of an element. - - See Also - ======== - - minorMatrix - cofactor - cofactorMatrix - """ - if not 0 <= i < self.rows or not 0 <= j < self.cols: - raise ValueError("`i` and `j` must satisfy 0 <= i < `self.rows` " + - "(%d)" % self.rows + "and 0 <= j < `self.cols` (%d)." % self.cols) - return self.minorMatrix(i, j).det(method) -
    -
    [docs] def minorMatrix(self, i, j): - """Creates the minor matrix of a given element. - - See Also - ======== - - minorEntry - cofactor - cofactorMatrix - """ - if not 0 <= i < self.rows or not 0 <= j < self.cols: - raise ValueError("`i` and `j` must satisfy 0 <= i < `self.rows` " + - "(%d)" % self.rows + "and 0 <= j < `self.cols` (%d)." % self.cols) - M = self.as_mutable() - M.row_del(i) - M.col_del(j) - return self._new(M) -
    -
    [docs] def cofactor(self, i, j, method="berkowitz"): - """Calculate the cofactor of an element. - - See Also - ======== - - cofactorMatrix - minorEntry - minorMatrix - """ - if (i + j) % 2 == 0: - return self.minorEntry(i, j, method) - else: - return -1*self.minorEntry(i, j, method) -
    -
    [docs] def jacobian(self, X): - """Calculates the Jacobian matrix (derivative of a vectorial function). - - Parameters - ========== - - self : vector of expressions representing functions f_i(x_1, ..., x_n). - X : set of x_i's in order, it can be a list or a Matrix - - Both self and X can be a row or a column matrix in any order - (i.e., jacobian() should always work). - - Examples - ======== - - >>> from sympy import sin, cos, Matrix - >>> from sympy.abc import rho, phi - >>> X = Matrix([rho*cos(phi), rho*sin(phi), rho**2]) - >>> Y = Matrix([rho, phi]) - >>> X.jacobian(Y) - [cos(phi), -rho*sin(phi)] - [sin(phi), rho*cos(phi)] - [ 2*rho, 0] - >>> X = Matrix([rho*cos(phi), rho*sin(phi)]) - >>> X.jacobian(Y) - [cos(phi), -rho*sin(phi)] - [sin(phi), rho*cos(phi)] - - See Also - ======== - - hessian - wronskian - """ - if not isinstance(X, MatrixBase): - X = self._new(X) - # Both X and self can be a row or a column matrix, so we need to make - # sure all valid combinations work, but everything else fails: - if self.shape[0] == 1: - m = self.shape[1] - elif self.shape[1] == 1: - m = self.shape[0] - else: - raise TypeError("self must be a row or a column matrix") - if X.shape[0] == 1: - n = X.shape[1] - elif X.shape[1] == 1: - n = X.shape[0] - else: - raise TypeError("X must be a row or a column matrix") - - # m is the number of functions and n is the number of variables - # computing the Jacobian is now easy: - return self._new(m, n, lambda j, i: self[j].diff(X[i])) -
    -
    [docs] def QRdecomposition(self): - """Return Q, R where A = Q*R, Q is orthogonal and R is upper triangular. - - Examples - ======== - - This is the example from wikipedia: - - >>> from sympy import Matrix, eye - >>> A = Matrix([[12, -51, 4], [6, 167, -68], [-4, 24, -41]]) - >>> Q, R = A.QRdecomposition() - >>> Q - [ 6/7, -69/175, -58/175] - [ 3/7, 158/175, 6/175] - [-2/7, 6/35, -33/35] - >>> R - [14, 21, -14] - [ 0, 175, -70] - [ 0, 0, 35] - >>> A == Q*R - True - - QR factorization of an identity matrix: - - >>> A = Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) - >>> Q, R = A.QRdecomposition() - >>> Q - [1, 0, 0] - [0, 1, 0] - [0, 0, 1] - >>> R - [1, 0, 0] - [0, 1, 0] - [0, 0, 1] - - See Also - ======== - - cholesky - LDLdecomposition - LUdecomposition - QRsolve - """ - cls = self.__class__ - self = self.as_mutable() - - if not self.rows >= self.cols: - raise MatrixError( - "The number of rows must be greater than columns") - n = self.rows - m = self.cols - rank = n - row_reduced = self.rref()[0] - for i in range(row_reduced.rows): - if row_reduced.row(i).norm() == 0: - rank -= 1 - if not rank == self.cols: - raise MatrixError("The rank of the matrix must match the columns") - Q, R = self.zeros(n, m), self.zeros(m) - for j in range(m): # for each column vector - tmp = self[:, j] # take original v - for i in range(j): - # subtract the project of self on new vector - tmp -= Q[:, i]*self[:, j].dot(Q[:, i]) - tmp.expand() - # normalize it - R[j, j] = tmp.norm() - Q[:, j] = tmp / R[j, j] - if Q[:, j].norm() != 1: - raise NotImplementedError( - "Could not normalize the vector %d." % j) - for i in range(j): - R[i, j] = Q[:, i].dot(self[:, j]) - return cls(Q), cls(R) -
    -
    [docs] def QRsolve(self, b): - """Solve the linear system 'Ax = b'. - - 'self' is the matrix 'A', the method argument is the vector - 'b'. The method returns the solution vector 'x'. If 'b' is a - matrix, the system is solved for each column of 'b' and the - return value is a matrix of the same shape as 'b'. - - This method is slower (approximately by a factor of 2) but - more stable for floating-point arithmetic than the LUsolve method. - However, LUsolve usually uses an exact arithmetic, so you don't need - to use QRsolve. - - This is mainly for educational purposes and symbolic matrices, for real - (or complex) matrices use sympy.mpmath.qr_solve. - - See Also - ======== - - lower_triangular_solve - upper_triangular_solve - cholesky_solve - diagonal_solve - LDLsolve - LUsolve - QRdecomposition - """ - - Q, R = self.as_mutable().QRdecomposition() - y = Q.T*b - - # back substitution to solve R*x = y: - # We build up the result "backwards" in the vector 'x' and reverse it - # only in the end. - x = [] - n = R.rows - for j in range(n - 1, -1, -1): - tmp = y[j, :] - for k in range(j + 1, n): - tmp -= R[j, k]*x[n - 1 - k] - x.append(tmp / R[j, j]) - return self._new([row._mat for row in reversed(x)]) -
    -
    [docs] def cross(self, b): - """Calculate the cross product of ``self`` and ``b``. - - See Also - ======== - - dot - multiply - multiply_elementwise - """ - if not is_sequence(b): - raise TypeError("`b` must be an ordered iterable or Matrix, not %s." % - type(b)) - if not (self.rows == 1 and self.cols == 3 or - self.rows == 3 and self.cols == 1) and \ - (b.rows == 1 and b.cols == 3 or - b.rows == 3 and b.cols == 1): - raise ShapeError("Dimensions incorrect for cross product.") - else: - return self._new(1, 3, ((self[1]*b[2] - self[2]*b[1]), - (self[2]*b[0] - self[0]*b[2]), - (self[0]*b[1] - self[1]*b[0]))) -
    -
    [docs] def dot(self, b): - """Return the dot product of Matrix self and b relaxing the condition - of compatible dimensions: if either the number of rows or columns are - the same as the length of b then the dot product is returned. If self - is a row or column vector, a scalar is returned. Otherwise, a list - of results is returned (and in that case the number of columns in self - must match the length of b). - - Examples - ======== - - >>> from sympy import Matrix - >>> M = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - >>> v = [1, 1, 1] - >>> M.row(0).dot(v) - 6 - >>> M.col(0).dot(v) - 12 - >>> M.dot(v) - [6, 15, 24] - - See Also - ======== - - cross - multiply - multiply_elementwise - """ - from .dense import Matrix - - if not isinstance(b, MatrixBase): - if is_sequence(b): - if len(b) != self.cols and len(b) != self.rows: - raise ShapeError("Dimensions incorrect for dot product.") - return self.dot(Matrix(b)) - else: - raise TypeError("`b` must be an ordered iterable or Matrix, not %s." % - type(b)) - if self.cols == b.rows: - if b.cols != 1: - self = self.T - b = b.T - prod = flatten((self*b).tolist()) - if len(prod) == 1: - return prod[0] - return prod - if self.cols == b.cols: - return self.dot(b.T) - elif self.rows == b.rows: - return self.T.dot(b) - else: - raise ShapeError("Dimensions incorrect for dot product.") -
    -
    [docs] def multiply_elementwise(self, b): - """Return the Hadamard product (elementwise product) of A and B - - Examples - ======== - - >>> from sympy.matrices import Matrix - >>> A = Matrix([[0, 1, 2], [3, 4, 5]]) - >>> B = Matrix([[1, 10, 100], [100, 10, 1]]) - >>> A.multiply_elementwise(B) - [ 0, 10, 200] - [300, 40, 5] - - See Also - ======== - - cross - dot - multiply - """ - from sympy.matrices import matrix_multiply_elementwise - - return matrix_multiply_elementwise(self, b) -
    -
    [docs] def values(self): - """Return non-zero values of self.""" - return [i for i in flatten(self.tolist()) if not i.is_zero] -
    -
    [docs] def norm(self, ord=None): - """Return the Norm of a Matrix or Vector. - In the simplest case this is the geometric size of the vector - Other norms can be specified by the ord parameter - - - ===== ============================ ========================== - ord norm for matrices norm for vectors - ===== ============================ ========================== - None Frobenius norm 2-norm - 'fro' Frobenius norm - does not exist - inf -- max(abs(x)) - -inf -- min(abs(x)) - 1 -- as below - -1 -- as below - 2 2-norm (largest sing. value) as below - -2 smallest singular value as below - other - does not exist sum(abs(x)**ord)**(1./ord) - ===== ============================ ========================== - - Examples - ======== - - >>> from sympy import Matrix, Symbol, trigsimp, cos, sin, oo - >>> x = Symbol('x', real=True) - >>> v = Matrix([cos(x), sin(x)]) - >>> trigsimp( v.norm() ) - 1 - >>> v.norm(10) - (sin(x)**10 + cos(x)**10)**(1/10) - >>> A = Matrix([[1, 1], [1, 1]]) - >>> A.norm(2)# Spectral norm (max of |Ax|/|x| under 2-vector-norm) - 2 - >>> A.norm(-2) # Inverse spectral norm (smallest singular value) - 0 - >>> A.norm() # Frobenius Norm - 2 - >>> Matrix([1, -2]).norm(oo) - 2 - >>> Matrix([-1, 2]).norm(-oo) - 1 - - See Also - ======== - - normalized - """ - # Row or Column Vector Norms - vals = list(self.values()) or [0] - if self.rows == 1 or self.cols == 1: - if ord == 2 or ord is None: # Common case sqrt(<x, x>) - return sqrt(Add(*(abs(i)**2 for i in vals))) - - elif ord == 1: # sum(abs(x)) - return Add(*(abs(i) for i in vals)) - - elif ord == S.Infinity: # max(abs(x)) - return Max(*[abs(i) for i in vals]) - - elif ord == S.NegativeInfinity: # min(abs(x)) - return Min(*[abs(i) for i in vals]) - - # Otherwise generalize the 2-norm, Sum(x_i**ord)**(1/ord) - # Note that while useful this is not mathematically a norm - try: - return Pow(Add(*(abs(i)**ord for i in vals)), S(1) / ord) - except (NotImplementedError, TypeError): - raise ValueError("Expected order to be Number, Symbol, oo") - - # Matrix Norms - else: - if ord == 2: # Spectral Norm - # Maximum singular value - return Max(*self.singular_values()) - - elif ord == -2: - # Minimum singular value - return Min(*self.singular_values()) - - elif (ord is None or isinstance(ord, str) and ord.lower() in - ['f', 'fro', 'frobenius', 'vector']): - # Reshape as vector and send back to norm function - return self.vec().norm(ord=2) - - else: - raise NotImplementedError("Matrix Norms under development") -
    -
    [docs] def normalized(self): - """Return the normalized version of ``self``. - - See Also - ======== - - norm - """ - if self.rows != 1 and self.cols != 1: - raise ShapeError("A Matrix must be a vector to normalize.") - norm = self.norm() - out = self.applyfunc(lambda i: i / norm) - return out -
    -
    [docs] def project(self, v): - """Return the projection of ``self`` onto the line containing ``v``. - - Examples - ======== - - >>> from sympy import Matrix, S, sqrt - >>> V = Matrix([sqrt(3)/2, S.Half]) - >>> x = Matrix([[1, 0]]) - >>> V.project(x) - [sqrt(3)/2, 0] - >>> V.project(-x) - [sqrt(3)/2, 0] - """ - return v*(self.dot(v) / v.dot(v)) -
    -
    [docs] def permuteBkwd(self, perm): - """Permute the rows of the matrix with the given permutation in reverse. - - Examples - ======== - - >>> from sympy.matrices import eye - >>> M = eye(3) - >>> M.permuteBkwd([[0, 1], [0, 2]]) - [0, 1, 0] - [0, 0, 1] - [1, 0, 0] - - See Also - ======== - - permuteFwd - """ - copy = self.copy() - for i in range(len(perm) - 1, -1, -1): - copy.row_swap(perm[i][0], perm[i][1]) - return copy -
    -
    [docs] def permuteFwd(self, perm): - """Permute the rows of the matrix with the given permutation. - - Examples - ======== - - >>> from sympy.matrices import eye - >>> M = eye(3) - >>> M.permuteFwd([[0, 1], [0, 2]]) - [0, 0, 1] - [1, 0, 0] - [0, 1, 0] - - See Also - ======== - - permuteBkwd - """ - copy = self.copy() - for i in range(len(perm)): - copy.row_swap(perm[i][0], perm[i][1]) - return copy -
    -
    [docs] def exp(self): - """Return the exponentiation of a square matrix.""" - if not self.is_square: - raise NonSquareMatrixError( - "Exponentiation is valid only for square matrices") - try: - U, D = self.diagonalize() - except MatrixError: - raise NotImplementedError("Exponentiation is implemented only for diagonalizable matrices") - for i in range(0, D.rows): - D[i, i] = C.exp(D[i, i]) - return U*D*U.inv() -
    - @property -
    [docs] def is_square(self): - """Checks if a matrix is square. - - A matrix is square if the number of rows equals the number of columns. - The empty matrix is square by definition, since the number of rows and - the number of columns are both zero. - - Examples - ======== - - >>> from sympy import Matrix - >>> a = Matrix([[1, 2, 3], [4, 5, 6]]) - >>> b = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - >>> c = Matrix([]) - >>> a.is_square - False - >>> b.is_square - True - >>> c.is_square - True - """ - return self.rows == self.cols -
    - @property -
    [docs] def is_zero(self): - """Checks if a matrix is a zero matrix. - - A matrix is zero if every element is zero. A matrix need not be square - to be considered zero. The empty matrix is zero by the principle of - vacuous truth. - - Examples - ======== - - >>> from sympy import Matrix, zeros - >>> a = Matrix([[0, 0], [0, 0]]) - >>> b = zeros(3, 4) - >>> c = Matrix([[0, 1], [0, 0]]) - >>> d = Matrix([]) - >>> a.is_zero - True - >>> b.is_zero - True - >>> c.is_zero - False - >>> d.is_zero - True - """ - return not list(self.values()) -
    -
    [docs] def is_nilpotent(self): - """Checks if a matrix is nilpotent. - - A matrix B is nilpotent if for some integer k, B**k is - a zero matrix. - - Examples - ======== - - >>> from sympy import Matrix - >>> a = Matrix([[0, 0, 0], [1, 0, 0], [1, 1, 0]]) - >>> a.is_nilpotent() - True - - >>> a = Matrix([[1, 0, 1], [1, 0, 0], [1, 1, 0]]) - >>> a.is_nilpotent() - False - """ - if not self.is_square: - raise NonSquareMatrixError( - "Nilpotency is valid only for square matrices") - x = Dummy('x') - if self.charpoly(x).args[0] == x**self.rows: - return True - return False -
    - @property -
    [docs] def is_upper(self): - """Check if matrix is an upper triangular matrix. True can be returned - even if the matrix is not square. - - Examples - ======== - - >>> from sympy import Matrix - >>> m = Matrix(2, 2, [1, 0, 0, 1]) - >>> m - [1, 0] - [0, 1] - >>> m.is_upper - True - - >>> m = Matrix(4, 3, [5, 1, 9, 0, 4 , 6, 0, 0, 5, 0, 0, 0]) - >>> m - [5, 1, 9] - [0, 4, 6] - [0, 0, 5] - [0, 0, 0] - >>> m.is_upper - True - - >>> m = Matrix(2, 3, [4, 2, 5, 6, 1, 1]) - >>> m - [4, 2, 5] - [6, 1, 1] - >>> m.is_upper - False - - See Also - ======== - - is_lower - is_diagonal - is_upper_hessenberg - """ - return all(self[i, j].is_zero - for i in range(1, self.rows) - for j in range(i)) -
    - @property -
    [docs] def is_lower(self): - """Check if matrix is a lower triangular matrix. True can be returned - even if the matrix is not square. - - Examples - ======== - - >>> from sympy import Matrix - >>> m = Matrix(2, 2, [1, 0, 0, 1]) - >>> m - [1, 0] - [0, 1] - >>> m.is_lower - True - - >>> m = Matrix(4, 3, [0, 0, 0, 2, 0, 0, 1, 4 , 0, 6, 6, 5]) - >>> m - [0, 0, 0] - [2, 0, 0] - [1, 4, 0] - [6, 6, 5] - >>> m.is_lower - True - - >>> from sympy.abc import x, y - >>> m = Matrix(2, 2, [x**2 + y, y**2 + x, 0, x + y]) - >>> m - [x**2 + y, x + y**2] - [ 0, x + y] - >>> m.is_lower - False - - See Also - ======== - - is_upper - is_diagonal - is_lower_hessenberg - """ - return all(self[i, j].is_zero - for i in range(self.rows) - for j in range(i + 1, self.cols)) -
    - @property -
    [docs] def is_upper_hessenberg(self): - """Checks if the matrix is the upper-Hessenberg form. - - The upper hessenberg matrix has zero entries - below the first subdiagonal. - - Examples - ======== - - >>> from sympy.matrices import Matrix - >>> a = Matrix([[1, 4, 2, 3], [3, 4, 1, 7], [0, 2, 3, 4], [0, 0, 1, 3]]) - >>> a - [1, 4, 2, 3] - [3, 4, 1, 7] - [0, 2, 3, 4] - [0, 0, 1, 3] - >>> a.is_upper_hessenberg - True - - See Also - ======== - - is_lower_hessenberg - is_upper - """ - return all(self[i, j].is_zero - for i in range(2, self.rows) - for j in range(i - 1)) -
    - @property -
    [docs] def is_lower_hessenberg(self): - r"""Checks if the matrix is in the lower-Hessenberg form. - - The lower hessenberg matrix has zero entries - above the first superdiagonal. - - Examples - ======== - - >>> from sympy.matrices import Matrix - >>> a = Matrix([[1, 2, 0, 0], [5, 2, 3, 0], [3, 4, 3, 7], [5, 6, 1, 1]]) - >>> a - [1, 2, 0, 0] - [5, 2, 3, 0] - [3, 4, 3, 7] - [5, 6, 1, 1] - >>> a.is_lower_hessenberg - True - - See Also - ======== - - is_upper_hessenberg - is_lower - """ - return all(self[i, j].is_zero - for i in range(self.rows) - for j in range(i + 2, self.cols)) -
    -
    [docs] def is_symbolic(self): - """Checks if any elements contain Symbols. - - Examples - ======== - - >>> from sympy.matrices import Matrix - >>> from sympy.abc import x, y - >>> M = Matrix([[x, y], [1, 0]]) - >>> M.is_symbolic() - True - - """ - return any(element.has(Symbol) for element in list(self.values())) -
    -
    [docs] def is_symmetric(self, simplify=True): - """Check if matrix is symmetric matrix, - that is square matrix and is equal to its transpose. - - By default, simplifications occur before testing symmetry. - They can be skipped using 'simplify=False'; while speeding things a bit, - this may however induce false negatives. - - Examples - ======== - - >>> from sympy import Matrix - >>> m = Matrix(2, 2, [0, 1, 1, 2]) - >>> m - [0, 1] - [1, 2] - >>> m.is_symmetric() - True - - >>> m = Matrix(2, 2, [0, 1, 2, 0]) - >>> m - [0, 1] - [2, 0] - >>> m.is_symmetric() - False - - >>> m = Matrix(2, 3, [0, 0, 0, 0, 0, 0]) - >>> m - [0, 0, 0] - [0, 0, 0] - >>> m.is_symmetric() - False - - >>> from sympy.abc import x, y - >>> m = Matrix(3, 3, [1, x**2 + 2*x + 1, y, (x + 1)**2 , 2, 0, y, 0, 3]) - >>> m - [ 1, x**2 + 2*x + 1, y] - [(x + 1)**2, 2, 0] - [ y, 0, 3] - >>> m.is_symmetric() - True - - If the matrix is already simplified, you may speed-up is_symmetric() - test by using 'simplify=False'. - - >>> m.is_symmetric(simplify=False) - False - >>> m1 = m.expand() - >>> m1.is_symmetric(simplify=False) - True - """ - if not self.is_square: - return False - if simplify: - delta = self - self.transpose() - delta.simplify() - return delta.equals(self.zeros(self.rows, self.cols)) - else: - return self == self.transpose() -
    -
    [docs] def is_anti_symmetric(self, simplify=True): - """Check if matrix M is an antisymmetric matrix, - that is, M is a square matrix with all M[i, j] == -M[j, i]. - - When ``simplify=True`` (default), the sum M[i, j] + M[j, i] is - simplified before testing to see if it is zero. By default, - the SymPy simplify function is used. To use a custom function - set simplify to a function that accepts a single argument which - returns a simplified expression. To skip simplification, set - simplify to False but note that although this will be faster, - it may induce false negatives. - - Examples - ======== - - >>> from sympy import Matrix, symbols - >>> m = Matrix(2, 2, [0, 1, -1, 0]) - >>> m - [ 0, 1] - [-1, 0] - >>> m.is_anti_symmetric() - True - >>> x, y = symbols('x y') - >>> m = Matrix(2, 3, [0, 0, x, -y, 0, 0]) - >>> m - [ 0, 0, x] - [-y, 0, 0] - >>> m.is_anti_symmetric() - False - - >>> from sympy.abc import x, y - >>> m = Matrix(3, 3, [0, x**2 + 2*x + 1, y, - ... -(x + 1)**2 , 0, x*y, - ... -y, -x*y, 0]) - - Simplification of matrix elements is done by default so even - though two elements which should be equal and opposite wouldn't - pass an equality test, the matrix is still reported as - anti-symmetric: - - >>> m[0, 1] == -m[1, 0] - False - >>> m.is_anti_symmetric() - True - - If 'simplify=False' is used for the case when a Matrix is already - simplified, this will speed things up. Here, we see that without - simplification the matrix does not appear anti-symmetric: - - >>> m.is_anti_symmetric(simplify=False) - False - - But if the matrix were already expanded, then it would appear - anti-symmetric and simplification in the is_anti_symmetric routine - is not needed: - - >>> m = m.expand() - >>> m.is_anti_symmetric(simplify=False) - True - """ - # accept custom simplification - simpfunc = simplify if isinstance(simplify, FunctionType) else \ - _simplify if simplify else False - - if not self.is_square: - return False - n = self.rows - if simplify: - for i in range(n): - # diagonal - if not simpfunc(self[i, i]).is_zero: - return False - # others - for j in range(i + 1, n): - diff = self[i, j] + self[j, i] - if not simpfunc(diff).is_zero: - return False - return True - else: - for i in range(n): - for j in range(i, n): - if self[i, j] != -self[j, i]: - return False - return True -
    -
    [docs] def is_diagonal(self): - """Check if matrix is diagonal, - that is matrix in which the entries outside the main diagonal are all zero. - - Examples - ======== - - >>> from sympy import Matrix, diag - >>> m = Matrix(2, 2, [1, 0, 0, 2]) - >>> m - [1, 0] - [0, 2] - >>> m.is_diagonal() - True - - >>> m = Matrix(2, 2, [1, 1, 0, 2]) - >>> m - [1, 1] - [0, 2] - >>> m.is_diagonal() - False - - >>> m = diag(1, 2, 3) - >>> m - [1, 0, 0] - [0, 2, 0] - [0, 0, 3] - >>> m.is_diagonal() - True - - See Also - ======== - - is_lower - is_upper - is_diagonalizable - diagonalize - """ - for i in range(self.rows): - for j in range(self.cols): - if i != j and self[i, j]: - return False - return True -
    -
    [docs] def det(self, method="bareis"): - """Computes the matrix determinant using the method "method". - - Possible values for "method": - bareis ... det_bareis - berkowitz ... berkowitz_det - lu_decomposition ... det_LU - - See Also - ======== - - det_bareis - berkowitz_det - det_LU - """ - - # if methods were made internal and all determinant calculations - # passed through here, then these lines could be factored out of - # the method routines - if not self.is_square: - raise NonSquareMatrixError() - if not self: - return S.One - if method == "bareis": - return self.det_bareis() - elif method == "berkowitz": - return self.berkowitz_det() - elif method == "det_LU": - return self.det_LU_decomposition() - else: - raise ValueError("Determinant method '%s' unrecognized" % method) -
    -
    [docs] def det_bareis(self): - """Compute matrix determinant using Bareis' fraction-free - algorithm which is an extension of the well known Gaussian - elimination method. This approach is best suited for dense - symbolic matrices and will result in a determinant with - minimal number of fractions. It means that less term - rewriting is needed on resulting formulae. - - TODO: Implement algorithm for sparse matrices (SFF), - http://www.eecis.udel.edu/~saunders/papers/sffge/it5.ps. - - See Also - ======== - - det - berkowitz_det - """ - if not self.is_square: - raise NonSquareMatrixError() - if not self: - return S.One - - M, n = self.copy(), self.rows - - if n == 1: - det = M[0, 0] - elif n == 2: - det = M[0, 0]*M[1, 1] - M[0, 1]*M[1, 0] - else: - sign = 1 # track current sign in case of column swap - - for k in range(n - 1): - # look for a pivot in the current column - # and assume det == 0 if none is found - if M[k, k] == 0: - for i in range(k + 1, n): - if M[i, k]: - M.row_swap(i, k) - sign *= -1 - break - else: - return S.Zero - - # proceed with Bareis' fraction-free (FF) - # form of Gaussian elimination algorithm - for i in range(k + 1, n): - for j in range(k + 1, n): - D = M[k, k]*M[i, j] - M[i, k]*M[k, j] - - if k > 0: - D /= M[k - 1, k - 1] - - if D.is_Atom: - M[i, j] = D - else: - M[i, j] = cancel(D) - - det = sign*M[n - 1, n - 1] - - return det.expand() -
    -
    [docs] def det_LU_decomposition(self): - """Compute matrix determinant using LU decomposition - - Note that this method fails if the LU decomposition itself - fails. In particular, if the matrix has no inverse this method - will fail. - - TODO: Implement algorithm for sparse matrices (SFF), - http://www.eecis.udel.edu/~saunders/papers/sffge/it5.ps. - - See Also - ======== - - det - det_bareis - berkowitz_det - """ - if not self.is_square: - raise NonSquareMatrixError() - if not self: - return S.One - - M, n = self.copy(), self.rows - p, prod = [], 1 - l, u, p = M.LUdecomposition() - if len(p) % 2: - prod = -1 - - for k in range(n): - prod = prod*u[k, k]*l[k, k] - - return prod.expand() -
    -
    [docs] def adjugate(self, method="berkowitz"): - """Returns the adjugate matrix. - - Adjugate matrix is the transpose of the cofactor matrix. - - http://en.wikipedia.org/wiki/Adjugate - - See Also - ======== - - cofactorMatrix - transpose - berkowitz - """ - - return self.cofactorMatrix(method).T -
    -
    [docs] def inverse_LU(self, iszerofunc=_iszero): - """Calculates the inverse using LU decomposition. - - See Also - ======== - - inv - inverse_GE - inverse_ADJ - """ - if not self.is_square: - raise NonSquareMatrixError() - - ok = self.rref(simplify=True)[0] - if any(iszerofunc(ok[j, j]) for j in range(ok.rows)): - raise ValueError("Matrix det == 0; not invertible.") - - return self.LUsolve(self.eye(self.rows), iszerofunc=_iszero) -
    -
    [docs] def inverse_GE(self, iszerofunc=_iszero): - """Calculates the inverse using Gaussian elimination. - - See Also - ======== - - inv - inverse_LU - inverse_ADJ - """ - from .dense import Matrix - if not self.is_square: - raise NonSquareMatrixError("A Matrix must be square to invert.") - - big = Matrix.hstack(self.as_mutable(), Matrix.eye(self.rows)) - red = big.rref(iszerofunc=iszerofunc, simplify=True)[0] - if any(iszerofunc(red[j, j]) for j in range(red.rows)): - raise ValueError("Matrix det == 0; not invertible.") - - return self._new(red[:, big.rows:]) -
    -
    [docs] def inverse_ADJ(self, iszerofunc=_iszero): - """Calculates the inverse using the adjugate matrix and a determinant. - - See Also - ======== - - inv - inverse_LU - inverse_GE - """ - if not self.is_square: - raise NonSquareMatrixError("A Matrix must be square to invert.") - - d = self.berkowitz_det() - zero = d.equals(0) - if zero is None: - # if equals() can't decide, will rref be able to? - ok = self.rref(simplify=True)[0] - zero = any(iszerofunc(ok[j, j]) for j in range(ok.rows)) - if zero: - raise ValueError("Matrix det == 0; not invertible.") - - return self.adjugate() / d -
    -
    [docs] def rref(self, simplified=False, iszerofunc=_iszero, - simplify=False): - """Return reduced row-echelon form of matrix and indices of pivot vars. - - To simplify elements before finding nonzero pivots set simplify=True - (to use the default SymPy simplify function) or pass a custom - simplify function. - - Examples - ======== - - >>> from sympy import Matrix - >>> from sympy.abc import x - >>> m = Matrix([[1, 2], [x, 1 - 1/x]]) - >>> m.rref() - ([1, 0] - [0, 1], [0, 1]) - """ - if simplified is not False: - SymPyDeprecationWarning( - feature="'simplified' as a keyword to rref", - useinstead="simplify=True, or set simplify equal to your " - "own custom simplification function", - issue=3382, deprecated_since_version="0.7.2", - ).warn() - simplify = simplify or True - simpfunc = simplify if isinstance( - simplify, FunctionType) else _simplify - # pivot: index of next row to contain a pivot - pivot, r = 0, self.as_mutable() - # pivotlist: indices of pivot variables (non-free) - pivotlist = [] - for i in range(r.cols): - if pivot == r.rows: - break - if simplify: - r[pivot, i] = simpfunc(r[pivot, i]) - if iszerofunc(r[pivot, i]): - for k in range(pivot, r.rows): - if simplify and k > pivot: - r[k, i] = simpfunc(r[k, i]) - if not iszerofunc(r[k, i]): - break - if k == r.rows - 1 and iszerofunc(r[k, i]): - continue - r.row_swap(pivot, k) - scale = r[pivot, i] - r.row_op(pivot, lambda x, _: x / scale) - for j in range(r.rows): - if j == pivot: - continue - scale = r[j, i] - r.row_op(j, lambda x, k: x - scale*r[pivot, k]) - pivotlist.append(i) - pivot += 1 - return self._new(r), pivotlist -
    -
    [docs] def nullspace(self, simplified=False, simplify=False): - """Returns list of vectors (Matrix objects) that span nullspace of self - """ - from sympy.matrices import zeros - - if simplified is not False: - SymPyDeprecationWarning( - feature="'simplified' as a keyword to nullspace", - useinstead="simplify=True, or set simplify equal to your " - "own custom simplification function", - issue=3382, deprecated_since_version="0.7.2", - ).warn() - simplify = simplify or True - simpfunc = simplify if isinstance( - simplify, FunctionType) else _simplify - reduced, pivots = self.rref(simplify=simpfunc) - - basis = [] - # create a set of vectors for the basis - for i in range(self.cols - len(pivots)): - basis.append(zeros(self.cols, 1)) - # contains the variable index to which the vector corresponds - basiskey, cur = [-1]*len(basis), 0 - for i in range(self.cols): - if i not in pivots: - basiskey[cur] = i - cur += 1 - for i in range(self.cols): - if i not in pivots: # free var, just set vector's ith place to 1 - basis[basiskey.index(i)][i, 0] = 1 - else: # add negative of nonpivot entry to corr vector - for j in range(i + 1, self.cols): - line = pivots.index(i) - v = reduced[line, j] - if simplify: - v = simpfunc(v) - if v: - if j in pivots: - # XXX: Is this the correct error? - raise NotImplementedError( - "Could not compute the nullspace of `self`.") - basis[basiskey.index(j)][i, 0] = -v - return [self._new(b) for b in basis] -
    -
    [docs] def berkowitz(self): - """The Berkowitz algorithm. - - Given N x N matrix with symbolic content, compute efficiently - coefficients of characteristic polynomials of 'self' and all - its square sub-matrices composed by removing both i-th row - and column, without division in the ground domain. - - This method is particularly useful for computing determinant, - principal minors and characteristic polynomial, when 'self' - has complicated coefficients e.g. polynomials. Semi-direct - usage of this algorithm is also important in computing - efficiently sub-resultant PRS. - - Assuming that M is a square matrix of dimension N x N and - I is N x N identity matrix, then the following following - definition of characteristic polynomial is begin used: - - charpoly(M) = det(t*I - M) - - As a consequence, all polynomials generated by Berkowitz - algorithm are monic. - - >>> from sympy import Matrix - >>> from sympy.abc import x, y, z - - >>> M = Matrix([[x, y, z], [1, 0, 0], [y, z, x]]) - - >>> p, q, r = M.berkowitz() - - >>> p # 1 x 1 M's sub-matrix - (1, -x) - - >>> q # 2 x 2 M's sub-matrix - (1, -x, -y) - - >>> r # 3 x 3 M's sub-matrix - (1, -2*x, x**2 - y*z - y, x*y - z**2) - - For more information on the implemented algorithm refer to: - - [1] S.J. Berkowitz, On computing the determinant in small - parallel time using a small number of processors, ACM, - Information Processing Letters 18, 1984, pp. 147-150 - - [2] M. Keber, Division-Free computation of sub-resultants - using Bezout matrices, Tech. Report MPI-I-2006-1-006, - Saarbrucken, 2006 - - See Also - ======== - - berkowitz_det - berkowitz_minors - berkowitz_charpoly - berkowitz_eigenvals - """ - from sympy.matrices import zeros - - if not self.is_square: - raise NonSquareMatrixError() - - A, N = self, self.rows - transforms = [0]*(N - 1) - - for n in range(N, 1, -1): - T, k = zeros(n + 1, n), n - 1 - - R, C = -A[k, :k], A[:k, k] - A, a = A[:k, :k], -A[k, k] - - items = [C] - - for i in range(0, n - 2): - items.append(A*items[i]) - - for i, B in enumerate(items): - items[i] = (R*B)[0, 0] - - items = [S.One, a] + items - - for i in range(n): - T[i:, i] = items[:n - i + 1] - - transforms[k - 1] = T - - polys = [self._new([S.One, -A[0, 0]])] - - for i, T in enumerate(transforms): - polys.append(T*polys[i]) - - return tuple(map(tuple, polys)) -
    -
    [docs] def berkowitz_det(self): - """Computes determinant using Berkowitz method. - - See Also - ======== - - det - berkowitz - """ - if not self.is_square: - raise NonSquareMatrixError() - if not self: - return S.One - poly = self.berkowitz()[-1] - sign = (-1)**(len(poly) - 1) - return sign*poly[-1] -
    -
    [docs] def berkowitz_minors(self): - """Computes principal minors using Berkowitz method. - - See Also - ======== - - berkowitz - """ - sign, minors = S.NegativeOne, [] - - for poly in self.berkowitz(): - minors.append(sign*poly[-1]) - sign = -sign - - return tuple(minors) -
    -
    [docs] def berkowitz_charpoly(self, x=Dummy('lambda'), simplify=_simplify): - """Computes characteristic polynomial minors using Berkowitz method. - - A PurePoly is returned so using different variables for ``x`` does - not affect the comparison or the polynomials: - - Examples - ======== - - >>> from sympy import Matrix - >>> from sympy.abc import x, y - >>> A = Matrix([[1, 3], [2, 0]]) - >>> A.berkowitz_charpoly(x) == A.berkowitz_charpoly(y) - True - - Specifying ``x`` is optional; a Dummy with name ``lambda`` is used by - default (which looks good when pretty-printed in unicode): - - >>> A.berkowitz_charpoly().as_expr() - _lambda**2 - _lambda - 6 - - No test is done to see that ``x`` doesn't clash with an existing - symbol, so using the default (``lambda``) or your own Dummy symbol is - the safest option: - - >>> A = Matrix([[1, 2], [x, 0]]) - >>> A.charpoly().as_expr() - _lambda**2 - _lambda - 2*x - >>> A.charpoly(x).as_expr() - x**2 - 3*x - - See Also - ======== - - berkowitz - """ - return PurePoly(list(map(simplify, self.berkowitz()[-1])), x) -
    - charpoly = berkowitz_charpoly - -
    [docs] def berkowitz_eigenvals(self, **flags): - """Computes eigenvalues of a Matrix using Berkowitz method. - - See Also - ======== - - berkowitz - """ - return roots(self.berkowitz_charpoly(Dummy('x')), **flags) -
    -
    [docs] def eigenvals(self, **flags): - """Return eigen values using the berkowitz_eigenvals routine. - - Since the roots routine doesn't always work well with Floats, - they will be replaced with Rationals before calling that - routine. If this is not desired, set flag ``rational`` to False. - """ - # roots doesn't like Floats, so replace them with Rationals - # unless the nsimplify flag indicates that this has already - # been done, e.g. in eigenvects - if flags.pop('rational', True): - if any(v.has(Float) for v in self): - self = self._new(self.rows, self.cols, - [nsimplify(v, rational=True) for v in self]) - - flags.pop('simplify', None) # pop unsupported flag - return self.berkowitz_eigenvals(**flags) -
    -
    [docs] def eigenvects(self, **flags): - """Return list of triples (eigenval, multiplicity, basis). - - The flag ``simplify`` has two effects: - 1) if bool(simplify) is True, as_content_primitive() - will be used to tidy up normalization artifacts; - 2) if nullspace needs simplification to compute the - basis, the simplify flag will be passed on to the - nullspace routine which will interpret it there. - - If the matrix contains any Floats, they will be changed to Rationals - for computation purposes, but the answers will be returned after being - evaluated with evalf. If it is desired to removed small imaginary - portions during the evalf step, pass a value for the ``chop`` flag. - """ - from sympy.matrices import eye - - simplify = flags.get('simplify', True) - primitive = bool(flags.get('simplify', False)) - chop = flags.pop('chop', False) - - flags.pop('multiple', None) # remove this if it's there - - # roots doesn't like Floats, so replace them with Rationals - float = False - if any(v.has(Float) for v in self): - float = True - self = self._new(self.rows, self.cols, [nsimplify( - v, rational=True) for v in self]) - flags['rational'] = False # to tell eigenvals not to do this - - out, vlist = [], self.eigenvals(**flags) - vlist = list(vlist.items()) - vlist.sort(key=default_sort_key) - flags.pop('rational', None) - - for r, k in vlist: - tmp = self.as_mutable() - eye(self.rows)*r - basis = tmp.nullspace() - # whether tmp.is_symbolic() is True or False, it is possible that - # the basis will come back as [] in which case simplification is - # necessary. - if not basis: - # The nullspace routine failed, try it again with simplification - basis = tmp.nullspace(simplify=simplify) - if not basis: - raise NotImplementedError( - "Can't evaluate eigenvector for eigenvalue %s" % r) - if primitive: - # the relationship A*e = lambda*e will still hold if we change the - # eigenvector; so if simplify is True we tidy up any normalization - # artifacts with as_content_primtive (default) and remove any pure Integer - # denominators. - l = 1 - for i, b in enumerate(basis[0]): - c, p = signsimp(b).as_content_primitive() - if c is not S.One: - b = c*p - l = ilcm(l, c.q) - basis[0][i] = b - if l != 1: - basis[0] *= l - if float: - out.append((r.evalf(chop=chop), k, [ - self._new(b).evalf(chop=chop) for b in basis])) - else: - out.append((r, k, [self._new(b) for b in basis])) - return out -
    -
    [docs] def singular_values(self): - """Compute the singular values of a Matrix - - Examples - ======== - - >>> from sympy import Matrix, Symbol, eye - >>> x = Symbol('x', real=True) - >>> A = Matrix([[0, 1, 0], [0, x, 0], [-1, 0, 0]]) - >>> A.singular_values() - [sqrt(x**2 + 1), 1, 0] - - See Also - ======== - - condition_number - """ - self = self.as_mutable() - # Compute eigenvalues of A.H A - valmultpairs = (self.H*self).eigenvals() - - # Expands result from eigenvals into a simple list - vals = [] - for k, v in list(valmultpairs.items()): - vals += [sqrt(k)]*v # dangerous! same k in several spots! - # sort them in descending order - vals.sort(reverse=True, key=default_sort_key) - - return vals -
    -
    [docs] def condition_number(self): - """Returns the condition number of a matrix. - - This is the maximum singular value divided by the minimum singular value - - Examples - ======== - - >>> from sympy import Matrix, S - >>> A = Matrix([[1, 0, 0], [0, 10, 0], [0, 0, S.One/10]]) - >>> A.condition_number() - 100 - - See Also - ======== - - singular_values - """ - - singularvalues = self.singular_values() - return Max(*singularvalues) / Min(*singularvalues) -
    - def __getattr__(self, attr): - if attr in ('diff', 'integrate', 'limit'): - def doit(*args): - item_doit = lambda item: getattr(item, attr)(*args) - return self.applyfunc(item_doit) - return doit - else: - raise AttributeError( - "%s has no attribute %s." % (self.__class__.__name__, attr)) - -
    [docs] def integrate(self, *args): - """Integrate each element of the matrix. - - Examples - ======== - - >>> from sympy.matrices import Matrix - >>> from sympy.abc import x, y - >>> M = Matrix([[x, y], [1, 0]]) - >>> M.integrate((x, )) - [x**2/2, x*y] - [ x, 0] - >>> M.integrate((x, 0, 2)) - [2, 2*y] - [2, 0] - - See Also - ======== - - limit - diff - """ - return self._new(self.rows, self.cols, - lambda i, j: self[i, j].integrate(*args)) -
    -
    [docs] def limit(self, *args): - """Calculate the limit of each element in the matrix. - - Examples - ======== - - >>> from sympy.matrices import Matrix - >>> from sympy.abc import x, y - >>> M = Matrix([[x, y], [1, 0]]) - >>> M.limit(x, 2) - [2, y] - [1, 0] - - See Also - ======== - - integrate - diff - """ - return self._new(self.rows, self.cols, - lambda i, j: self[i, j].limit(*args)) -
    -
    [docs] def diff(self, *args): - """Calculate the derivative of each element in the matrix. - - Examples - ======== - - >>> from sympy.matrices import Matrix - >>> from sympy.abc import x, y - >>> M = Matrix([[x, y], [1, 0]]) - >>> M.diff(x) - [1, 0] - [0, 0] - - See Also - ======== - - integrate - limit - """ - return self._new(self.rows, self.cols, - lambda i, j: self[i, j].diff(*args)) -
    -
    [docs] def vec(self): - """Return the Matrix converted into a one column matrix by stacking columns - - Examples - ======== - - >>> from sympy import Matrix - >>> m=Matrix([[1, 3], [2, 4]]) - >>> m - [1, 3] - [2, 4] - >>> m.vec() - [1] - [2] - [3] - [4] - - See Also - ======== - - vech - """ - return self.T.reshape(len(self), 1) -
    -
    [docs] def vech(self, diagonal=True, check_symmetry=True): - """Return the unique elements of a symmetric Matrix as a one column matrix - by stacking the elements in the lower triangle. - - Arguments: - diagonal -- include the diagonal cells of self or not - check_symmetry -- checks symmetry of self but not completely reliably - - Examples - ======== - - >>> from sympy import Matrix - >>> m=Matrix([[1, 2], [2, 3]]) - >>> m - [1, 2] - [2, 3] - >>> m.vech() - [1] - [2] - [3] - >>> m.vech(diagonal=False) - [2] - - See Also - ======== - - vec - """ - from sympy.matrices import zeros - - c = self.cols - if c != self.rows: - raise ShapeError("Matrix must be square") - if check_symmetry: - self.simplify() - if self != self.transpose(): - raise ValueError("Matrix appears to be asymmetric; consider check_symmetry=False") - count = 0 - if diagonal: - v = zeros(c*(c + 1) // 2, 1) - for j in range(c): - for i in range(j, c): - v[count] = self[i, j] - count += 1 - else: - v = zeros(c*(c - 1) // 2, 1) - for j in range(c): - for i in range(j + 1, c): - v[count] = self[i, j] - count += 1 - return v -
    -
    [docs] def get_diag_blocks(self): - """Obtains the square sub-matrices on the main diagonal of a square matrix. - - Useful for inverting symbolic matrices or solving systems of - linear equations which may be decoupled by having a block diagonal - structure. - - Examples - ======== - - >>> from sympy import Matrix, symbols - >>> from sympy.abc import x, y, z - >>> A = Matrix([[1, 3, 0, 0], [y, z*z, 0, 0], [0, 0, x, 0], [0, 0, 0, 0]]) - >>> a1, a2, a3 = A.get_diag_blocks() - >>> a1 - [1, 3] - [y, z**2] - >>> a2 - [x] - >>> a3 - [0] - >>> - - """ - sub_blocks = [] - - def recurse_sub_blocks(M): - i = 1 - while i <= M.shape[0]: - if i == 1: - to_the_right = M[0, i:] - to_the_bottom = M[i:, 0] - else: - to_the_right = M[:i, i:] - to_the_bottom = M[i:, :i] - if any(to_the_right) or any(to_the_bottom): - i += 1 - continue - else: - sub_blocks.append(M[:i, :i]) - if M.shape == M[:i, :i].shape: - return - else: - recurse_sub_blocks(M[i:, i:]) - return - recurse_sub_blocks(self) - return sub_blocks -
    -
    [docs] def diagonalize(self, reals_only=False, sort=False, normalize=False): - """Return diagonalized matrix D and transformation P such as - - D = P^-1 * M * P - - where M is current matrix. - - Examples - ======== - - >>> from sympy import Matrix - >>> m = Matrix(3, 3, [1, 2, 0, 0, 3, 0, 2, -4, 2]) - >>> m - [1, 2, 0] - [0, 3, 0] - [2, -4, 2] - >>> (P, D) = m.diagonalize() - >>> D - [1, 0, 0] - [0, 2, 0] - [0, 0, 3] - >>> P - [-1, 0, -1] - [ 0, 0, -1] - [ 2, 1, 2] - >>> P.inv() * m * P - [1, 0, 0] - [0, 2, 0] - [0, 0, 3] - - See Also - ======== - - is_diagonal - is_diagonalizable - """ - from sympy.matrices import diag - - if not self.is_square: - raise NonSquareMatrixError() - if not self.is_diagonalizable(reals_only, False): - self._diagonalize_clear_subproducts() - raise MatrixError("Matrix is not diagonalizable") - else: - if self._eigenvects is None: - self._eigenvects = self.eigenvects(simplify=True) - if sort: - self._eigenvects.sort(key=default_sort_key) - self._eigenvects.reverse() - diagvals = [] - P = self._new(self.rows, 0, []) - for eigenval, multiplicity, vects in self._eigenvects: - for k in range(multiplicity): - diagvals.append(eigenval) - vec = vects[k] - if normalize: - vec = vec / vec.norm() - P = P.col_insert(P.cols, vec) - D = diag(*diagvals) - self._diagonalize_clear_subproducts() - return (P, D) -
    -
    [docs] def is_diagonalizable(self, reals_only=False, clear_subproducts=True): - """Check if matrix is diagonalizable. - - If reals_only==True then check that diagonalized matrix consists of the only not complex values. - - Some subproducts could be used further in other methods to avoid double calculations, - By default (if clear_subproducts==True) they will be deleted. - - Examples - ======== - - >>> from sympy import Matrix - >>> m = Matrix(3, 3, [1, 2, 0, 0, 3, 0, 2, -4, 2]) - >>> m - [1, 2, 0] - [0, 3, 0] - [2, -4, 2] - >>> m.is_diagonalizable() - True - >>> m = Matrix(2, 2, [0, 1, 0, 0]) - >>> m - [0, 1] - [0, 0] - >>> m.is_diagonalizable() - False - >>> m = Matrix(2, 2, [0, 1, -1, 0]) - >>> m - [ 0, 1] - [-1, 0] - >>> m.is_diagonalizable() - True - >>> m.is_diagonalizable(True) - False - - See Also - ======== - - is_diagonal - diagonalize - """ - if not self.is_square: - return False - res = False - self._is_symbolic = self.is_symbolic() - self._is_symmetric = self.is_symmetric() - self._eigenvects = None - #if self._is_symbolic: - # self._diagonalize_clear_subproducts() - # raise NotImplementedError("Symbolic matrices are not implemented for diagonalization yet") - self._eigenvects = self.eigenvects(simplify=True) - all_iscorrect = True - for eigenval, multiplicity, vects in self._eigenvects: - if len(vects) != multiplicity: - all_iscorrect = False - break - elif reals_only and not eigenval.is_real: - all_iscorrect = False - break - res = all_iscorrect - if clear_subproducts: - self._diagonalize_clear_subproducts() - return res -
    - def _diagonalize_clear_subproducts(self): - del self._is_symbolic - del self._is_symmetric - del self._eigenvects - -
    [docs] def jordan_form(self, calc_transformation=True): - """Return Jordan form J of current matrix. - - If calc_transformation is specified as False, then transformation P such that - - J = P^-1 * M * P - - will not be calculated. - - Notes - ===== - - Calculation of transformation P is not implemented yet. - - Examples - ======== - - >>> from sympy import Matrix - >>> m = Matrix([ - ... [ 6, 5, -2, -3], - ... [-3, -1, 3, 3], - ... [ 2, 1, -2, -3], - ... [-1, 1, 5, 5]]) - >>> (P, J) = m.jordan_form() - >>> J - [2, 1, 0, 0] - [0, 2, 0, 0] - [0, 0, 2, 1] - [0, 0, 0, 2] - - See Also - ======== - - jordan_cells - """ - from sympy.matrices import diag - - (P, Jcells) = self.jordan_cells(calc_transformation) - J = diag(*Jcells) - return (P, J) -
    -
    [docs] def jordan_cells(self, calc_transformation=True): - """Return a list of Jordan cells of current matrix. - This list shape Jordan matrix J. - - If calc_transformation is specified as False, then transformation P such that - - J = P^-1 * M * P - - will not be calculated. - - Notes - ===== - - Calculation of transformation P is not implemented yet. - - Examples - ======== - - >>> from sympy import Matrix - >>> m = Matrix(4, 4, [ - ... 6, 5, -2, -3, - ... -3, -1, 3, 3, - ... 2, 1, -2, -3, - ... -1, 1, 5, 5]) - - >>> (P, Jcells) = m.jordan_cells() - >>> Jcells[0] - [2, 1] - [0, 2] - >>> Jcells[1] - [2, 1] - [0, 2] - - See Also - ======== - - jordan_form - """ - from sympy.matrices import jordan_cell, diag - - if not self.is_square: - raise NonSquareMatrixError() - _eigenvects = self.eigenvects() - Jcells = [] - for eigenval, multiplicity, vects in _eigenvects: - geometrical = len(vects) - if geometrical == multiplicity: - Jcell = diag(*([eigenval]*multiplicity)) - Jcells.append(Jcell) - else: - sizes = self._jordan_split(multiplicity, geometrical) - cells = [] - for size in sizes: - cell = jordan_cell(eigenval, size) - cells.append(cell) - Jcells += cells - return (None, Jcells) -
    - def _jordan_split(self, algebraical, geometrical): - """Return a list of integers with sum equal to 'algebraical' - and length equal to 'geometrical'""" - n1 = algebraical // geometrical - res = [n1]*geometrical - res[len(res) - 1] += algebraical % geometrical - assert sum(res) == algebraical - return res - -
    [docs] def has(self, *patterns): - """Test whether any subexpression matches any of the patterns. - - Examples - ======== - - >>> from sympy import Matrix, Float - >>> from sympy.abc import x, y - >>> A = Matrix(((1, x), (0.2, 3))) - >>> A.has(x) - True - >>> A.has(y) - False - >>> A.has(Float) - True - """ - return any(a.has(*patterns) for a in self._mat) -
    -
    [docs] def dual(self): - """Returns the dual of a matrix, which is: - - `(1/2)*levicivita(i, j, k, l)*M(k, l)` summed over indices `k` and `l` - - Since the levicivita method is anti_symmetric for any pairwise - exchange of indices, the dual of a symmetric matrix is the zero - matrix. Strictly speaking the dual defined here assumes that the - 'matrix' `M` is a contravariant anti_symmetric second rank tensor, - so that the dual is a covariant second rank tensor. - - """ - from sympy import LeviCivita - from sympy.matrices import zeros - - M, n = self[:, :], self.rows - work = zeros(n) - if self.is_symmetric(): - return work - - for i in range(1, n): - for j in range(1, n): - acum = 0 - for k in range(1, n): - acum += LeviCivita(i, j, 0, k)*M[0, k] - work[i, j] = acum - work[j, i] = -acum - - for l in range(1, n): - acum = 0 - for a in range(1, n): - for b in range(1, n): - acum += LeviCivita(0, l, a, b)*M[a, b] - acum /= 2 - work[0, l] = -acum - work[l, 0] = acum - - return work -
    - @classmethod -
    [docs] def hstack(cls, *args): - """Return a matrix formed by joining args horizontally (i.e. - by repeated application of row_join). - - Examples - ======== - - >>> from sympy.matrices import Matrix, eye - >>> Matrix.hstack(eye(2), 2*eye(2)) - [1, 0, 2, 0] - [0, 1, 0, 2] - """ - return reduce(cls.row_join, args) -
    - @classmethod -
    [docs] def vstack(cls, *args): - """Return a matrix formed by joining args vertically (i.e. - by repeated application of col_join). - - Examples - ======== - - >>> from sympy.matrices import Matrix, eye - >>> Matrix.vstack(eye(2), 2*eye(2)) - [1, 0] - [0, 1] - [2, 0] - [0, 2] - """ - return reduce(cls.col_join, args) -
    -
    [docs] def row_join(self, rhs): - """Concatenates two matrices along self's last and rhs's first column - - Examples - ======== - - >>> from sympy import Matrix, zeros, ones - >>> M = zeros(3) - >>> V = ones(3, 1) - >>> M.row_join(V) - [0, 0, 0, 1] - [0, 0, 0, 1] - [0, 0, 0, 1] - - See Also - ======== - - row - col_join - """ - if self.rows != rhs.rows: - raise ShapeError( - "`self` and `rhs` must have the same number of rows.") - - newmat = self.zeros(self.rows, self.cols + rhs.cols) - newmat[:, :self.cols] = self - newmat[:, self.cols:] = rhs - return newmat -
    -
    [docs] def col_join(self, bott): - """Concatenates two matrices along self's last and bott's first row - - Examples - ======== - - >>> from sympy import Matrix, zeros, ones - >>> M = zeros(3) - >>> V = ones(1, 3) - >>> M.col_join(V) - [0, 0, 0] - [0, 0, 0] - [0, 0, 0] - [1, 1, 1] - - See Also - ======== - - col - row_join - """ - if self.cols != bott.cols: - raise ShapeError( - "`self` and `bott` must have the same number of columns.") - - newmat = self.zeros(self.rows + bott.rows, self.cols) - newmat[:self.rows, :] = self - newmat[self.rows:, :] = bott - return newmat -
    -
    [docs] def row_insert(self, pos, mti): - """Insert one or more rows at the given row position. - - Examples - ======== - - >>> from sympy import Matrix, zeros, ones - >>> M = zeros(3) - >>> V = ones(1, 3) - >>> M.row_insert(1, V) - [0, 0, 0] - [1, 1, 1] - [0, 0, 0] - [0, 0, 0] - - See Also - ======== - - row - col_insert - """ - if pos == 0: - return mti.col_join(self) - elif pos < 0: - pos = self.rows + pos - if pos < 0: - pos = 0 - elif pos > self.rows: - pos = self.rows - - if self.cols != mti.cols: - raise ShapeError( - "`self` and `mti` must have the same number of columns.") - - newmat = self.zeros(self.rows + mti.rows, self.cols) - i, j = pos, pos + mti.rows - newmat[:i, :] = self[:i, :] - newmat[i: j, :] = mti - newmat[j:, :] = self[i:, :] - return newmat -
    -
    [docs] def col_insert(self, pos, mti): - """Insert one or more columns at the given column position. - - Examples - ======== - - >>> from sympy import Matrix, zeros, ones - >>> M = zeros(3) - >>> V = ones(3, 1) - >>> M.col_insert(1, V) - [0, 1, 0, 0] - [0, 1, 0, 0] - [0, 1, 0, 0] - - See Also - ======== - - col - row_insert - """ - if pos == 0: - return mti.row_join(self) - elif pos < 0: - pos = self.cols + pos - if pos < 0: - pos = 0 - elif pos > self.cols: - pos = self.cols - - if self.rows != mti.rows: - raise ShapeError("self and mti must have the same number of rows.") - - newmat = self.zeros(self.rows, self.cols + mti.cols) - i, j = pos, pos + mti.cols - newmat[:, :i] = self[:, :i] - newmat[:, i:j] = mti - newmat[:, j:] = self[:, i:] - return newmat - -
    -
    [docs]def classof(A, B): - """ - Get the type of the result when combining matrices of different types. - - Currently the strategy is that immutability is contagious. - - Examples - ======== - - >>> from sympy import Matrix, ImmutableMatrix - >>> from sympy.matrices.matrices import classof - >>> M = Matrix([[1, 2], [3, 4]]) # a Mutable Matrix - >>> IM = ImmutableMatrix([[1, 2], [3, 4]]) - >>> classof(M, IM) - <class 'sympy.matrices.immutable.ImmutableMatrix'> - """ - try: - if A._class_priority > B._class_priority: - return A.__class__ - else: - return B.__class__ - except: - pass - try: - import numpy - if isinstance(A, numpy.ndarray): - return B.__class__ - if isinstance(B, numpy.ndarray): - return A.__class__ - except: - pass - raise TypeError("Incompatible classes %s, %s" % (A.__class__, B.__class__)) - -
    -
    [docs]def a2idx(j, n=None): - """Return integer after making positive and validating against n.""" - if isinstance(j, slice): - return j - if type(j) is not int: - try: - j = j.__index__() - except AttributeError: - raise IndexError("Invalid index a[%r]" % (j, )) - if n is not None: - if j < 0: - j += n - if not (j >= 0 and j < n): - raise IndexError("Index out of range: a[%s]" % (j, )) - return int(j)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/matrices/sparse.html b/dev-py3k/_modules/sympy/matrices/sparse.html deleted file mode 100644 index ae06cac2b80..00000000000 --- a/dev-py3k/_modules/sympy/matrices/sparse.html +++ /dev/null @@ -1,1559 +0,0 @@ - - - - - - - - - - sympy.matrices.sparse — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.matrices.sparse

    -import copy
    -from collections import defaultdict
    -
    -from sympy.core.containers import Dict
    -from sympy.core.compatibility import is_sequence, as_int
    -from sympy.core.singleton import S
    -from sympy.functions.elementary.miscellaneous import sqrt
    -from sympy.core.sympify import sympify
    -from sympy.utilities.exceptions import SymPyDeprecationWarning
    -
    -from .matrices import MatrixBase, ShapeError, a2idx
    -from .dense import Matrix
    -import collections
    -
    -
    -
    [docs]class SparseMatrix(MatrixBase): - """ - A sparse matrix (a matrix with a large number of zero elements). - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix - >>> SparseMatrix(2, 2, list(range(4))) - [0, 1] - [2, 3] - >>> SparseMatrix(2, 2, {(1, 1): 2}) - [0, 0] - [0, 2] - - See Also - ======== - sympy.matrices.dense.Matrix - """ - - def __init__(self, *args): - - if len(args) == 1 and isinstance(args[0], SparseMatrix): - self.rows = args[0].rows - self.cols = args[0].cols - self._smat = dict(args[0]._smat) - return - - self._smat = {} - - if len(args) == 3: - self.rows = as_int(args[0]) - self.cols = as_int(args[1]) - - if isinstance(args[2], collections.Callable): - op = args[2] - for i in range(self.rows): - for j in range(self.cols): - value = sympify(op(i, j)) - if value: - self._smat[(i, j)] = value - elif isinstance(args[2], (dict, Dict)): - # manual copy, copy.deepcopy() doesn't work - for key in list(args[2].keys()): - v = args[2][key] - if v: - self._smat[key] = v - elif is_sequence(args[2]): - if len(args[2]) != self.rows*self.cols: - raise ValueError( - 'List length (%s) != rows*columns (%s)' % - (len(args[2]), self.rows*self.cols)) - flat_list = args[2] - for i in range(self.rows): - for j in range(self.cols): - value = sympify(flat_list[i*self.cols + j]) - if value: - self._smat[(i, j)] = value - else: - # handle full matrix forms with _handle_creation_inputs - r, c, _list = Matrix._handle_creation_inputs(*args) - self.rows = r - self.cols = c - for i in range(self.rows): - for j in range(self.cols): - value = _list[self.cols*i + j] - if value: - self._smat[(i, j)] = value - - def __getitem__(self, key): - - if type(key) is tuple: - i, j = key - if isinstance(i, int) and isinstance(j, int): - i, j = self.key2ij(key) - rv = self._smat.get((i, j), S.Zero) - return rv - elif isinstance(i, slice) or isinstance(j, slice): - return self.submatrix(key) - - # check for single arg, like M[:] or M[3] - if isinstance(key, slice): - lo, hi = key.indices(len(self))[:2] - L = [] - for i in range(lo, hi): - m, n = divmod(i, self.cols) - L.append(self._smat.get((m, n), S.Zero)) - return L - - i, j = divmod(a2idx(key, len(self)), self.cols) - return self._smat.get((i, j), S.Zero) - - def __setitem__(self, key, value): - raise NotImplementedError() - - def copy(self): - return self._new(self.rows, self.cols, self._smat) - - @property - def is_Identity(self): - if not self.is_square: - return False - if not all(self[i, i] == 1 for i in range(self.rows)): - return False - return len(self) == self.rows - -
    [docs] def tolist(self): - """Convert this sparse matrix into a list of nested Python lists. - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix, ones - >>> a = SparseMatrix(((1, 2), (3, 4))) - >>> a.tolist() - [[1, 2], [3, 4]] - - When there are no rows then it will not be possible to tell how - many columns were in the original matrix: - - >>> SparseMatrix(ones(0, 3)).tolist() - [] - - """ - if not self.rows: - return [] - if not self.cols: - return [[] for i in range(self.rows)] - I, J = self.shape - return [[self[i, j] for j in range(J)] for i in range(I)] -
    -
    [docs] def row(self, i): - """Returns column i from self as a row vector. - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix - >>> a = SparseMatrix(((1, 2), (3, 4))) - >>> a.row(0) - [1, 2] - - See Also - ======== - col - row_list - """ - smat = {} - for j in range(self.cols): - if (i, j) in self._smat: - smat[i, j] = self._smat[i, j] - return self._new(1, self.cols, smat) -
    -
    [docs] def col(self, j): - """Returns column j from self as a column vector. - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix - >>> a = SparseMatrix(((1, 2), (3, 4))) - >>> a.col(0) - [1] - [3] - - See Also - ======== - row - col_list - """ - smat = {} - for i in range(self.rows): - if (i, j) in self._smat: - smat[i, j] = self._smat[i, j] - return self._new(self.rows, 1, smat) -
    -
    [docs] def row_list(self): - """Returns a row-sorted list of non-zero elements of the matrix. - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix - >>> a = SparseMatrix(((1, 2), (3, 4))) - >>> a - [1, 2] - [3, 4] - >>> a.RL - [(0, 0, 1), (0, 1, 2), (1, 0, 3), (1, 1, 4)] - - See Also - ======== - row_op - col_list - """ - - new = [] - for i in range(self.rows): - for j in range(self.cols): - value = self[(i, j)] - if value: - new.append((i, j, value)) - return new -
    - RL = property(row_list, None, None, "Alternate faster representation") - -
    [docs] def col_list(self): - """Returns a column-sorted list of non-zero elements of the matrix. - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix - >>> a=SparseMatrix(((1, 2), (3, 4))) - >>> a - [1, 2] - [3, 4] - >>> a.CL - [(0, 0, 1), (1, 0, 3), (0, 1, 2), (1, 1, 4)] - - See Also - ======== - col_op - row_list - """ - new = [] - for j in range(self.cols): - for i in range(self.rows): - value = self[(i, j)] - if value: - new.append((i, j, value)) - return new -
    - CL = property(col_list, None, None, "Alternate faster representation") - - def _eval_trace(self): - """Calculate the trace of a square matrix. - - Examples - ======== - - >>> from sympy.matrices import eye - >>> eye(3).trace() - 3 - - """ - trace = S.Zero - for i in range(self.cols): - trace += self._smat.get((i, i), 0) - return trace - - def _eval_transpose(self): - """Returns the transposed SparseMatrix of this SparseMatrix. - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix - >>> a = SparseMatrix(((1, 2), (3, 4))) - >>> a - [1, 2] - [3, 4] - >>> a.T - [1, 3] - [2, 4] - """ - tran = self.zeros(self.cols, self.rows) - for key, value in self._smat.items(): - key = key[1], key[0] # reverse - tran._smat[key] = value - return tran - - def _eval_conjugate(self): - """Return the by-element conjugation. - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix - >>> from sympy import I - >>> a = SparseMatrix(((1, 2 + I), (3, 4), (I, -I))) - >>> a - [1, 2 + I] - [3, 4] - [I, -I] - >>> a.C - [ 1, 2 - I] - [ 3, 4] - [-I, I] - - See Also - ======== - - transpose: Matrix transposition - H: Hermite conjugation - D: Dirac conjugation - """ - conj = self.copy() - for key, value in self._smat.items(): - conj._smat[key] = value.conjugate() - return conj - -
    [docs] def multiply(self, other): - """Fast multiplication exploiting the sparsity of the matrix. - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix, ones - >>> A, B = SparseMatrix(ones(4, 3)), SparseMatrix(ones(3, 4)) - >>> A.multiply(B) == 3*ones(4) - True - - See Also - ======== - - add - """ - A = self - B = other - # sort B's row_list into list of rows - Blist = [[] for i in range(B.rows)] - for i, j, v in B.row_list(): - Blist[i].append((j, v)) - Cdict = defaultdict(int) - for k, j, Akj in A.row_list(): - for n, Bjn in Blist[j]: - temp = Akj*Bjn - Cdict[k, n] += temp - rv = self.zeros(A.rows, B.cols) - rv._smat = dict([(k, v) for k, v in Cdict.items() if v]) - return rv -
    -
    [docs] def scalar_multiply(self, scalar): - "Scalar element-wise multiplication" - M = self.zeros(*self.shape) - if scalar: - for i in self._smat: - v = scalar*self._smat[i] - if v: - M._smat[i] = v - else: - M._smat.pop(i, None) - return M -
    - def __mul__(self, other): - """Multiply self and other, watching for non-matrix entities. - - When multiplying be a non-sparse matrix, the result is no longer - sparse. - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix, eye, zeros - >>> I = SparseMatrix(eye(3)) - >>> I*I == I - True - >>> Z = zeros(3) - >>> I*Z - [0, 0, 0] - [0, 0, 0] - [0, 0, 0] - >>> I*2 == 2*I - True - """ - if isinstance(other, SparseMatrix): - return self.multiply(other) - if isinstance(other, MatrixBase): - return other._new(self*self._new(other)) - return self.scalar_multiply(other) - - def __rmul__(self, other): - """Return product the same type as other (if a Matrix). - - When multiplying be a non-sparse matrix, the result is no longer - sparse. - - Examples - ======== - - >>> from sympy.matrices import Matrix, SparseMatrix - >>> A = Matrix(2, 2, list(range(1, 5))) - >>> S = SparseMatrix(2, 2, list(range(2, 6))) - >>> A*S == S*A - False - >>> (isinstance(A*S, SparseMatrix) == - ... isinstance(S*A, SparseMatrix) == False) - True - """ - if isinstance(other, MatrixBase): - return other*other._new(self) - return self.scalar_multiply(other) - - def __add__(self, other): - """Add other to self, efficiently if possible. - - When adding a non-sparse matrix, the result is no longer - sparse. - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix, eye - >>> A = SparseMatrix(eye(3)) + SparseMatrix(eye(3)) - >>> B = SparseMatrix(eye(3)) + eye(3) - >>> A - [2, 0, 0] - [0, 2, 0] - [0, 0, 2] - >>> A == B - True - >>> isinstance(A, SparseMatrix) and isinstance(B, SparseMatrix) - False - - """ - if isinstance(other, SparseMatrix): - return self.add(other) - elif isinstance(other, MatrixBase): - return other._new(other + self) - else: - raise NotImplementedError( - "Cannot add %s to %s" % - tuple([c.__class__.__name__ for c in (other, self)])) - - def __neg__(self): - """Negate all elements of self. - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix, eye - >>> -SparseMatrix(eye(3)) - [-1, 0, 0] - [ 0, -1, 0] - [ 0, 0, -1] - - """ - rv = self.copy() - for k, v in rv._smat.items(): - rv._smat[k] = -v - return rv - -
    [docs] def add(self, other): - """Add two sparse matrices with dictionary representation. - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix, eye, ones - >>> SparseMatrix(eye(3)).add(SparseMatrix(ones(3))) - [2, 1, 1] - [1, 2, 1] - [1, 1, 2] - >>> SparseMatrix(eye(3)).add(-SparseMatrix(eye(3))) - [0, 0, 0] - [0, 0, 0] - [0, 0, 0] - - Only the non-zero elements are stored, so the resulting dictionary - that is used to represent the sparse matrix is empty: - >>> _._smat - {} - - See Also - ======== - - multiply - """ - if not isinstance(other, SparseMatrix): - raise ValueError('only use add with %s, not %s' % - tuple([c.__class__.__name__ for c in (self, other)])) - if self.shape != other.shape: - raise ShapeError() - M = self.copy() - for i, v in other._smat.items(): - v = M[i] + v - if v: - M._smat[i] = v - else: - M._smat.pop(i, None) - return M -
    - def submatrix(self, keys): - rlo, rhi, clo, chi = self.key2bounds(keys) - return self._new(rhi - rlo, chi - clo, - lambda i, j: self[i + rlo, j + clo]) - -
    [docs] def is_symmetric(self, simplify=True): - """Return True if self is symmetric. - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix, eye - >>> M = SparseMatrix(eye(3)) - >>> M.is_symmetric() - True - >>> M[0, 2] = 1 - >>> M.is_symmetric() - False - """ - if simplify: - return all((k[1], k[0]) in self._smat and - not (self[k] - self[(k[1], k[0])]).simplify() - for k in self._smat) - else: - return all((k[1], k[0]) in self._smat and - self[k] == self[(k[1], k[0])] for k in self._smat) -
    -
    [docs] def has(self, *patterns): - """Test whether any subexpression matches any of the patterns. - - Examples - ======== - - >>> from sympy import SparseMatrix, Float - >>> from sympy.abc import x, y - >>> A = SparseMatrix(((1, x), (0.2, 3))) - >>> A.has(x) - True - >>> A.has(y) - False - >>> A.has(Float) - True - """ - return any(self[key].has(*patterns) for key in self._smat) -
    -
    [docs] def applyfunc(self, f): - """Apply a function to each element of the matrix. - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix - >>> m = SparseMatrix(2, 2, lambda i, j: i*2+j) - >>> m - [0, 1] - [2, 3] - >>> m.applyfunc(lambda i: 2*i) - [0, 2] - [4, 6] - - """ - if not isinstance(f, collections.Callable): - raise TypeError("`f` must be callable.") - - out = self.copy() - for k, v in self._smat.items(): - fv = f(v) - if fv: - out._smat[k] = fv - else: - out._smat.pop(k, None) - return out -
    -
    [docs] def reshape(self, rows, cols): - """Reshape matrix while retaining original size. - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix - >>> S = SparseMatrix(4, 2, list(range(8))) - >>> S.reshape(2, 4) - [0, 1, 2, 3] - [4, 5, 6, 7] - - """ - if len(self) != rows*cols: - raise ValueError("Invalid reshape parameters %d %d" % (rows, cols)) - smat = {} - for k, v in self._smat.items(): - i, j = k - n = i*self.cols + j - ii, jj = divmod(n, cols) - smat[(ii, jj)] = self._smat[(i, j)] - return self._new(rows, cols, smat) -
    -
    [docs] def liupc(self): - """Liu's algorithm, for pre-determination of the Elimination Tree of - the given matrix, used in row-based symbolic Cholesky factorization. - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix - >>> S = SparseMatrix([ - ... [1, 0, 3, 2], - ... [0, 0, 1, 0], - ... [4, 0, 0, 5], - ... [0, 6, 7, 0]]) - >>> S.liupc() - ([[0], [], [0], [1, 2]], [4, 3, 4, 4]) - - References - ========== - - Symbolic Sparse Cholesky Factorization using Elimination Trees, - Jeroen Van Grondelle (1999) - http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.39.7582, - downloaded from http://tinyurl.com/9o2jsxj - """ - # Algorithm 2.4, p 17 of reference - - # get the indices of the elements that are non-zero on or below diag - R = [[] for r in range(self.rows)] - for r, c, _ in self.row_list(): - if c <= r: - R[r].append(c) - - inf = len(R) # nothing will be this large - parent = [inf]*self.rows - virtual = [inf]*self.rows - for r in range(self.rows): - for c in R[r][:-1]: - while virtual[c] < r: - t = virtual[c] - virtual[c] = r - c = t - if virtual[c] == inf: - parent[c] = virtual[c] = r - return R, parent -
    -
    [docs] def row_structure_symbolic_cholesky(self): - """Symbolic cholesky factorization, for pre-determination of the - non-zero structure of the Cholesky factororization. - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix - >>> S = SparseMatrix([ - ... [1, 0, 3, 2], - ... [0, 0, 1, 0], - ... [4, 0, 0, 5], - ... [0, 6, 7, 0]]) - >>> S.row_structure_symbolic_cholesky() - [[0], [], [0], [1, 2]] - - References - ========== - - Symbolic Sparse Cholesky Factorization using Elimination Trees, - Jeroen Van Grondelle (1999) - http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.39.7582, - downloaded from http://tinyurl.com/9o2jsxj - """ - - R, parent = self.liupc() - inf = len(R) # this acts as infinity - Lrow = copy.deepcopy(R) - for k in range(self.rows): - for j in R[k]: - while j != inf and j != k: - Lrow[k].append(j) - j = parent[j] - Lrow[k] = list(sorted(set(Lrow[k]))) - return Lrow -
    - def _cholesky_sparse(self): - """Algorithm for numeric Cholesky factorization of a sparse matrix.""" - Crowstruc = self.row_structure_symbolic_cholesky() - C = self.zeros(self.rows) - for i in range(len(Crowstruc)): - for j in Crowstruc[i]: - if i != j: - C[i, j] = self[i, j] - summ = 0 - for p1 in Crowstruc[i]: - if p1 < j: - for p2 in Crowstruc[j]: - if p2 < j: - if p1 == p2: - summ += C[i, p1]*C[j, p1] - else: - break - else: - break - C[i, j] -= summ - C[i, j] /= C[j, j] - else: - C[j, j] = self[j, j] - summ = 0 - for k in Crowstruc[j]: - if k < j: - summ += C[j, k]**2 - else: - break - C[j, j] -= summ - C[j, j] = sqrt(C[j, j]) - - return C - - def _LDL_sparse(self): - """Algorithm for numeric LDL factization, exploiting sparse structure. - """ - Lrowstruc = self.row_structure_symbolic_cholesky() - L = self.eye(self.rows) - D = self.zeros(self.rows, self.cols) - - for i in range(len(Lrowstruc)): - for j in Lrowstruc[i]: - if i != j: - L[i, j] = self[i, j] - summ = 0 - for p1 in Lrowstruc[i]: - if p1 < j: - for p2 in Lrowstruc[j]: - if p2 < j: - if p1 == p2: - summ += L[i, p1]*L[j, p1]*D[p1, p1] - else: - break - else: - break - L[i, j] -= summ - L[i, j] /= D[j, j] - elif i == j: - D[i, i] = self[i, i] - summ = 0 - for k in Lrowstruc[i]: - if k < i: - summ += L[i, k]**2*D[k, k] - else: - break - D[i, i] -= summ - - return L, D - - def _lower_triangular_solve(self, rhs): - """Fast algorithm for solving a lower-triangular system, - exploiting the sparsity of the given matrix. - """ - rows = [[] for i in range(self.rows)] - for i, j, v in self.row_list(): - if i > j: - rows[i].append((j, v)) - X = rhs.copy() - for i in range(self.rows): - for j, v in rows[i]: - X[i, 0] -= v*X[j, 0] - X[i, 0] /= self[i, i] - return self._new(X) - - def _upper_triangular_solve(self, rhs): - """Fast algorithm for solving an upper-triangular system, - exploiting the sparsity of the given matrix. - """ - rows = [[] for i in range(self.rows)] - for i, j, v in self.row_list(): - if i < j: - rows[i].append((j, v)) - X = rhs.copy() - for i in range(self.rows - 1, -1, -1): - rows[i].reverse() - for j, v in rows[i]: - X[i, 0] -= v*X[j, 0] - X[i, 0] /= self[i, i] - return self._new(X) - - def _diagonal_solve(self, rhs): - "Diagonal solve." - return self._new(self.rows, 1, lambda i, j: rhs[i, 0] / self[i, i]) - - def _cholesky_solve(self, rhs): - # for speed reasons, this is not uncommented, but if you are - # having difficulties, try uncommenting to make sure that the - # input matrix is symmetric - - #assert self.is_symmetric() - L = self._cholesky_sparse() - Y = L._lower_triangular_solve(rhs) - rv = L.T._upper_triangular_solve(Y) - return rv - - def _LDL_solve(self, rhs): - # for speed reasons, this is not uncommented, but if you are - # having difficulties, try uncommenting to make sure that the - # input matrix is symmetric - - #assert self.is_symmetric() - L, D = self._LDL_sparse() - Z = L._lower_triangular_solve(rhs) - Y = D._diagonal_solve(Z) - return L.T._upper_triangular_solve(Y) - -
    [docs] def cholesky(self): - """ - Returns the Cholesky decomposition L of a matrix A - such that L * L.T = A - - A must be a square, symmetric, positive-definite - and non-singular matrix - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix - >>> A = SparseMatrix(((25,15,-5),(15,18,0),(-5,0,11))) - >>> A.cholesky() - [ 5, 0, 0] - [ 3, 3, 0] - [-1, 1, 3] - >>> A.cholesky() * A.cholesky().T == A - True - """ - - from sympy.core.numbers import nan, oo - if not self.is_symmetric(): - raise ValueError('Cholesky decomposition applies only to ' - 'symmetric matrices.') - M = self.as_mutable()._cholesky_sparse() - if M.has(nan) or M.has(oo): - raise ValueError('Cholesky decomposition applies only to ' - 'positive-definite matrices') - return self._new(M) -
    -
    [docs] def LDLdecomposition(self): - """ - Returns the LDL Decomposition (matrices ``L`` and ``D``) of matrix - ``A``, such that ``L * D * L.T == A``. ``A`` must be a square, - symmetric, positive-definite and non-singular. - - This method eliminates the use of square root and ensures that all - the diagonal entries of L are 1. - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix - >>> A = SparseMatrix(((25, 15, -5), (15, 18, 0), (-5, 0, 11))) - >>> L, D = A.LDLdecomposition() - >>> L - [ 1, 0, 0] - [ 3/5, 1, 0] - [-1/5, 1/3, 1] - >>> D - [25, 0, 0] - [ 0, 9, 0] - [ 0, 0, 9] - >>> L * D * L.T == A - True - - """ - from sympy.core.numbers import nan, oo - if not self.is_symmetric(): - raise ValueError('LDL decomposition applies only to ' - 'symmetric matrices.') - L, D = self.as_mutable()._LDL_sparse() - if L.has(nan) or L.has(oo) or D.has(nan) or D.has(oo): - raise ValueError('LDL decomposition applies only to ' - 'positive-definite matrices') - - return self._new(L), self._new(D) -
    -
    [docs] def solve_least_squares(self, rhs, method='LDL'): - """Return the least-square fit to the data. - - By default the cholesky_solve routine is used (method='CH'); other - methods of matrix inversion can be used. To find out which are - available, see the docstring of the .inv() method. - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix, Matrix, ones - >>> A = Matrix([1, 2, 3]) - >>> B = Matrix([2, 3, 4]) - >>> S = SparseMatrix(A.row_join(B)) - >>> S - [1, 2] - [2, 3] - [3, 4] - - If each line of S represent coefficients of Ax + By - and x and y are [2, 3] then S*xy is: - - >>> r = S*Matrix([2, 3]); r - [ 8] - [13] - [18] - - But let's add 1 to the middle value and then solve for the - least-squares value of xy: - - >>> xy = S.solve_least_squares(Matrix([8, 14, 18])); xy - [ 5/3] - [10/3] - - The error is given by S*xy - r: - - >>> S*xy - r - [1/3] - [1/3] - [1/3] - >>> _.norm().n(2) - 0.58 - - If a different xy is used, the norm will be higher: - - >>> xy += ones(2, 1)/10 - >>> (S*xy - r).norm().n(2) - 1.5 - - """ - t = self.T - return (t*self).inv(method=method)*t*rhs -
    -
    [docs] def solve(self, rhs, method='LDL'): - """Return solution to self*soln = rhs using given inversion method. - - For a list of possible inversion methods, see the .inv() docstring. - """ - if not self.is_square: - if self.rows < self.cols: - raise ValueError('Under-determined system.') - elif self.rows > self.cols: - raise ValueError('For over-determined system, M, having ' - 'more rows than columns, try M.solve_least_squares(rhs).') - else: - return self.inv(method=method)*rhs -
    - def _eval_inverse(self, **kwargs): - """Return the matrix inverse using Cholesky or LDL (default) - decomposition as selected with the ``method`` keyword: 'CH' or 'LDL', - respectively. - - Examples - ======== - - >>> from sympy import SparseMatrix, Matrix - >>> A = SparseMatrix([ - ... [ 2, -1, 0], - ... [-1, 2, -1], - ... [ 0, 0, 2]]) - >>> A.inv('CH') - [2/3, 1/3, 1/6] - [1/3, 2/3, 1/3] - [ 0, 0, 1/2] - >>> A.inv(method='LDL') # use of 'method=' is optional - [2/3, 1/3, 1/6] - [1/3, 2/3, 1/3] - [ 0, 0, 1/2] - >>> A * _ - [1, 0, 0] - [0, 1, 0] - [0, 0, 1] - - """ - sym = self.is_symmetric() - M = self.as_mutable() - I = M.eye(M.rows) - if not sym: - t = M.T - r1 = M[0, :] - M = t*M - I = t*I - method = kwargs.get('method', 'LDL') - if method in "LDL": - solve = M._LDL_solve - elif method == "CH": - solve = M._cholesky_solve - else: - raise NotImplementedError( - 'Method may be "CH" or "LDL", not %s.' % method) - rv = M.hstack(*[solve(I[:, i]) for i in range(I.cols)]) - if not sym: - scale = (r1*rv[:, 0])[0, 0] - rv /= scale - return self._new(rv) - - def __eq__(self, other): - try: - if self.shape != other.shape: - return False - if isinstance(other, SparseMatrix): - return self._smat == other._smat - elif isinstance(other, MatrixBase): - return self._smat == MutableSparseMatrix(other)._smat - except AttributeError: - return False - - def __ne__(self, other): - return not self == other - -
    [docs] def as_mutable(self): - """Returns a mutable version of this matrix. - - Examples - ======== - - >>> from sympy import ImmutableMatrix - >>> X = ImmutableMatrix([[1, 2], [3, 4]]) - >>> Y = X.as_mutable() - >>> Y[1, 1] = 5 # Can set values in Y - >>> Y - [1, 2] - [3, 5] - """ - return MutableSparseMatrix(self) -
    -
    [docs] def as_immutable(self): - """Returns an Immutable version of this Matrix.""" - from .immutable import ImmutableSparseMatrix - return ImmutableSparseMatrix(self) -
    - @classmethod -
    [docs] def zeros(cls, r, c=None): - """Return an r x c matrix of zeros, square if c is omitted.""" - if is_sequence(r): - SymPyDeprecationWarning( - feature="The syntax zeros([%i, %i])" % tuple(r), - useinstead="zeros(%i, %i)." % tuple(r), - issue=3381, deprecated_since_version="0.7.2", - ).warn() - r, c = r - else: - c = r if c is None else c - r = as_int(r) - c = as_int(c) - return cls(r, c, {}) -
    - @classmethod -
    [docs] def eye(cls, n): - """Return an n x n identity matrix.""" - n = as_int(n) - return cls(n, n, dict([((i, i), S.One) for i in range(n)])) -
    - def __hash__(self): - return hash((type(self).__name__,) + (self.shape, tuple(self._mat))) - -
    -
    [docs]class MutableSparseMatrix(SparseMatrix, MatrixBase): - @classmethod - def _new(cls, *args, **kwargs): - return cls(*args) - - def __setitem__(self, key, value): - """Assign value to position designated by key. - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix, ones - >>> M = SparseMatrix(2, 2, {}) - >>> M[1] = 1; M - [0, 1] - [0, 0] - >>> M[1, 1] = 2; M - [0, 1] - [0, 2] - >>> M = SparseMatrix(2, 2, {}) - >>> M[:, 1] = [1, 1]; M - [0, 1] - [0, 1] - >>> M = SparseMatrix(2, 2, {}) - >>> M[1, :] = [[1, 1]]; M - [0, 0] - [1, 1] - - - To replace row r you assign to position r*m where m - is the number of columns: - - >>> M = SparseMatrix(4, 4, {}) - >>> m = M.cols - >>> M[3*m] = ones(1, m)*2; M - [0, 0, 0, 0] - [0, 0, 0, 0] - [0, 0, 0, 0] - [2, 2, 2, 2] - - And to replace column c you can assign to position c: - - >>> M[2] = ones(m, 1)*4; M - [0, 0, 4, 0] - [0, 0, 4, 0] - [0, 0, 4, 0] - [2, 2, 4, 2] - """ - rv = self._setitem(key, value) - if rv is not None: - i, j, value = rv - if value: - self._smat[(i, j)] = value - elif (i, j) in self._smat: - del self._smat[(i, j)] - - def __hash__(self): - return hash((type(self).__name__,) + (self.shape, tuple(self._mat))) - -
    [docs] def row_del(self, k): - """Delete the given row of the matrix. - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix - >>> M = SparseMatrix([[0, 0], [0, 1]]) - >>> M - [0, 0] - [0, 1] - >>> M.row_del(0) - >>> M - [0, 1] - - See Also - ======== - - col_del - """ - newD = {} - k = a2idx(k, self.rows) - for (i, j) in self._smat: - if i == k: - pass - elif i > k: - newD[i - 1, j] = self._smat[i, j] - else: - newD[i, j] = self._smat[i, j] - self._smat = newD - self.rows -= 1 -
    -
    [docs] def col_del(self, k): - """Delete the given column of the matrix. - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix - >>> M = SparseMatrix([[0, 0], [0, 1]]) - >>> M - [0, 0] - [0, 1] - >>> M.col_del(0) - >>> M - [0] - [1] - - See Also - ======== - - row_del - """ - newD = {} - k = a2idx(k, self.cols) - for (i, j) in self._smat: - if j == k: - pass - elif j > k: - newD[i, j - 1] = self._smat[i, j] - else: - newD[i, j] = self._smat[i, j] - self._smat = newD - self.cols -= 1 -
    -
    [docs] def row_swap(self, i, j): - """Swap, in place, columns i and j. - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix - >>> S = SparseMatrix.eye(3); S[2, 1] = 2 - >>> S.row_swap(1, 0); S - [0, 1, 0] - [1, 0, 0] - [0, 2, 1] - """ - if i > j: - i, j = j, i - rows = self.row_list() - temp = [] - for ii, jj, v in rows: - if ii == i: - self._smat.pop((ii, jj)) - temp.append((jj, v)) - elif ii == j: - self._smat.pop((ii, jj)) - self._smat[i, jj] = v - elif ii > j: - break - for k, v in temp: - self._smat[j, k] = v -
    -
    [docs] def col_swap(self, i, j): - """Swap, in place, columns i and j. - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix - >>> S = SparseMatrix.eye(3); S[2, 1] = 2 - >>> S.col_swap(1, 0); S - [0, 1, 0] - [1, 0, 0] - [2, 0, 1] - """ - if i > j: - i, j = j, i - rows = self.col_list() - temp = [] - for ii, jj, v in rows: - if jj == i: - self._smat.pop((ii, jj)) - temp.append((ii, v)) - elif jj == j: - self._smat.pop((ii, jj)) - self._smat[ii, i] = v - elif jj > j: - break - for k, v in temp: - self._smat[k, j] = v -
    -
    [docs] def row_join(self, other): - """Returns B appended after A (column-wise augmenting):: - - [A B] - - Examples - ======== - - >>> from sympy import SparseMatrix, Matrix - >>> A = SparseMatrix(((1, 0, 1), (0, 1, 0), (1, 1, 0))) - >>> A - [1, 0, 1] - [0, 1, 0] - [1, 1, 0] - >>> B = SparseMatrix(((1, 0, 0), (0, 1, 0), (0, 0, 1))) - >>> B - [1, 0, 0] - [0, 1, 0] - [0, 0, 1] - >>> C = A.row_join(B); C - [1, 0, 1, 1, 0, 0] - [0, 1, 0, 0, 1, 0] - [1, 1, 0, 0, 0, 1] - >>> C == A.row_join(Matrix(B)) - True - - Joining at row ends is the same as appending columns at the end - of the matrix: - - >>> C == A.col_insert(A.cols, B) - True - """ - A, B = self, other - if not A.rows == B.rows: - raise ShapeError() - A = A.copy() - if not isinstance(B, SparseMatrix): - k = 0 - b = B._mat - for i in range(B.rows): - for j in range(B.cols): - v = b[k] - if v: - A._smat[(i, j + A.cols)] = v - k += 1 - else: - for (i, j), v in B._smat.items(): - A._smat[(i, j + A.cols)] = v - A.cols += B.cols - return A -
    -
    [docs] def col_join(self, other): - """Returns B augmented beneath A (row-wise joining):: - - [A] - [B] - - Examples - ======== - - >>> from sympy import SparseMatrix, Matrix, ones - >>> A = SparseMatrix(ones(3)) - >>> A - [1, 1, 1] - [1, 1, 1] - [1, 1, 1] - >>> B = SparseMatrix.eye(3) - >>> B - [1, 0, 0] - [0, 1, 0] - [0, 0, 1] - >>> C = A.col_join(B); C - [1, 1, 1] - [1, 1, 1] - [1, 1, 1] - [1, 0, 0] - [0, 1, 0] - [0, 0, 1] - >>> C == A.col_join(Matrix(B)) - True - - Joining along columns is the same as appending rows at the end - of the matrix: - - >>> C == A.row_insert(A.rows, Matrix(B)) - True - """ - A, B = self, other - if not A.cols == B.cols: - raise ShapeError() - A = A.copy() - if not isinstance(B, SparseMatrix): - k = 0 - b = B._mat - for i in range(B.rows): - for j in range(B.cols): - v = b[k] - if v: - A._smat[(i + A.rows, j)] = v - k += 1 - else: - for (i, j), v in B._smat.items(): - A._smat[i + A.rows, j] = v - A.rows += B.rows - return A -
    - def copyin_list(self, key, value): - if not is_sequence(value): - raise TypeError("`value` must be of type list or tuple.") - self.copyin_matrix(key, Matrix(value)) - - def copyin_matrix(self, key, value): - # include this here because it's not part of BaseMatrix - rlo, rhi, clo, chi = self.key2bounds(key) - shape = value.shape - dr, dc = rhi - rlo, chi - clo - if shape != (dr, dc): - raise ShapeError( - "The Matrix `value` doesn't have the same dimensions " - "as the in sub-Matrix given by `key`.") - if not isinstance(value, SparseMatrix): - for i in range(value.rows): - for j in range(value.cols): - self[i + rlo, j + clo] = value[i, j] - else: - if (rhi - rlo)*(chi - clo) < len(self): - for i in range(rlo, rhi): - for j in range(clo, chi): - self._smat.pop((i, j), None) - else: - for i, j, v in self.row_list(): - if rlo <= i < rhi and clo <= j < chi: - self._smat.pop((i, j), None) - for k, v in value._smat.items(): - i, j = k - self[i + rlo, j + clo] = value[i, j] - -
    [docs] def row_op(self, i, f): - """In-place operation on row i using two-arg functor whose args are - interpreted as (self[i, j], j) for j in range(self.cols). - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix - >>> M = SparseMatrix.eye(3)*2 - >>> M[0, 1] = -1 - >>> M.row_op(1, lambda v, j: v + 2*M[0, j]); M - [2, -1, 0] - [4, 0, 0] - [0, 0, 2] - """ - for j in range(self.cols): - v = self._smat.get((i, j), S.Zero) - fv = f(v, j) - if fv: - self._smat[(i, j)] = fv - elif v: - self._smat.pop((i, j)) -
    -
    [docs] def col_op(self, j, f): - """In-place operation on col j using two-arg functor whose args are - interpreted as (self[i, j], i) for i in range(self.rows). - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix - >>> M = SparseMatrix.eye(3)*2 - >>> M[1, 0] = -1 - >>> M.col_op(1, lambda v, i: v + 2*M[i, 0]); M - [ 2, 4, 0] - [-1, 0, 0] - [ 0, 0, 2] - """ - for i in range(self.rows): - v = self._smat.get((i, j), S.Zero) - fv = f(v, i) - if fv: - self._smat[(i, j)] = fv - elif v: - self._smat.pop((i, j)) -
    -
    [docs] def fill(self, value): - """Fill self with the given value. - - Notes - ===== - - Unless many values are going to be deleted (i.e. set to zero) - this will create a matrix that is slower than a dense matrix in - operations. - - Examples - ======== - - >>> from sympy.matrices import SparseMatrix - >>> M = SparseMatrix.zeros(3); M - [0, 0, 0] - [0, 0, 0] - [0, 0, 0] - >>> M.fill(1); M - [1, 1, 1] - [1, 1, 1] - [1, 1, 1] - """ - if not value: - self._smat = {} - else: - v = sympify(value) - self._smat = dict([((i, j), v) - for i in range(self.rows) for j in range(self.cols)])
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/ntheory/factor_.html b/dev-py3k/_modules/sympy/ntheory/factor_.html deleted file mode 100644 index 53204877ac4..00000000000 --- a/dev-py3k/_modules/sympy/ntheory/factor_.html +++ /dev/null @@ -1,1409 +0,0 @@ - - - - - - - - - - sympy.ntheory.factor_ — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.ntheory.factor_

    -"""
    -Integer factorization
    -"""
    -import random
    -import math
    -
    -from sympy.core.evalf import bitcount
    -from sympy.core.numbers import igcd
    -from sympy.core.power import integer_nthroot, Pow
    -from sympy.core.mul import Mul
    -from sympy.core.compatibility import as_int
    -from .primetest import isprime
    -from .generate import sieve, primerange, nextprime
    -from sympy.core.singleton import S
    -
    -small_trailing = [i and max(int(not i % 2**j) and j for j in range(1, 8))
    -    for i in range(256)]
    -
    -
    -
    [docs]def smoothness(n): - """ - Return the B-smooth and B-power smooth values of n. - - The smoothness of n is the largest prime factor of n; the power- - smoothness is the largest divisor raised to its multiplicity. - - >>> from sympy.ntheory.factor_ import smoothness - >>> smoothness(2**7*3**2) - (3, 128) - >>> smoothness(2**4*13) - (13, 16) - >>> smoothness(2) - (2, 2) - - See Also - ======== - - factorint, smoothness_p - """ - - if n == 1: - return (1, 1) # not prime, but otherwise this causes headaches - facs = factorint(n) - return max(facs), max([m**facs[m] for m in facs]) - -
    -
    [docs]def smoothness_p(n, m=-1, power=0, visual=None): - """ - Return a list of [m, (p, (M, sm(p + m), psm(p + m)))...] - where: - - 1. p**M is the base-p divisor of n - 2. sm(p + m) is the smoothness of p + m (m = -1 by default) - 3. psm(p + n) is the power smoothness of p + m - - The list is sorted according to smoothness (default) or by power smoothness - if power=1. - - The smoothness of the numbers to the left (m = -1) or right (m = 1) of a - factor govern the results that are obtained from the p +/- 1 type factoring - methods. - - >>> from sympy.ntheory.factor_ import smoothness_p, factorint - >>> smoothness_p(10431, m=1) - (1, [(3, (2, 2, 4)), (19, (1, 5, 5)), (61, (1, 31, 31))]) - >>> smoothness_p(10431) - (-1, [(3, (2, 2, 2)), (19, (1, 3, 9)), (61, (1, 5, 5))]) - >>> smoothness_p(10431, power=1) - (-1, [(3, (2, 2, 2)), (61, (1, 5, 5)), (19, (1, 3, 9))]) - - If visual=True then an annotated string will be returned: - - >>> print(smoothness_p(21477639576571, visual=1)) - p**i=4410317**1 has p-1 B=1787, B-pow=1787 - p**i=4869863**1 has p-1 B=2434931, B-pow=2434931 - - This string can also be generated directly from a factorization dictionary - and vice versa: - - >>> factorint(17*9) - {3: 2, 17: 1} - >>> smoothness_p(_) - 'p**i=3**2 has p-1 B=2, B-pow=2\\np**i=17**1 has p-1 B=2, B-pow=16' - >>> smoothness_p(_) - {3: 2, 17: 1} - - The table of the output logic is: - - ====== ====== ======= ======= - | Visual - ------ ---------------------- - Input True False other - ====== ====== ======= ======= - dict str tuple str - str str tuple dict - tuple str tuple str - n str tuple tuple - mul str tuple tuple - ====== ====== ======= ======= - - See Also - ======== - - factorint, smoothness - """ - from sympy.utilities import flatten - - # visual must be True, False or other (stored as None) - if visual in (1, 0): - visual = bool(visual) - elif visual not in (True, False): - visual = None - - if type(n) is str: - if visual: - return n - d = {} - for li in n.splitlines(): - k, v = [int(i) for i in - li.split('has')[0].split('=')[1].split('**')] - d[k] = v - if visual is not True and visual is not False: - return d - return smoothness_p(d, visual=False) - elif type(n) is not tuple: - facs = factorint(n, visual=False) - - if power: - k = -1 - else: - k = 1 - if type(n) is not tuple: - rv = (m, sorted([(f, - tuple([M] + list(smoothness(f + m)))) - for f, M in [i for i in list(facs.items())]], - key=lambda x: (x[1][k], x[0]))) - else: - rv = n - - if visual is False or (visual is not True) and (type(n) in [int, Mul]): - return rv - lines = [] - for dat in rv[1]: - dat = flatten(dat) - dat.insert(2, m) - lines.append('p**i=%i**%i has p%+i B=%i, B-pow=%i' % tuple(dat)) - return '\n'.join(lines) - -
    -
    [docs]def trailing(n): - """Count the number of trailing zero digits in the binary - representation of n, i.e. determine the largest power of 2 - that divides n. - - Examples - ======== - - >>> from sympy import trailing - >>> trailing(128) - 7 - >>> trailing(63) - 0 - """ - n = int(n) - if not n: - return 0 - low_byte = n & 0xff - if low_byte: - return small_trailing[low_byte] - - # 2**m is quick for z up through 2**30 - z = bitcount(n) - 1 - if type(z) is int: - if n == 1 << z: - return z - - t = 0 - p = 8 - while not n & 1: - while not n & ((1 << p) - 1): - n >>= p - t += p - p *= 2 - p //= 2 - return t - -
    -
    [docs]def multiplicity(p, n): - """ - Find the greatest integer m such that p**m divides n. - - Examples - ======== - - >>> from sympy.ntheory import multiplicity - >>> [multiplicity(5, n) for n in [8, 5, 25, 125, 250]] - [0, 1, 2, 3, 3] - - """ - p, n = as_int(p), as_int(n) - if p == 2: - return trailing(n) - if p < 2: - raise ValueError('p must be an integer, 2 or larger, but got %s' % p) - if p == n: - return 1 - - m = 0 - n, rem = divmod(n, p) - while not rem: - m += 1 - if m > 5: - # The multiplicity could be very large. Better - # to increment in powers of two - e = 2 - while 1: - ppow = p**e - if ppow < n: - nnew, rem = divmod(n, ppow) - if not rem: - m += e - e *= 2 - n = nnew - continue - return m + multiplicity(p, n) - n, rem = divmod(n, p) - return m - -
    -
    [docs]def perfect_power(n, candidates=None, big=True, factor=True): - """ - Return ``(b, e)`` such that ``n`` == ``b**e`` if ``n`` is a - perfect power; otherwise return ``False``. - - By default, the base is recursively decomposed and the exponents - collected so the largest possible ``e`` is sought. If ``big=False`` - then the smallest possible ``e`` (thus prime) will be chosen. - - If ``candidates`` for exponents are given, they are assumed to be sorted - and the first one that is larger than the computed maximum will signal - failure for the routine. - - If ``factor=True`` then simultaneous factorization of n is attempted - since finding a factor indicates the only possible root for n. This - is True by default since only a few small factors will be tested in - the course of searching for the perfect power. - - Examples - ======== - - >>> from sympy import perfect_power - >>> perfect_power(16) - (2, 4) - >>> perfect_power(16, big = False) - (4, 2) - """ - n = int(n) - if n < 3: - return False - logn = math.log(n, 2) - max_possible = int(logn) + 2 # only check values less than this - not_square = n % 10 in [2, 3, 7, 8] # squares cannot end in 2, 3, 7, 8 - if not candidates: - candidates = primerange(2 + not_square, max_possible) - - afactor = 2 + n % 2 - for e in candidates: - if e < 3: - if e == 1 or e == 2 and not_square: - continue - if e > max_possible: - return False - - # see if there is a factor present - if factor: - if n % afactor == 0: - # find what the potential power is - if afactor == 2: - e = trailing(n) - else: - e = multiplicity(afactor, n) - # if it's a trivial power we are done - if e == 1: - return False - - # maybe the bth root of n is exact - r, exact = integer_nthroot(n, e) - if not exact: - # then remove this factor and check to see if - # any of e's factors are a common exponent; if - # not then it's not a perfect power - n //= afactor**e - m = perfect_power(n, candidates=primefactors(e), big=big) - if m is False: - return False - else: - r, m = m - # adjust the two exponents so the bases can - # be combined - g = igcd(m, e) - if g == 1: - return False - m //= g - e //= g - r, e = r**m*afactor**e, g - if not big: - e0 = primefactors(e) - if len(e0) > 1 or e0[0] != e: - e0 = e0[0] - r, e = r**(e//e0), e0 - return r, e - else: - # get the next factor ready for the next pass through the loop - afactor = nextprime(afactor) - - # Weed out downright impossible candidates - if logn/e < 40: - b = 2.0**(logn/e) - if abs(int(b + 0.5) - b) > 0.01: - continue - - # now see if the plausible e makes a perfect power - r, exact = integer_nthroot(n, e) - if exact: - if big: - m = perfect_power(r, big=big, factor=factor) - if m is not False: - r, e = m[0], e*m[1] - return int(r), e - else: - return False - -
    -
    [docs]def pollard_rho(n, s=2, a=1, retries=5, seed=1234, max_steps=None, F=None): - r""" - Use Pollard's rho method to try to extract a nontrivial factor - of ``n``. The returned factor may be a composite number. If no - factor is found, ``None`` is returned. - - The algorithm generates pseudo-random values of x with a generator - function, replacing x with F(x). If F is not supplied then the - function x**2 + ``a`` is used. The first value supplied to F(x) is ``s``. - Upon failure (if ``retries`` is > 0) a new ``a`` and ``s`` will be - supplied; the ``a`` will be ignored if F was supplied. - - The sequence of numbers generated by such functions generally have a - a lead-up to some number and then loop around back to that number and - begin to repeat the sequence, e.g. 1, 2, 3, 4, 5, 3, 4, 5 -- this leader - and loop look a bit like the Greek letter rho, and thus the name, 'rho'. - - For a given function, very different leader-loop values can be obtained - so it is a good idea to allow for retries: - - >>> from sympy.ntheory.generate import cycle_length - >>> n = 16843009 - >>> F = lambda x:(2048*pow(x, 2, n) + 32767) % n - >>> for s in range(5): - ... print('loop length = %4i; leader length = %3i' % next(cycle_length(F, s))) - ... - loop length = 2489; leader length = 42 - loop length = 78; leader length = 120 - loop length = 1482; leader length = 99 - loop length = 1482; leader length = 285 - loop length = 1482; leader length = 100 - - Here is an explicit example where there is a two element leadup to - a sequence of 3 numbers (11, 14, 4) that then repeat: - - >>> x=2 - >>> for i in range(9): - ... x=(x**2+12)%17 - ... print(x, end=' ') - ... - 16 13 11 14 4 11 14 4 11 - >>> next(cycle_length(lambda x: (x**2+12)%17, 2)) - (3, 2) - >>> list(cycle_length(lambda x: (x**2+12)%17, 2, values=True)) - [16, 13, 11, 14, 4] - - Instead of checking the differences of all generated values for a gcd - with n, only the kth and 2*kth numbers are checked, e.g. 1st and 2nd, - 2nd and 4th, 3rd and 6th until it has been detected that the loop has been - traversed. Loops may be many thousands of steps long before rho finds a - factor or reports failure. If ``max_steps`` is specified, the iteration - is cancelled with a failure after the specified number of steps. - - Examples - ======== - - >>> from sympy import pollard_rho - >>> n=16843009 - >>> F=lambda x:(2048*pow(x,2,n) + 32767) % n - >>> pollard_rho(n, F=F) - 257 - - Use the default setting with a bad value of ``a`` and no retries: - - >>> pollard_rho(n, a=n-2, retries=0) - - If retries is > 0 then perhaps the problem will correct itself when - new values are generated for a: - - >>> pollard_rho(n, a=n-2, retries=1) - 257 - - References - ========== - - - Richard Crandall & Carl Pomerance (2005), "Prime Numbers: - A Computational Perspective", Springer, 2nd edition, 229-231 - - """ - n = int(n) - if n < 5: - raise ValueError('pollard_rho should receive n > 4') - prng = random.Random(seed + retries) - V = s - for i in range(retries + 1): - U = V - if not F: - F = lambda x: (pow(x, 2, n) + a) % n - j = 0 - while 1: - if max_steps and (j > max_steps): - break - j += 1 - U = F(U) - V = F(F(V)) # V is 2x further along than U - g = igcd(U - V, n) - if g == 1: - continue - if g == n: - break - return int(g) - V = prng.randint(0, n - 1) - a = prng.randint(1, n - 3) # for x**2 + a, a%n should not be 0 or -2 - F = None - return None - -
    -
    [docs]def pollard_pm1(n, B=10, a=2, retries=0, seed=1234): - """ - Use Pollard's p-1 method to try to extract a nontrivial factor - of ``n``. Either a divisor (perhaps composite) or ``None`` is returned. - - The value of ``a`` is the base that is used in the test gcd(a**M - 1, n). - The default is 2. If ``retries`` > 0 then if no factor is found after the - first attempt, a new ``a`` will be generated randomly (using the ``seed``) - and the process repeated. - - Note: the value of M is lcm(1..B) = reduce(ilcm, range(2, B + 1)). - - A search is made for factors next to even numbers having a power smoothness - less than ``B``. Choosing a larger B increases the likelihood of finding a - larger factor but takes longer. Whether a factor of n is found or not - depends on ``a`` and the power smoothness of the even mumber just less than - the factor p (hence the name p - 1). - - Although some discussion of what constitutes a good ``a`` some - descriptions are hard to interpret. At the modular.math site referenced - below it is stated that if gcd(a**M - 1, n) = N then a**M % q**r is 1 - for every prime power divisor of N. But consider the following: - - >>> from sympy.ntheory.factor_ import smoothness_p, pollard_pm1 - >>> n=257*1009 - >>> smoothness_p(n) - (-1, [(257, (1, 2, 256)), (1009, (1, 7, 16))]) - - So we should (and can) find a root with B=16: - - >>> pollard_pm1(n, B=16, a=3) - 1009 - - If we attempt to increase B to 256 we find that it doesn't work: - - >>> pollard_pm1(n, B=256) - >>> - - But if the value of ``a`` is changed we find that only multiples of - 257 work, e.g.: - - >>> pollard_pm1(n, B=256, a=257) - 1009 - - Checking different ``a`` values shows that all the ones that didn't - work had a gcd value not equal to ``n`` but equal to one of the - factors: - - >>> from sympy.core.numbers import ilcm, igcd - >>> from sympy import factorint, Pow - >>> M = 1 - >>> for i in range(2, 256): - ... M = ilcm(M, i) - ... - >>> set([igcd(pow(a, M, n) - 1, n) for a in range(2, 256) if - ... igcd(pow(a, M, n) - 1, n) != n]) - set([1009]) - - But does aM % d for every divisor of n give 1? - - >>> aM = pow(255, M, n) - >>> [(d, aM%Pow(*d.args)) for d in factorint(n, visual=True).args] - [(257**1, 1), (1009**1, 1)] - - No, only one of them. So perhaps the principle is that a root will - be found for a given value of B provided that: - - 1) the power smoothness of the p - 1 value next to the root - does not exceed B - 2) a**M % p != 1 for any of the divisors of n. - - By trying more than one ``a`` it is possible that one of them - will yield a factor. - - Examples - ======== - - With the default smoothness bound, this number can't be cracked: - - >>> from sympy.ntheory import pollard_pm1, primefactors - >>> pollard_pm1(21477639576571) - - Increasing the smoothness bound helps: - - >>> pollard_pm1(21477639576571, B=2000) - 4410317 - - Looking at the smoothness of the factors of this number we find: - - >>> from sympy.utilities import flatten - >>> from sympy.ntheory.factor_ import smoothness_p, factorint - >>> print(smoothness_p(21477639576571, visual=1)) - p**i=4410317**1 has p-1 B=1787, B-pow=1787 - p**i=4869863**1 has p-1 B=2434931, B-pow=2434931 - - The B and B-pow are the same for the p - 1 factorizations of the divisors - because those factorizations had a very large prime factor: - - >>> factorint(4410317 - 1) - {2: 2, 617: 1, 1787: 1} - >>> factorint(4869863-1) - {2: 1, 2434931: 1} - - Note that until B reaches the B-pow value of 1787, the number is not cracked; - - >>> pollard_pm1(21477639576571, B=1786) - >>> pollard_pm1(21477639576571, B=1787) - 4410317 - - The B value has to do with the factors of the number next to the divisor, - not the divisors themselves. A worst case scenario is that the number next - to the factor p has a large prime divisisor or is a perfect power. If these - conditions apply then the power-smoothness will be about p/2 or p. The more - realistic is that there will be a large prime factor next to p requiring - a B value on the order of p/2. Although primes may have been searched for - up to this level, the p/2 is a factor of p - 1, something that we don't - know. The modular.math reference below states that 15% of numbers in the - range of 10**15 to 15**15 + 10**4 are 10**6 power smooth so a B of 10**6 - will fail 85% of the time in that range. From 10**8 to 10**8 + 10**3 the - percentages are nearly reversed...but in that range the simple trial - division is quite fast. - - References - ========== - - - Richard Crandall & Carl Pomerance (2005), "Prime Numbers: - A Computational Perspective", Springer, 2nd edition, 236-238 - - http://modular.math.washington.edu/edu/2007/spring/ent/ent-html/ - node81.html - - http://www.cs.toronto.edu/~yuvalf/Factorization.pdf - """ - - n = int(n) - if n < 4 or B < 3: - raise ValueError('pollard_pm1 should receive n > 3 and B > 2') - prng = random.Random(seed + B) - - # computing a**lcm(1,2,3,..B) % n for B > 2 - # it looks weird, but it's right: primes run [2, B] - # and the answer's not right until the loop is done. - for i in range(retries + 1): - aM = a - for p in sieve.primerange(2, B + 1): - e = int(math.log(B, p)) - aM = pow(aM, pow(p, e), n) - g = igcd(aM - 1, n) - if 1 < g < n: - return int(g) - - # get a new a: - # since the exponent, lcm(1..B), is even, if we allow 'a' to be 'n-1' - # then (n - 1)**even % n will be 1 which will give a g of 0 and 1 will - # give a zero, too, so we set the range as [2, n-2]. Some references - # say 'a' should be coprime to n, but either will detect factors. - a = prng.randint(2, n - 2) - -
    -def _trial(factors, n, candidates, verbose=False): - """ - Helper function for integer factorization. Trial factors ``n` - against all integers given in the sequence ``candidates`` - and updates the dict ``factors`` in-place. Returns the reduced - value of ``n`` and a flag indicating whether any factors were found. - """ - if verbose: - factors0 = list(factors.keys()) - nfactors = len(factors) - for d in candidates: - if n % d == 0: - m = multiplicity(d, n) - n //= d**m - factors[d] = m - if verbose: - for k in sorted(set(factors).difference(set(factors0))): - print(factor_msg % (k, factors[k])) - return int(n), len(factors) != nfactors - - -def _check_termination(factors, n, limitp1, use_trial, use_rho, use_pm1, - verbose): - """ - Helper function for integer factorization. Checks if ``n`` - is a prime or a perfect power, and in those cases updates - the factorization and raises ``StopIteration``. - """ - - if verbose: - print('Check for termination') - - # since we've already been factoring there is no need to do - # simultaneous factoring with the power check - p = perfect_power(n, factor=False) - if p is not False: - base, exp = p - if limitp1: - limit = limitp1 - 1 - else: - limit = limitp1 - facs = factorint(base, limit, use_trial, use_rho, use_pm1, - verbose=False) - for b, e in list(facs.items()): - if verbose: - print(factor_msg % (b, e)) - factors[b] = exp*e - raise StopIteration - - if isprime(n): - factors[int(n)] = 1 - raise StopIteration - - if n == 1: - raise StopIteration - -trial_int_msg = "Trial division with ints [%i ... %i] and fail_max=%i" -trial_msg = "Trial division with primes [%i ... %i]" -rho_msg = "Pollard's rho with retries %i, max_steps %i and seed %i" -pm1_msg = "Pollard's p-1 with smoothness bound %i and seed %i" -factor_msg = '\t%i ** %i' -fermat_msg = 'Close factors satisying Fermat condition found.' -complete_msg = 'Factorization is complete.' - - -def _factorint_small(factors, n, limit, fail_max): - """ - Return the value of n and either a 0 (indicating that factorization up - to the limit was complete) or else the next near-prime that would have - been tested. - - Factoring stops if there are fail_max unsuccessful tests in a row. - - If factors of n were found they will be in the factors dictionary as - {factor: multiplicity} and the returned value of n will have had those - factors removed. The factors dictionary is modified in-place. - - """ - - def done(n, d): - """return n, d if the sqrt(n) wasn't reached yet, else - n, 0 indicating that factoring is done. - """ - if d*d <= n: - return n, d - return n, 0 - - d = 2 - m = trailing(n) - if m: - factors[d] = m - n >>= m - d = 3 - if limit < d: - if n > 1: - factors[n] = 1 - return done(n, d) - # reduce - m = 0 - while n % d == 0: - n //= d - m += 1 - if m == 20: - mm = multiplicity(d, n) - m += mm - n //= d**mm - break - if m: - factors[d] = m - - # when d*d exceeds maxx or n we are done; if limit**2 is greater - # than n then maxx is set to zero so the value of n will flag the finish - if limit*limit > n: - maxx = 0 - else: - maxx = limit*limit - - dd = maxx or n - d = 5 - fails = 0 - while fails < fail_max: - if d*d > dd: - break - # d = 6*i - 1 - # reduce - m = 0 - while n % d == 0: - n //= d - m += 1 - if m == 20: - mm = multiplicity(d, n) - m += mm - n //= d**mm - break - if m: - factors[d] = m - dd = maxx or n - fails = 0 - else: - fails += 1 - d += 2 - if d*d > dd: - break - # d = 6*i - 1 - # reduce - m = 0 - while n % d == 0: - n //= d - m += 1 - if m == 20: - mm = multiplicity(d, n) - m += mm - n //= d**mm - break - if m: - factors[d] = m - dd = maxx or n - fails = 0 - else: - fails += 1 - # d = 6*(i+1) - 1 - d += 4 - - return done(n, d) - - -
    [docs]def factorint(n, limit=None, use_trial=True, use_rho=True, use_pm1=True, - verbose=False, visual=None): - r""" - Given a positive integer ``n``, ``factorint(n)`` returns a dict containing - the prime factors of ``n`` as keys and their respective multiplicities - as values. For example: - - >>> from sympy.ntheory import factorint - >>> factorint(2000) # 2000 = (2**4) * (5**3) - {2: 4, 5: 3} - >>> factorint(65537) # This number is prime - {65537: 1} - - For input less than 2, factorint behaves as follows: - - - ``factorint(1)`` returns the empty factorization, ``{}`` - - ``factorint(0)`` returns ``{0:1}`` - - ``factorint(-n)`` adds ``-1:1`` to the factors and then factors ``n`` - - Partial Factorization: - - If ``limit`` (> 3) is specified, the search is stopped after performing - trial division up to (and including) the limit (or taking a - corresponding number of rho/p-1 steps). This is useful if one has - a large number and only is interested in finding small factors (if - any). Note that setting a limit does not prevent larger factors - from being found early; it simply means that the largest factor may - be composite. Since checking for perfect power is relatively cheap, it is - done regardless of the limit setting. - - This number, for example, has two small factors and a huge - semi-prime factor that cannot be reduced easily: - - >>> from sympy.ntheory import isprime - >>> a = 1407633717262338957430697921446883 - >>> f = factorint(a, limit=10000) - >>> f == {991: 1, 202916782076162456022877024859: 1, 7: 1} - True - >>> isprime(max(f)) - False - - This number has a small factor and a residual perfect power whose - base is greater than the limit: - - >>> factorint(3*101**7, limit=5) - {3: 1, 101: 7} - - Visual Factorization: - - If ``visual`` is set to ``True``, then it will return a visual - factorization of the integer. For example: - - >>> from sympy import pprint - >>> pprint(factorint(4200, visual=True)) - 3 1 2 1 - 2 *3 *5 *7 - - Note that this is achieved by using the evaluate=False flag in Mul - and Pow. If you do other manipulations with an expression where - evaluate=False, it may evaluate. Therefore, you should use the - visual option only for visualization, and use the normal dictionary - returned by visual=False if you want to perform operations on the - factors. - - You can easily switch between the two forms by sending them back to - factorint: - - >>> from sympy import Mul, Pow - >>> regular = factorint(1764); regular - {2: 2, 3: 2, 7: 2} - >>> pprint(factorint(regular)) - 2 2 2 - 2 *3 *7 - - >>> visual = factorint(1764, visual=True); pprint(visual) - 2 2 2 - 2 *3 *7 - >>> print(factorint(visual)) - {2: 2, 3: 2, 7: 2} - - If you want to send a number to be factored in a partially factored form - you can do so with a dictionary or unevaluated expression: - - >>> factorint(factorint({4: 2, 12: 3})) # twice to toggle to dict form - {2: 10, 3: 3} - >>> factorint(Mul(4, 12, **dict(evaluate=False))) - {2: 4, 3: 1} - - The table of the output logic is: - - ====== ====== ======= ======= - Visual - ------ ---------------------- - Input True False other - ====== ====== ======= ======= - dict mul dict mul - n mul dict dict - mul mul dict dict - ====== ====== ======= ======= - - Notes - ===== - - Algorithm: - - The function switches between multiple algorithms. Trial division - quickly finds small factors (of the order 1-5 digits), and finds - all large factors if given enough time. The Pollard rho and p-1 - algorithms are used to find large factors ahead of time; they - will often find factors of the order of 10 digits within a few - seconds: - - >>> factors = factorint(12345678910111213141516) - >>> for base, exp in sorted(factors.items()): - ... print(base, exp) - ... - 2 2 - 2507191691 1 - 1231026625769 1 - - Any of these methods can optionally be disabled with the following - boolean parameters: - - - ``use_trial``: Toggle use of trial division - - ``use_rho``: Toggle use of Pollard's rho method - - ``use_pm1``: Toggle use of Pollard's p-1 method - - ``factorint`` also periodically checks if the remaining part is - a prime number or a perfect power, and in those cases stops. - - - If ``verbose`` is set to ``True``, detailed progress is printed. - - See Also - ======== - - smoothness, smoothness_p, divisors - - """ - factordict = {} - if visual and not isinstance(n, Mul) and not isinstance(n, dict): - factordict = factorint(n, limit=limit, use_trial=use_trial, - use_rho=use_rho, use_pm1=use_pm1, - verbose=verbose, visual=False) - elif isinstance(n, Mul): - factordict = dict([(int(k), int(v)) for k, v in - list(n.as_powers_dict().items())]) - elif isinstance(n, dict): - factordict = n - if factordict and (isinstance(n, Mul) or isinstance(n, dict)): - # check it - for k in list(factordict.keys()): - if isprime(k): - continue - e = factordict.pop(k) - d = factorint(k, limit=limit, use_trial=use_trial, use_rho=use_rho, - use_pm1=use_pm1, verbose=verbose, visual=False) - for k, v in list(d.items()): - if k in factordict: - factordict[k] += v*e - else: - factordict[k] = v*e - if visual or (type(n) is dict and - visual is not True and - visual is not False): - if factordict == {}: - return S.One - if -1 in factordict: - factordict.pop(-1) - args = [S.NegativeOne] - else: - args = [] - args.extend([Pow(*i, **{'evaluate':False}) - for i in sorted(factordict.items())]) - return Mul(*args, **{'evaluate': False}) - elif isinstance(n, dict) or isinstance(n, Mul): - return factordict - - assert use_trial or use_rho or use_pm1 - - n = int(n) - if limit: - limit = int(limit) - - # special cases - if n < 0: - factors = factorint( - -n, limit=limit, use_trial=use_trial, use_rho=use_rho, - use_pm1=use_pm1, verbose=verbose, visual=False) - factors[-1] = 1 - return factors - - if limit: - if limit < 2: - if n == 1: - return {} - return {n: 1} - elif n < 10: - # doing this we are assured of getting a limit > 2 - # when we have to compute it later - return [{0: 1}, {}, {2: 1}, {3: 1}, {2: 2}, {5: 1}, - {2: 1, 3: 1}, {7: 1}, {2: 3}, {3: 2}][n] - - factors = {} - - # do simplistic factorization - if verbose: - sn = str(n) - if len(sn) > 50: - print('Factoring %s' % sn[:5] + \ - '..(%i other digits)..' % (len(sn) - 10) + sn[-5:]) - else: - print('Factoring', n) - - if use_trial: - # this is the preliminary factorization for small factors - small = 2**15 - fail_max = 600 - small = min(small, limit or small) - if verbose: - print(trial_int_msg % (2, small, fail_max)) - n, next_p = _factorint_small(factors, n, small, fail_max) - else: - next_p = 2 - if factors and verbose: - for k in sorted(factors): - print(factor_msg % (k, factors[k])) - if next_p == 0: - if n > 1: - factors[int(n)] = 1 - if verbose: - print(complete_msg) - return factors - - # continue with more advanced factorization methods - - # first check if the simplistic run didn't finish - # because of the limit and check for a perfect - # power before exiting - try: - if limit and next_p > limit: - if verbose: - print('Exceeded limit:', limit) - - _check_termination(factors, n, limit, use_trial, use_rho, use_pm1, - verbose) - - if n > 1: - factors[int(n)] = 1 - return factors - else: - # Before quitting (or continuing on)... - - # ...do a Fermat test since it's so easy and we need the - # square root anyway. Finding 2 factors is easy if they are - # "close enough." This is the big root equivalent of dividing by - # 2, 3, 5. - sqrt_n = integer_nthroot(n, 2)[0] - a = sqrt_n + 1 - a2 = a**2 - b2 = a2 - n - for i in range(3): - b, fermat = integer_nthroot(b2, 2) - if fermat: - break - b2 += 2*a + 1 # equiv to (a+1)**2 - n - a += 1 - if fermat: - if verbose: - print(fermat_msg) - if limit: - limit -= 1 - for r in [a - b, a + b]: - facs = factorint(r, limit=limit, use_trial=use_trial, - use_rho=use_rho, use_pm1=use_pm1, - verbose=verbose) - factors.update(facs) - raise StopIteration - - # ...see if factorization can be terminated - _check_termination(factors, n, limit, use_trial, use_rho, use_pm1, - verbose) - - except StopIteration: - if verbose: - print(complete_msg) - return factors - - # these are the limits for trial division which will - # be attempted in parallel with pollard methods - low, high = next_p, 2*next_p - - limit = limit or sqrt_n - # add 1 to make sure limit is reached in primerange calls - limit += 1 - - while 1: - - try: - high_ = high - if limit < high_: - high_ = limit - - # Trial division - if use_trial: - if verbose: - print(trial_msg % (low, high_)) - ps = sieve.primerange(low, high_) - n, found_trial = _trial(factors, n, ps, verbose) - if found_trial: - _check_termination(factors, n, limit, use_trial, use_rho, - use_pm1, verbose) - else: - found_trial = False - - if high > limit: - if verbose: - print('Exceeded limit:', limit) - if n > 1: - factors[int(n)] = 1 - raise StopIteration - - # Only used advanced methods when no small factors were found - if not found_trial: - if (use_pm1 or use_rho): - high_root = max(int(math.log(high_**0.7)), low, 3) - - # Pollard p-1 - if use_pm1: - if verbose: - print((pm1_msg % (high_root, high_))) - c = pollard_pm1(n, B=high_root, seed=high_) - if c: - # factor it and let _trial do the update - ps = factorint(c, limit=limit - 1, - use_trial=use_trial, - use_rho=use_rho, - use_pm1=use_pm1, - verbose=verbose) - n, _ = _trial(factors, n, ps, verbose=False) - _check_termination(factors, n, limit, use_trial, - use_rho, use_pm1, verbose) - - # Pollard rho - if use_rho: - max_steps = high_root - if verbose: - print((rho_msg % (1, max_steps, high_))) - c = pollard_rho(n, retries=1, max_steps=max_steps, - seed=high_) - if c: - # factor it and let _trial do the update - ps = factorint(c, limit=limit - 1, - use_trial=use_trial, - use_rho=use_rho, - use_pm1=use_pm1, - verbose=verbose) - n, _ = _trial(factors, n, ps, verbose=False) - _check_termination(factors, n, limit, use_trial, - use_rho, use_pm1, verbose) - - except StopIteration: - if verbose: - print(complete_msg) - return factors - - low, high = high, high*2 - -
    -
    [docs]def primefactors(n, limit=None, verbose=False): - """Return a sorted list of n's prime factors, ignoring multiplicity - and any composite factor that remains if the limit was set too low - for complete factorization. Unlike factorint(), primefactors() does - not return -1 or 0. - - Examples - ======== - - >>> from sympy.ntheory import primefactors, factorint, isprime - >>> primefactors(6) - [2, 3] - >>> primefactors(-5) - [5] - - >>> sorted(factorint(123456).items()) - [(2, 6), (3, 1), (643, 1)] - >>> primefactors(123456) - [2, 3, 643] - - >>> sorted(factorint(10000000001, limit=200).items()) - [(101, 1), (99009901, 1)] - >>> isprime(99009901) - False - >>> primefactors(10000000001, limit=300) - [101] - - See Also - ======== - - divisors - """ - n = int(n) - s = [] - factors = sorted(factorint(n, limit=limit, verbose=verbose).keys()) - s = [f for f in factors[:-1:] if f not in [-1, 0, 1]] - if factors and isprime(factors[-1]): - s += [factors[-1]] - return s - -
    -def _divisors(n): - """Helper function for divisors which generates the divisors.""" - - factordict = factorint(n) - ps = sorted(factordict.keys()) - - def rec_gen(n=0): - if n == len(ps): - yield 1 - else: - pows = [1] - for j in range(factordict[ps[n]]): - pows.append(pows[-1] * ps[n]) - for q in rec_gen(n + 1): - for p in pows: - yield p * q - - for p in rec_gen(): - yield p - - -
    [docs]def divisors(n, generator=False): - r""" - Return all divisors of n sorted from 1..n by default. - If generator is True an unordered generator is returned. - - The number of divisors of n can be quite large if there are many - prime factors (counting repeated factors). If only the number of - factors is desired use divisor_count(n). - - Examples - ======== - - >>> from sympy import divisors, divisor_count - >>> divisors(24) - [1, 2, 3, 4, 6, 8, 12, 24] - >>> divisor_count(24) - 8 - - >>> list(divisors(120, generator=True)) - [1, 2, 4, 8, 3, 6, 12, 24, 5, 10, 20, 40, 15, 30, 60, 120] - - This is a slightly modified version of Tim Peters referenced at: - http://stackoverflow.com/questions/1010381/python-factorization - - See Also - ======== - - primefactors, factorint, divisor_count - """ - - n = abs(n) - if isprime(n): - return [1, n] - elif n == 1: - return [1] - elif n == 0: - return [] - else: - rv = _divisors(n) - if not generator: - return sorted(rv) - return rv - -
    -
    [docs]def divisor_count(n, modulus=1): - """ - Return the number of divisors of ``n``. If ``modulus`` is not 1 then only - those that are divisible by ``modulus`` are counted. - - References - ========== - - - http://www.mayer.dial.pipex.com/maths/formulae.htm - - >>> from sympy import divisor_count - >>> divisor_count(6) - 4 - - See Also - ======== - - factorint, divisors, totient - """ - - if not modulus: - return 0 - elif modulus != 1: - n, r = divmod(n, modulus) - if r: - return 0 - if n == 0: - return 0 - return Mul(*[v + 1 for k, v in list(factorint(n).items()) if k > 1]) - -
    -
    [docs]def totient(n): - """ - Calculate the Euler totient function phi(n) - - >>> from sympy.ntheory import totient - >>> totient(1) - 1 - >>> totient(25) - 20 - - See Also - ======== - - divisor_count - """ - n = as_int(n) - if n < 1: - raise ValueError("n must be a positive integer") - factors = factorint(n) - t = 1 - for p, k in factors.items(): - t *= (p - 1) * p**(k - 1) - return t
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/ntheory/generate.html b/dev-py3k/_modules/sympy/ntheory/generate.html deleted file mode 100644 index f5aecfea3c8..00000000000 --- a/dev-py3k/_modules/sympy/ntheory/generate.html +++ /dev/null @@ -1,740 +0,0 @@ - - - - - - - - - - sympy.ntheory.generate — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.ntheory.generate

    -"""
    -Generating and counting primes.
    -
    -"""
    -import random
    -from bisect import bisect
    -# Using arrays for sieving instead of lists greatly reduces
    -# memory consumption
    -from array import array as _array
    -
    -from sympy.core.compatibility import as_int
    -from .primetest import isprime
    -
    -
    -def _arange(a, b):
    -    ar = _array('l', [0]*(b - a))
    -    for i, e in enumerate(range(a, b)):
    -        ar[i] = e
    -    return ar
    -
    -
    -
    [docs]class Sieve: - """An infinite list of prime numbers, implemented as a dynamically - growing sieve of Eratosthenes. When a lookup is requested involving - an odd number that has not been sieved, the sieve is automatically - extended up to that number. - - >>> from sympy import sieve - >>> from array import array # this line and next for doctest only - >>> sieve._list = array('l', [2, 3, 5, 7, 11, 13]) - - >>> 25 in sieve - False - >>> sieve._list - array('l', [2, 3, 5, 7, 11, 13, 17, 19, 23]) - """ - - # data shared (and updated) by all Sieve instances - _list = _array('l', [2, 3, 5, 7, 11, 13]) - - def __repr__(self): - return "<Sieve with %i primes sieved: 2, 3, 5, ... %i, %i>" % \ - (len(self._list), self._list[-2], self._list[-1]) - -
    [docs] def extend(self, n): - """Grow the sieve to cover all primes <= n (a real number). - - Examples - ======== - - >>> from sympy import sieve - >>> from array import array # this line and next for doctest only - >>> sieve._list = array('l', [2, 3, 5, 7, 11, 13]) - - >>> sieve.extend(30) - >>> sieve[10] == 29 - True - """ - n = int(n) - if n <= self._list[-1]: - return - - # We need to sieve against all bases up to sqrt(n). - # This is a recursive call that will do nothing if there are enough - # known bases already. - maxbase = int(n**0.5) + 1 - self.extend(maxbase) - - # Create a new sieve starting from sqrt(n) - begin = self._list[-1] + 1 - newsieve = _arange(begin, n + 1) - - # Now eliminate all multiples of primes in [2, sqrt(n)] - for p in self.primerange(2, maxbase): - # Start counting at a multiple of p, offsetting - # the index to account for the new sieve's base index - startindex = (-begin) % p - for i in range(startindex, len(newsieve), p): - newsieve[i] = 0 - - # Merge the sieves - self._list += _array('l', [x for x in newsieve if x]) -
    -
    [docs] def extend_to_no(self, i): - """Extend to include the ith prime number. - - i must be an integer. - - The list is extended by 50% if it is too short, so it is - likely that it will be longer than requested. - - Examples - ======== - - >>> from sympy import sieve - >>> from array import array # this line and next for doctest only - >>> sieve._list = array('l', [2, 3, 5, 7, 11, 13]) - - >>> sieve.extend_to_no(9) - >>> sieve._list - array('l', [2, 3, 5, 7, 11, 13, 17, 19, 23]) - """ - i = as_int(i) - while len(self._list) < i: - self.extend(int(self._list[-1] * 1.5)) -
    -
    [docs] def primerange(self, a, b): - """Generate all prime numbers in the range [a, b). - - Examples - ======== - - >>> from sympy import sieve - >>> print([i for i in sieve.primerange(7, 18)]) - [7, 11, 13, 17] - """ - from sympy.functions.elementary.integers import ceiling - - # wrapping ceiling in int will raise an error if there was a problem - # determining whether the expression was exactly an integer or not - a = max(2, int(ceiling(a))) - b = int(ceiling(b)) - if a >= b: - return - self.extend(b) - i = self.search(a)[1] - maxi = len(self._list) + 1 - while i < maxi: - p = self._list[i - 1] - if p < b: - yield p - i += 1 - else: - return -
    -
    [docs] def search(self, n): - """Return the indices i, j of the primes that bound n. - - If n is prime then i == j. - - Although n can be an expression, if ceiling cannot convert - it to an integer then an n error will be raised. - - Examples - ======== - - >>> from sympy import sieve - >>> sieve.search(25) - (9, 10) - >>> sieve.search(23) - (9, 9) - """ - from sympy.functions.elementary.integers import ceiling - - # wrapping ceiling in int will raise an error if there was a problem - # determining whether the expression was exactly an integer or not - test = int(ceiling(n)) - n = int(n) - if n < 2: - raise ValueError("n should be >= 2 but got: %s" % n) - if n > self._list[-1]: - self.extend(n) - b = bisect(self._list, n) - if self._list[b - 1] == test: - return b, b - else: - return b, b + 1 -
    - def __contains__(self, n): - try: - n = as_int(n) - assert n >= 2 - except (ValueError, AssertionError): - return False - if n % 2 == 0: - return n == 2 - a, b = self.search(n) - return a == b - - def __getitem__(self, n): - """Return the nth prime number""" - n = as_int(n) - self.extend_to_no(n) - return self._list[n - 1] - -# Generate a global object for repeated use in trial division etc
    -sieve = Sieve() - - -
    [docs]def prime(nth): - """ Return the nth prime, with the primes indexed as prime(1) = 2, - prime(2) = 3, etc.... The nth prime is approximately n*log(n) and - can never be larger than 2**n. - - References - ========== - - - http://primes.utm.edu/glossary/xpage/BertrandsPostulate.html - - Examples - ======== - - >>> from sympy import prime - >>> prime(10) - 29 - >>> prime(1) - 2 - - See Also - ======== - - sympy.ntheory.primetest.isprime : Test if n is prime - primerange : Generate all primes in a given range - primepi : Return the number of primes less than or equal to n - """ - n = as_int(nth) - if n < 1: - raise ValueError("nth must be a positive integer; prime(1) == 2") - return sieve[n] - -
    -
    [docs]def primepi(n): - """ Return the value of the prime counting function pi(n) = the number - of prime numbers less than or equal to n. - - Examples - ======== - - >>> from sympy import primepi - >>> primepi(25) - 9 - - See Also - ======== - - sympy.ntheory.primetest.isprime : Test if n is prime - primerange : Generate all primes in a given range - prime : Return the nth prime - """ - n = int(n) - if n < 2: - return 0 - else: - n = int(n) - return sieve.search(n)[0] - -
    -
    [docs]def nextprime(n, ith=1): - """ Return the ith prime greater than n. - - i must be an integer. - - Notes - ===== - - Potential primes are located at 6*j +/- 1. This - property is used during searching. - - >>> from sympy import nextprime - >>> [(i, nextprime(i)) for i in range(10, 15)] - [(10, 11), (11, 13), (12, 13), (13, 17), (14, 17)] - >>> nextprime(2, ith=2) # the 2nd prime after 2 - 5 - - See Also - ======== - - prevprime : Return the largest prime smaller than n - primerange : Generate all primes in a given range - - """ - n = int(n) - i = as_int(ith) - if i > 1: - pr = n - j = 1 - while 1: - pr = nextprime(pr) - j += 1 - if j > i: - break - return pr - - if n < 2: - return 2 - if n < 7: - return {2: 3, 3: 5, 4: 5, 5: 7, 6: 7}[n] - nn = 6*(n//6) - if nn == n: - n += 1 - if isprime(n): - return n - n += 4 - elif n - nn == 5: - n += 2 - if isprime(n): - return n - n += 4 - else: - n = nn + 5 - while 1: - if isprime(n): - return n - n += 2 - if isprime(n): - return n - n += 4 - -
    -
    [docs]def prevprime(n): - """ Return the largest prime smaller than n. - - Notes - ===== - - Potential primes are located at 6*j +/- 1. This - property is used during searching. - - >>> from sympy import prevprime - >>> [(i, prevprime(i)) for i in range(10, 15)] - [(10, 7), (11, 7), (12, 11), (13, 11), (14, 13)] - - See Also - ======== - - nextprime : Return the ith prime greater than n - primerange : Generates all primes in a given range - """ - from sympy.functions.elementary.integers import ceiling - - # wrapping ceiling in int will raise an error if there was a problem - # determining whether the expression was exactly an integer or not - n = int(ceiling(n)) - if n < 3: - raise ValueError("no preceding primes") - if n < 8: - return {3: 2, 4: 3, 5: 3, 6: 5, 7: 5}[n] - nn = 6*(n//6) - if n - nn <= 1: - n = nn - 1 - if isprime(n): - return n - n -= 4 - else: - n = nn + 1 - while 1: - if isprime(n): - return n - n -= 2 - if isprime(n): - return n - n -= 4 - -
    -
    [docs]def primerange(a, b): - """ Generate a list of all prime numbers in the range [a, b). - - If the range exists in the default sieve, the values will - be returned from there; otherwise values will be returned - but will not modifiy the sieve. - - Notes - ===== - - Some famous conjectures about the occurence of primes in a given - range are [1]: - - - Twin primes: though often not, the following will give 2 primes - an infinite number of times: - primerange(6*n - 1, 6*n + 2) - - Legendre's: the following always yields at least one prime - primerange(n**2, (n+1)**2+1) - - Bertrand's (proven): there is always a prime in the range - primerange(n, 2*n) - - Brocard's: there are at least four primes in the range - primerange(prime(n)**2, prime(n+1)**2) - - The average gap between primes is log(n) [2]; the gap between - primes can be arbitrarily large since sequences of composite - numbers are arbitrarily large, e.g. the numbers in the sequence - n! + 2, n! + 3 ... n! + n are all composite. - - References - ========== - - 1. http://en.wikipedia.org/wiki/Prime_number - 2. http://primes.utm.edu/notes/gaps.html - - Examples - ======== - - >>> from sympy import primerange, sieve - >>> print([i for i in primerange(1, 30)]) - [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] - - The Sieve method, primerange, is generally faster but it will - occupy more memory as the sieve stores values. The default - instance of Sieve, named sieve, can be used: - - >>> list(sieve.primerange(1, 30)) - [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] - - See Also - ======== - - nextprime : Return the ith prime greater than n - prevprime : Return the largest prime smaller than n - randprime : Returns a random prime in a given range - primorial : Returns the product of primes based on condition - Sieve.primerange : return range from already computed primes - or extend the sieve to contain the requested - range. - """ - from sympy.functions.elementary.integers import ceiling - - # if we already have the range, return it - if b <= sieve._list[-1]: - for i in sieve.primerange(a, b): - yield i - return - # otherwise compute, without storing, the desired range - if a >= b: - return - # wrapping ceiling in int will raise an error if there was a problem - # determining whether the expression was exactly an integer or not - a = int(ceiling(a)) - 1 - b = int(ceiling(b)) - while 1: - a = nextprime(a) - if a < b: - yield a - else: - return - -
    -
    [docs]def randprime(a, b): - """ Return a random prime number in the range [a, b). - - Bertrand's postulate assures that - randprime(a, 2*a) will always succeed for a > 1. - - References - ========== - - - http://en.wikipedia.org/wiki/Bertrand's_postulate - - Examples - ======== - - >>> from sympy import randprime, isprime - >>> randprime(1, 30) #doctest: +SKIP - 13 - >>> isprime(randprime(1, 30)) - True - - See Also - ======== - - primerange : Generate all primes in a given range - - """ - if a >= b: - return - a, b = list(map(int, (a, b))) - n = random.randint(a - 1, b) - p = nextprime(n) - if p >= b: - p = prevprime(b) - if p < a: - raise ValueError("no primes exist in the specified range") - return p - -
    -
    [docs]def primorial(n, nth=True): - """ - Returns the product of the first n primes (default) or - the primes less than or equal to n (when ``nth=False``). - - >>> from sympy.ntheory.generate import primorial, randprime, primerange - >>> from sympy import factorint, Mul, primefactors, sqrt - >>> primorial(4) # the first 4 primes are 2, 3, 5, 7 - 210 - >>> primorial(4, nth=False) # primes <= 4 are 2 and 3 - 6 - >>> primorial(1) - 2 - >>> primorial(1, nth=False) - 1 - >>> primorial(sqrt(101), nth=False) - 210 - - One can argue that the primes are infinite since if you take - a set of primes and multiply them together (e.g. the primorial) and - then add or subtract 1, the result cannot be divided by any of the - original factors, hence either 1 or more new primes must divide this - product of primes. - - In this case, the number itself is a new prime: - - >>> factorint(primorial(4) + 1) - {211: 1} - - In this case two new primes are the factors: - - >>> factorint(primorial(4) - 1) - {11: 1, 19: 1} - - Here, some primes smaller and larger than the primes multiplied together - are obtained: - - >>> p = list(primerange(10, 20)) - >>> sorted(set(primefactors(Mul(*p) + 1)).difference(set(p))) - [2, 5, 31, 149] - - See Also - ======== - - primerange : Generate all primes in a given range - - """ - if nth: - n = as_int(n) - else: - n = int(n) - if n < 1: - raise ValueError("primorial argument must be >= 1") - p = 1 - if nth: - for i in range(1, n + 1): - p *= prime(i) - else: - for i in primerange(2, n + 1): - p *= i - return p - -
    -
    [docs]def cycle_length(f, x0, nmax=None, values=False): - """For a given iterated sequence, return a generator that gives - the length of the iterated cycle (lambda) and the length of terms - before the cycle begins (mu); if ``values`` is True then the - terms of the sequence will be returned instead. The sequence is - started with value ``x0``. - - Note: more than the first lambda + mu terms may be returned and this - is the cost of cycle detection with Brent's method; there are, however, - generally less terms calculated than would have been calculated if the - proper ending point were determined, e.g. by using Floyd's method. - - >>> from sympy.ntheory.generate import cycle_length - - This will yield successive values of i <-- func(i): - - >>> def iter(func, i): - ... while 1: - ... ii = func(i) - ... yield ii - ... i = ii - ... - - A function is defined: - - >>> func = lambda i: (i**2 + 1) % 51 - - and given a seed of 4 and the mu and lambda terms calculated: - - >>> next(cycle_length(func, 4)) - (6, 2) - - We can see what is meant by looking at the output: - - >>> n = cycle_length(func, 4, values=True) - >>> list(ni for ni in n) - [17, 35, 2, 5, 26, 14, 44, 50, 2, 5, 26, 14] - - There are 6 repeating values after the first 2. - - If a sequence is suspected of being longer than you might wish, ``nmax`` - can be used to exit early (and mu will be returned as None): - - >>> next(cycle_length(func, 4, nmax = 4)) - (4, None) - >>> [ni for ni in cycle_length(func, 4, nmax = 4, values=True)] - [17, 35, 2, 5] - - Code modified from: - http://en.wikipedia.org/wiki/Cycle_detection. - """ - - nmax = int(nmax or 0) - - # main phase: search successive powers of two - power = lam = 1 - tortoise, hare = x0, f(x0) # f(x0) is the element/node next to x0. - i = 0 - while tortoise != hare and (not nmax or i < nmax): - i += 1 - if power == lam: # time to start a new power of two? - tortoise = hare - power *= 2 - lam = 0 - if values: - yield hare - hare = f(hare) - lam += 1 - if nmax and i == nmax: - if values: - return - else: - yield nmax, None - return - if not values: - # Find the position of the first repetition of length lambda - mu = 0 - tortoise = hare = x0 - for i in range(lam): - hare = f(hare) - while tortoise != hare: - tortoise = f(tortoise) - hare = f(hare) - mu += 1 - if mu: - mu -= 1 - yield lam, mu
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/ntheory/modular.html b/dev-py3k/_modules/sympy/ntheory/modular.html deleted file mode 100644 index f0c83fbdffb..00000000000 --- a/dev-py3k/_modules/sympy/ntheory/modular.html +++ /dev/null @@ -1,370 +0,0 @@ - - - - - - - - - - sympy.ntheory.modular — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.ntheory.modular

    -from sympy.core.numbers import igcdex, igcd
    -from sympy.core.mul import prod
    -from sympy.core.compatibility import as_int
    -from sympy.ntheory.primetest import isprime
    -from sympy.polys.domains import ZZ
    -from sympy.polys.galoistools import gf_crt, gf_crt1, gf_crt2
    -from functools import reduce
    -
    -
    -
    [docs]def symmetric_residue(a, m): - """Return the residual mod m such that it is within half of the modulus. - - >>> from sympy.ntheory.modular import symmetric_residue - >>> symmetric_residue(1, 6) - 1 - >>> symmetric_residue(4, 6) - -2 - """ - if a <= m // 2: - return a - else: - return a - m - -
    -
    [docs]def crt(m, v, symmetric=False, check=True): - r"""Chinese Remainder Theorem. - - The moduli in m are assumed to be pairwise coprime. The output - is then an integer f, such that f = v_i mod m_i for each pair out - of v and m. If ``symmetric`` is False a positive integer will be - returned, else \|f\| will be less than or equal to the LCM of the - moduli, and thus f may be negative. - - If the moduli are not co-prime the correct result will be returned - if/when the test of the result is found to be incorrect. This result - will be None if there is no solution. - - The keyword ``check`` can be set to False if it is known that the moduli - are coprime. - - As an example consider a set of residues ``U = [49, 76, 65]`` - and a set of moduli ``M = [99, 97, 95]``. Then we have:: - - >>> from sympy.ntheory.modular import crt, solve_congruence - - >>> crt([99, 97, 95], [49, 76, 65]) - (639985, 912285) - - This is the correct result because:: - - >>> [639985 % m for m in [99, 97, 95]] - [49, 76, 65] - - If the moduli are not co-prime, you may receive an incorrect result - if you use ``check=False``: - - >>> crt([12, 6, 17], [3, 4, 2], check=False) - (954, 1224) - >>> [954 % m for m in [12, 6, 17]] - [6, 0, 2] - >>> crt([12, 6, 17], [3, 4, 2]) is None - True - >>> crt([3, 6], [2, 5]) - (5, 6) - - Note: the order of gf_crt's arguments is reversed relative to crt, - and that solve_congruence takes residue, modulus pairs. - - Programmer's note: rather than checking that all pairs of moduli share - no GCD (an O(n**2) test) and rather than factoring all moduli and seeing - that there is no factor in common, a check that the result gives the - indicated residuals is performed -- an O(n) operation. - - See Also - ======== - - solve_congruence - sympy.polys.galoistools.gf_crt : low level crt routine used by this routine - """ - if check: - m = list(map(as_int, m)) - v = list(map(as_int, v)) - - result = gf_crt(v, m, ZZ) - mm = prod(m) - - if check: - if not all(v % m == result % m for v, m in zip(v, m)): - result = solve_congruence(*list(zip(v, m)), - **dict(check=False, - symmetric=symmetric)) - if result is None: - return result - result, mm = result - - if symmetric: - return symmetric_residue(result, mm), mm - return result, mm - -
    -
    [docs]def crt1(m): - """First part of Chinese Remainder Theorem, for multiple application. - - Examples - ======== - - >>> from sympy.ntheory.modular import crt1 - >>> crt1([18, 42, 6]) - (4536, [252, 108, 756], [0, 2, 0]) - """ - - return gf_crt1(m, ZZ) - -
    -
    [docs]def crt2(m, v, mm, e, s, symmetric=False): - """Second part of Chinese Remainder Theorem, for multiple application. - - Examples - ======== - - >>> from sympy.ntheory.modular import crt1, crt2 - >>> mm, e, s = crt1([18, 42, 6]) - >>> crt2([18, 42, 6], [0, 0, 0], mm, e, s) - (0, 4536) - """ - - result = gf_crt2(v, m, mm, e, s, ZZ) - - if symmetric: - return symmetric_residue(result, mm), mm - return result, mm - -
    -
    [docs]def solve_congruence(*remainder_modulus_pairs, **hint): - """Compute the integer ``n`` that has the residual ``ai`` when it is - divided by ``mi`` where the ``ai`` and ``mi`` are given as pairs to - this function: ((a1, m1), (a2, m2), ...). If there is no solution, - return None. Otherwise return ``n`` and its modulus. - - The ``mi`` values need not be co-prime. If it is known that the moduli are - not co-prime then the hint ``check`` can be set to False (default=True) and - the check for a quicker solution via crt() (valid when the moduli are - co-prime) will be skipped. - - If the hint ``symmetric`` is True (default is False), the value of ``n`` - will be within 1/2 of the modulus, possibly negative. - - Examples - ======== - - >>> from sympy.ntheory.modular import solve_congruence - - What number is 2 mod 3, 3 mod 5 and 2 mod 7? - - >>> solve_congruence((2, 3), (3, 5), (2, 7)) - (23, 105) - >>> [23 % m for m in [3, 5, 7]] - [2, 3, 2] - - If you prefer to work with all remainder in one list and - all moduli in another, send the arguments like this: - - >>> solve_congruence(*list(zip((2, 3, 2), (3, 5, 7)))) - (23, 105) - - The moduli need not be co-prime; in this case there may or - may not be a solution: - - >>> solve_congruence((2, 3), (4, 6)) is None - True - - >>> solve_congruence((2, 3), (5, 6)) - (5, 6) - - The symmetric flag will make the result be within 1/2 of the modulus: - - >>> solve_congruence((2, 3), (5, 6), symmetric=True) - (-1, 6) - - See Also - ======== - - crt : high level routine implementing the Chinese Remainder Theorem - - """ - def combine(c1, c2): - """Return the tuple (a, m) which satisfies the requirement - that n = a + i*m satisfy n = a1 + j*m1 and n = a2 = k*m2. - - References - ========== - - - http://en.wikipedia.org/wiki/Method_of_successive_substitution - """ - a1, m1 = c1 - a2, m2 = c2 - a, b, c = m1, a2 - a1, m2 - g = reduce(igcd, [a, b, c]) - a, b, c = [i//g for i in [a, b, c]] - if a != 1: - inv_a, _, g = igcdex(a, c) - if g != 1: - return None - b *= inv_a - a, m = a1 + m1*b, m1*c - return a, m - - rm = remainder_modulus_pairs - symmetric = hint.get('symmetric', False) - - if hint.get('check', True): - rm = [(as_int(r), as_int(m)) for r, m in rm] - - # ignore redundant pairs but raise an error otherwise; also - # make sure that a unique set of bases is sent to gf_crt if - # they are all prime. - # - # The routine will work out less-trivial violations and - # return None, e.g. for the pairs (1,3) and (14,42) there - # is no answer because 14 mod 42 (having a gcd of 14) implies - # (14/2) mod (42/2), (14/7) mod (42/7) and (14/14) mod (42/14) - # which, being 0 mod 3, is inconsistent with 1 mod 3. But to - # preprocess the input beyond checking of another pair with 42 - # or 3 as the modulus (for this example) is not necessary. - uniq = {} - for r, m in rm: - r %= m - if m in uniq: - if r != uniq[m]: - return None - continue - uniq[m] = r - rm = [(r, m) for m, r in uniq.items()] - del uniq - - # if the moduli are co-prime, the crt will be significantly faster; - # checking all pairs for being co-prime gets to be slow but a prime - # test is a good trade-off - if all(isprime(m) for r, m in rm): - r, m = list(zip(*rm)) - return crt(m, r, symmetric=symmetric, check=False) - - rv = (0, 1) - for rmi in rm: - rv = combine(rv, rmi) - if rv is None: - break - n, m = rv - n = n % m - else: - if symmetric: - return symmetric_residue(n, m), m - return n, m
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/ntheory/multinomial.html b/dev-py3k/_modules/sympy/ntheory/multinomial.html deleted file mode 100644 index 0d8d52192e5..00000000000 --- a/dev-py3k/_modules/sympy/ntheory/multinomial.html +++ /dev/null @@ -1,355 +0,0 @@ - - - - - - - - - - sympy.ntheory.multinomial — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.ntheory.multinomial

    -from collections import defaultdict
    -
    -
    -def binomial_coefficients(n):
    -
    [docs] """Return a dictionary containing pairs :math:`{(k1,k2) : C_kn}` where - :math:`C_kn` are binomial coefficients and :math:`n=k1+k2`. - Examples - ======== - - >>> from sympy.ntheory import binomial_coefficients - >>> binomial_coefficients(9) - {(0, 9): 1, (1, 8): 9, (2, 7): 36, (3, 6): 84, - (4, 5): 126, (5, 4): 126, (6, 3): 84, (7, 2): 36, (8, 1): 9, (9, 0): 1} - - See Also - ======== - - binomial_coefficients_list, multinomial_coefficients - """ - d = {(0, n): 1, (n, 0): 1} - a = 1 - for k in range(1, n//2 + 1): - a = (a * (n - k + 1))//k - d[k, n - k] = d[n - k, k] = a - return d - - -def binomial_coefficients_list(n):
    -
    [docs] """ Return a list of binomial coefficients as rows of the Pascal's - triangle. - - Examples - ======== - - >>> from sympy.ntheory import binomial_coefficients_list - >>> binomial_coefficients_list(9) - [1, 9, 36, 84, 126, 126, 84, 36, 9, 1] - - See Also - ======== - - binomial_coefficients, multinomial_coefficients - """ - d = [1] * (n + 1) - a = 1 - for k in range(1, n//2 + 1): - a = (a * (n - k + 1))//k - d[k] = d[n - k] = a - return d - - -def multinomial_coefficients0(m, n, _tuple=tuple, _zip=zip):
    - """Return a dictionary containing pairs ``{(k1,k2,..,km) : C_kn}`` - where ``C_kn`` are multinomial coefficients such that - ``n=k1+k2+..+km``. - - For example: - - >>> from sympy import multinomial_coefficients - >>> multinomial_coefficients(2, 5) # indirect doctest - {(0, 5): 1, (1, 4): 5, (2, 3): 10, (3, 2): 10, (4, 1): 5, (5, 0): 1} - - The algorithm is based on the following result: - - Consider a polynomial and its ``n``-th exponent:: - - P(x) = sum_{i=0}^m p_i x^i - P(x)^n = sum_{k=0}^{m n} a(n,k) x^k - - The coefficients ``a(n,k)`` can be computed using the - J.C.P. Miller Pure Recurrence [see D.E.Knuth, Seminumerical - Algorithms, The art of Computer Programming v.2, Addison - Wesley, Reading, 1981;]:: - - a(n,k) = 1/(k p_0) sum_{i=1}^m p_i ((n+1)i-k) a(n,k-i), - - where ``a(n,0) = p_0^n``. - """ - - if not m: - if n: - return {} - else: - return {(): 1} - if m == 2: - return binomial_coefficients(n) - symbols = [(0,)*i + (1,) + (0,)*(m - i - 1) for i in range(m)] - s0 = symbols[0] - p0 = [_tuple(aa - bb for aa, bb in _zip(s, s0)) for s in symbols] - r = {_tuple(aa*n for aa in s0): 1} - l = [0] * (n*(m - 1) + 1) - l[0] = list(r.items()) - for k in range(1, n*(m - 1) + 1): - d = defaultdict(int) - for i in range(1, min(m, k + 1)): - nn = (n + 1)*i - k - if not nn: - continue - t = p0[i] - for t2, c2 in l[k - i]: - tt = _tuple([aa + bb for aa, bb in _zip(t2, t)]) - d[tt] += nn*c2 - if not d[tt]: - del d[tt] - r1 = [(t, c//k) for (t, c) in d.items()] - l[k] = r1 - r.update(r1) - return r - - -def multinomial_coefficients(m, n): -
    [docs] r"""Return a dictionary containing pairs ``{(k1,k2,..,km) : C_kn}`` - where ``C_kn`` are multinomial coefficients such that - ``n=k1+k2+..+km``. - - For example: - - >>> from sympy.ntheory import multinomial_coefficients - >>> multinomial_coefficients(2, 5) # indirect doctest - {(0, 5): 1, (1, 4): 5, (2, 3): 10, (3, 2): 10, (4, 1): 5, (5, 0): 1} - - The algorithm is based on the following result: - - .. math:: - \binom{n}{k_1, \ldots, k_m} = - \frac{k_1 + 1}{n - k_1} \sum_{i=2}^m \binom{n}{k_1 + 1, \ldots, k_i - 1, \ldots} - - Code contributed to Sage by Yann Laigle-Chapuy, copied with permission - of the author. - - See Also - ======== - - binomial_coefficients_list, binomial_coefficients - """ - if not m: - if n: - return {} - else: - return {(): 1} - if m == 2: - return binomial_coefficients(n) - if m >= 2*n and n > 1: - return dict(multinomial_coefficients_iterator(m, n)) - t = [n] + [0] * (m - 1) - r = {tuple(t): 1} - if n: - j = 0 # j will be the leftmost nonzero position - else: - j = m - # enumerate tuples in co-lex order - while j < m - 1: - # compute next tuple - tj = t[j] - if j: - t[j] = 0 - t[0] = tj - if tj > 1: - t[j + 1] += 1 - j = 0 - start = 1 - v = 0 - else: - j += 1 - start = j + 1 - v = r[tuple(t)] - t[j] += 1 - # compute the value - # NB: the initialization of v was done above - for k in range(start, m): - if t[k]: - t[k] -= 1 - v += r[tuple(t)] - t[k] += 1 - t[0] -= 1 - r[tuple(t)] = (v * tj) // (n - t[0]) - return r - - -def multinomial_coefficients_iterator(m, n, _tuple=tuple):
    -
    [docs] """multinomial coefficient iterator - - This routine has been optimized for `m` large with respect to `n` by taking - advantage of the fact that when the monomial tuples `t` are stripped of - zeros, their coefficient is the same as that of the monomial tuples from - ``multinomial_coefficients(n, n)``. Therefore, the latter coefficients are - precomputed to save memory and time. - - >>> from sympy.ntheory.multinomial import multinomial_coefficients - >>> m53, m33 = multinomial_coefficients(5,3), multinomial_coefficients(3,3) - >>> m53[(0,0,0,1,2)] == m53[(0,0,1,0,2)] == m53[(1,0,2,0,0)] == m33[(0,1,2)] - True - - Examples - ======== - - >>> from sympy.ntheory.multinomial import multinomial_coefficients_iterator - >>> it = multinomial_coefficients_iterator(20,3) - >>> next(it) - ((3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 1) - """ - if m < 2*n or n == 1: - mc = multinomial_coefficients(m, n) - for k, v in mc.items(): - yield(k, v) - else: - mc = multinomial_coefficients(n, n) - mc1 = {} - for k, v in mc.items(): - mc1[_tuple(filter(None, k))] = v - mc = mc1 - - t = [n] + [0] * (m - 1) - t1 = _tuple(t) - b = _tuple(filter(None, t1)) - yield (t1, mc[b]) - if n: - j = 0 # j will be the leftmost nonzero position - else: - j = m - # enumerate tuples in co-lex order - while j < m - 1: - # compute next tuple - tj = t[j] - if j: - t[j] = 0 - t[0] = tj - if tj > 1: - t[j + 1] += 1 - j = 0 - else: - j += 1 - t[j] += 1 - - t[0] -= 1 - t1 = _tuple(t) - b = _tuple(filter(None, t1)) - yield (t1, mc[b]) -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    - - - - - diff --git a/dev-py3k/_modules/sympy/ntheory/partitions_.html b/dev-py3k/_modules/sympy/ntheory/partitions_.html deleted file mode 100644 index 4eac0f53730..00000000000 --- a/dev-py3k/_modules/sympy/ntheory/partitions_.html +++ /dev/null @@ -1,212 +0,0 @@ - - - - - - - - - - sympy.ntheory.partitions_ — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.ntheory.partitions_

    -from sympy.mpmath.libmp import (fzero,
    -    from_man_exp, from_int, from_rational,
    -    fone, fhalf, bitcount, to_int, to_str, mpf_mul, mpf_div, mpf_sub,
    -    mpf_add, mpf_sqrt, mpf_pi, mpf_cosh_sinh, pi_fixed, mpf_cos)
    -from sympy.core.numbers import igcd
    -import math
    -
    -
    -def _a(n, j, prec):
    -    """Compute the inner sum in the HRR formula."""
    -    if j == 1:
    -        return fone
    -    s = fzero
    -    pi = pi_fixed(prec)
    -    for h in range(1, j):
    -        if igcd(h, j) != 1:
    -            continue
    -        # & with mask to compute fractional part of fixed-point number
    -        one = 1 << prec
    -        onemask = one - 1
    -        half = one >> 1
    -        g = 0
    -        if j >= 3:
    -            for k in range(1, j):
    -                t = h*k*one//j
    -                if t > 0:
    -                    frac = t & onemask
    -                else:
    -                    frac = -((-t) & onemask)
    -                g += k*(frac - half)
    -        g = ((g - 2*h*n*one)*pi//j) >> prec
    -        s = mpf_add(s, mpf_cos(from_man_exp(g, -prec), prec), prec)
    -    return s
    -
    -
    -def _d(n, j, prec, sq23pi, sqrt8):
    -    """
    -    Compute the sinh term in the outer sum of the HRR formula.
    -    The constants sqrt(2/3*pi) and sqrt(8) must be precomputed.
    -    """
    -    j = from_int(j)
    -    pi = mpf_pi(prec)
    -    a = mpf_div(sq23pi, j, prec)
    -    b = mpf_sub(from_int(n), from_rational(1, 24, prec), prec)
    -    c = mpf_sqrt(b, prec)
    -    ch, sh = mpf_cosh_sinh(mpf_mul(a, c), prec)
    -    D = mpf_div(mpf_sqrt(j, prec), mpf_mul(mpf_mul(sqrt8, b), pi), prec)
    -    E = mpf_sub(mpf_mul(a, ch), mpf_div(sh, c, prec), prec)
    -    return mpf_mul(D, E)
    -
    -
    -
    [docs]def npartitions(n, verbose=False): - """ - Calculate the partition function P(n), i.e. the number of ways that - n can be written as a sum of positive integers. - - P(n) is computed using the Hardy-Ramanujan-Rademacher formula, - described e.g. at http://mathworld.wolfram.com/PartitionFunctionP.html - - The correctness of this implementation has been tested for 10**n - up to n = 8. - - Examples - ======== - - >>> from sympy.ntheory import npartitions - >>> npartitions(25) - 1958 - """ - n = int(n) - if n < 0: - return 0 - if n <= 5: - return [1, 1, 2, 3, 5, 7][n] - # Estimate number of bits in p(n). This formula could be tidied - pbits = int((math.pi*(2*n/3.)**0.5 - math.log(4*n))/math.log(10) + 1) * \ - math.log(10, 2) - prec = p = int(pbits*1.1 + 100) - s = fzero - M = max(6, int(0.24*n**0.5 + 4)) - sq23pi = mpf_mul(mpf_sqrt(from_rational(2, 3, p), p), mpf_pi(p), p) - sqrt8 = mpf_sqrt(from_int(8), p) - for q in range(1, M): - a = _a(n, q, p) - d = _d(n, q, p, sq23pi, sqrt8) - s = mpf_add(s, mpf_mul(a, d), prec) - if verbose: - print("step", q, "of", M, to_str(a, 10), to_str(d, 10)) - # On average, the terms decrease rapidly in magnitude. Dynamically - # reducing the precision greatly improves performance. - p = bitcount(abs(to_int(d))) + 50 - np = to_int(mpf_add(s, fhalf, prec)) - return int(np) -
    -__all__ = ['npartitions'] -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/ntheory/primetest.html b/dev-py3k/_modules/sympy/ntheory/primetest.html deleted file mode 100644 index 28123191736..00000000000 --- a/dev-py3k/_modules/sympy/ntheory/primetest.html +++ /dev/null @@ -1,387 +0,0 @@ - - - - - - - - - - sympy.ntheory.primetest — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.ntheory.primetest

    -"""
    -Primality testing
    -
    -"""
    -
    -# prime list to use when number must be tested as a probable prime.
    -#>>> list(primerange(2, 200))
    -_isprime_fallback_primes = [
    -    2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47,
    -    53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107,
    -    109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167,
    -    173, 179, 181, 191, 193, 197, 199]
    -#>>> len(_)
    -#46
    -# pseudoprimes that will pass through last mr_safe test
    -_pseudos = set([
    -            669094855201,
    -           1052516956501, 2007193456621, 2744715551581, 9542968210729,
    -          17699592963781, 19671510288601,
    -          24983920772821, 24984938689453, 29661584268781, 37473222618541,
    -          46856248255981, 47922612926653, 48103703944453, 49110566041153,
    -          49752242681221, 91206655032481, 91481980096033, 119034193492321,
    -         123645258399601, 128928036060253, 137364148720147, 150753857310253,
    -         153131886327421, 155216912613121, 185610214763821, 224334357392701,
    -         227752294950181, 230058334559041, 304562854940401, 306001576998253,
    -         335788261073821, 377133492079081, 379242177424951, 389970770948461,
    -         397319638319521, 448114903362253, 523235160050221, 628999496281621,
    -         699349238838253, 746667678235753, 790198268451301, 794036495175661,
    -         823820871230281, 867739535711821, 1039918661294761, 1099127938585141,
    -        1104388025338153, 1173374598605653, 1262797719066157, 1265872947674653,
    -        1325898212229667, 1327034517143653, 1418575746675583, 1666122072463621,
    -        1837400535259453, 1857422490084961, 1870756820971741, 1914550540480717,
    -        2018963273468221, 2163829000939453, 2206020317369221, 2301037384029121,
    -        2416062055125421, 2435076500074921, 2545656135020833, 2594428516569781,
    -        2669983768115821, 2690937050990653, 2758640869506607, 2833525461416653,
    -        2876662942007221, 2932155806957821, 2957010595723801, 3183606449929153,
    -        3220133449185901, 3424103775720253, 3625360152399541, 3939300299037421,
    -        3947917710714841, 3980273496750253, 4182256679324041, 4450605887818261,
    -        4727893739521501, 4750350311306953, 4755334362931153, 5756440863559753,
    -        5760976603475341, 5794399356078761, 5954850603819253, 6125544931991761,
    -        6320931714094861, 6347593619672581, 6406268028524101, 6510632945054941,
    -        6620082224794741, 6627325072566061, 6844056606431101, 6989404981060153,
    -        7144293947609521, 7288348593229021, 7288539837129253, 7406102904971689,
    -        7430233301822341, 7576425305871193, 7601696719033861, 7803926845356487,
    -        7892007967006633, 7947797946559453, 8207000460596953, 8295064717807513,
    -        8337196000698841, 8352714234009421, 8389755717406381, 8509654470665701,
    -        8757647355282841, 8903933671696381, 8996133652295653, 9074421465661261,
    -        9157536631454221, 9188353522314541])
    -
    -
    -def _test(n, base):
    -    """Miller-Rabin strong pseudoprime test for one base.
    -    Return False if n is definitely composite, True if n is
    -    probably prime, with a probability greater than 3/4.
    -    """
    -    from sympy.ntheory.factor_ import trailing
    -
    -    n = int(n)
    -    if n < 2:
    -        return False
    -    # remove powers of 2 from n (= t * 2**s)
    -    s = trailing(n - 1)
    -    t = n >> s
    -    # do the Fermat test
    -    b = pow(base, t, n)
    -    if b == 1 or b == n - 1:
    -        return True
    -    else:
    -        for j in range(1, s):
    -            b = (b**2) % n
    -            if b == n - 1:
    -                return True
    -    return False
    -
    -
    -
    [docs]def mr(n, bases): - """Perform a Miller-Rabin strong pseudoprime test on n using a - given list of bases/witnesses. - - References - ========== - - - Richard Crandall & Carl Pomerance (2005), "Prime Numbers: - A Computational Perspective", Springer, 2nd edition, 135-138 - - A list of thresholds and the bases they require are here: - http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test#Deterministic_variants_of_the_test - - Examples - ======== - - >>> from sympy.ntheory.primetest import mr - >>> mr(1373651, [2, 3]) - False - >>> mr(479001599, [31, 73]) - True - """ - n = int(n) - for base in bases: - if not _test(n, base): - return False - return True - -
    -def _mr_safe(n): - """For n < 10**16, use the Miller-Rabin test to determine with - certainty (unless the code is buggy!) whether n is prime. - - Although the primes 2 through 17 are sufficient to confirm that a number - less than 341550071728322 (that is not prime 2 through 17) is prime, this - range is broken up into smaller ranges with earlier ranges requiring less - work. For example, for n < 1373653 only the bases 2 and 3 need be tested. - - What makes this a "safe" Miller-Rabin routine is that for n less than - the indicated limit, the given bases have been confirmed to detect all - composite numbers. What can potentially make this routine "unsafe" is - including ranges for which previous tests do not removes prime factors of - the bases being used. For example, this routine assumes that 2 and 3 have - already been removed as prime; but if the first test were the one for - n < 170584961 (that uses bases 350 and 3958281543) the routine would have - to ensure that the primes 5, 7, 29, 67, 679067 are already removed or else - they will be reported as being composite. For this reason it is helpful to - list the prime factors of the bases being tested as is done below. The - _mr_safe_helper can be used to generate this info-tag. - - References for the bounds: - ========================== - - 1. http://primes.utm.edu/prove/prove2_3.html - 2. http://www.trnicely.net/misc/mpzspsp.html - 3. http://en.wikipedia.org/wiki/Miller-Rabin_primality_test# - Accuracy_of_the_test - 4. http://zdu.spaces.live.com/?_c11_BlogPart_pagedir= - Next&_c11_BlogPart_handle=cns!C95152CB25EF2037! - 138&_c11_BlogPart_BlogPart=blogview&_c=BlogPart - 5. http://primes.utm.edu/glossary/xpage/Pseudoprime.html - 6. http://uucode.com/obf/dalbec/alg.html#sprp - """ - - if n < 1373653: - return mr(n, [2, 3]) - #[2, 3] stot = 1 clear == bases - # these two (and similar below) are commented out since they are - # more expensive in terms of stot than a later test. - #if n < 9080191: return mr(n, [31, 73]) # ref [3] - # [31, 73] stot = 4 clear == bases - #if n < 25326001: return mr(n, [2, 3, 5]) - # [2, 3, 5] stot = 3 clear == bases - if n < 170584961: - return mr(n, [350, 3958281543]) - # [350, 3958281543] stot = 1 clear [2, 3, 5, 7, 29, 67, 679067] - if n < 4759123141: - return mr(n, [2, 7, 61]) # ref [3] - # [2, 7, 61] stot = 3 clear == bases - if n < 75792980677: - return mr(n, [2, 379215, 457083754]) - # [2, 379215, 457083754] stot = 1 clear [2, 3, 5, 53, 228541877] - #if n < 118670087467: return n is not 3215031751 and mr(n, [2, 3, 5, 7]) # ref [3] - # [2, 3, 5, 7] stot = 4 clear == bases - if n < 1000000000000: - return mr(n, [2, 13, 23, 1662803]) - # [2, 13, 23, 1662803] stot = 4 clear == bases - #if n < 2152302898747: return mr(n, [2, 3, 5, 7, 11]) - # [2, 3, 5, 7, 11] stot = 5 clear == bases - #if n < 3474749660383: return mr(n, [2, 3, 5, 7, 11, 13]) - # [2, 3, 5, 7, 11, 13] stot = 7 clear == bases - #if n < 21652684502221: return mr(n, [2, 1215, 34862, 574237825]) - # [2, 1215, 34862, 574237825] stot = 8 clear [2, 3, 5, 7, 17431, 3281359] - #if n < 341550071728321: return mr(n, [2, 3, 5, 7, 11, 13, 17]) - # [2, 3, 5, 7, 11, 13, 17] stot = 11 clear == bases - if n < 10000000000000000: - return mr(n, [2, 3, 7, 61, 24251]) and n not in _pseudos - # [2, 3, 7, 61, 24251] stot = 5 clear == bases - raise ValueError("n too large") - - -
    [docs]def isprime(n): - """ - Test if n is a prime number (True) or not (False). For n < 10**16 the - answer is accurate; greater n values have a small probability of actually - being pseudoprimes. - - Negative primes (e.g. -2) are not considered prime. - - The function first looks for trivial factors, and if none is found, - performs a safe Miller-Rabin strong pseudoprime test with bases - that are known to prove a number prime. Finally, a general Miller-Rabin - test is done with the first k bases which, which will report a - pseudoprime as a prime with an error of about 4**-k. The current value - of k is 46 so the error is about 2 x 10**-28. - - Examples - ======== - - >>> from sympy.ntheory import isprime - >>> isprime(13) - True - >>> isprime(15) - False - - See Also - ======== - - sympy.ntheory.generate.primerange : Generates all primes in a given range - sympy.ntheory.generate.primepi : Return the number of primes less than or equal to n - sympy.ntheory.generate.prime : Return the nth prime - """ - n = int(n) - if n < 2: - return False - if n & 1 == 0: - return n == 2 - if n <= 23001: - return pow(2, n, n) == 2 and n not in [341, 561, 645, 1105, 1387, 1729, - 1905, 2047, 2465, 2701, 2821, - 3277, 4033, 4369, 4371, 4681, - 5461, 6601, 7957, 8321, 8481, - 8911, 10261, 10585, 11305, - 12801, 13741, 13747, 13981, - 14491, 15709, 15841, 16705, - 18705, 18721, 19951, 23001] - try: - return _mr_safe(n) - except ValueError: - return mr(n, _isprime_fallback_primes) - -
    -def _mr_safe_helper(_s): - """ - Analyze a (new) mr_safe line for for total number of s's to - be tested in _test along with the primes that must be cleared - by a previous test. - - e.g. - >>> from sympy.ntheory.primetest import _mr_safe_helper - >>> print(_mr_safe_helper("if n < 170584961: return mr(n, [350, 3958281543])")) - # [350, 3958281543] stot = 1 clear [2, 3, 5, 7, 29, 67, 679067] - >>> print(_mr_safe_helper('return mr(n, [2, 379215, 457083754])')) - # [2, 379215, 457083754] stot = 1 clear [2, 3, 5, 53, 228541877] - """ - - def _info(bases): - """ - Analyze the list of bases, reporting the number of 'j-loops' that - will be required if this list is passed to _test (stot) and the primes - that must be cleared by a previous test. - - This info tag should then be appended to any new mr_safe line - that is added so someone can easily see whether that line satisfies - the requirements of mr_safe (see docstring there for details). - """ - from sympy.ntheory.factor_ import factorint, trailing - - factors = [] - tot = 0 - for b in bases: - tot += trailing(b - 1) - f = factorint(b) - factors.extend(f) - factors = sorted(set(factors)) - bases = sorted(set(bases)) - if bases == factors: - factors = '== bases' - else: - factors = str(factors) - return ' # %s stot = %s clear %s' % tuple( - [str(x).replace('L', '') for x in (list(bases), tot, factors)]) - - _r = [int(_x) for _x in _s.split('[')[1].split(']')[0].split(',')] - return _info(_r) -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/ntheory/residue_ntheory.html b/dev-py3k/_modules/sympy/ntheory/residue_ntheory.html deleted file mode 100644 index d9761a11202..00000000000 --- a/dev-py3k/_modules/sympy/ntheory/residue_ntheory.html +++ /dev/null @@ -1,334 +0,0 @@ - - - - - - - - - - sympy.ntheory.residue_ntheory — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.ntheory.residue_ntheory

    -from sympy.core.numbers import igcd
    -from sympy.core.compatibility import as_int
    -from .primetest import isprime
    -from .factor_ import factorint, trailing, totient
    -
    -
    -
    [docs]def n_order(a, n): - """Returns the order of ``a`` modulo ``n``. - - The order of ``a`` modulo ``n`` is the smallest integer - ``k`` such that ``a**k`` leaves a remainder of 1 with ``n``. - - Examples - ======== - - >>> from sympy.ntheory import n_order - >>> n_order(3, 7) - 6 - >>> n_order(4, 7) - 3 - """ - a, n = as_int(a), as_int(n) - if igcd(a, n) != 1: - raise ValueError("The two numbers should be relatively prime") - group_order = totient(n) - factors = factorint(group_order) - order = 1 - if a > n: - a = a % n - for p, e in factors.items(): - exponent = group_order - for f in range(0, e + 1): - if (a ** (exponent)) % n != 1: - order *= p ** (e - f + 1) - break - exponent = exponent // p - return order - -
    -
    [docs]def is_primitive_root(a, p): - """ - Returns True if ``a`` is a primitive root of ``p`` - - ``a`` is said to be the primitive root of ``p`` if gcd(a, p) == 1 and - totient(p) is the smallest positive number s.t. - - a**totient(p) cong 1 mod(p) - - Examples - ======== - - >>> from sympy.ntheory import is_primitive_root, n_order, totient - >>> is_primitive_root(3, 10) - True - >>> is_primitive_root(9, 10) - False - >>> n_order(3, 10) == totient(10) - True - >>> n_order(9, 10) == totient(10) - False - - """ - a, p = as_int(a), as_int(p) - if igcd(a, p) != 1: - raise ValueError("The two numbers should be relatively prime") - if a > p: - a = a % p - if n_order(a, p) == totient(p): - return True - else: - return False - -
    -
    [docs]def is_quad_residue(a, p): - """ - Returns True if ``a`` (mod ``p``) is in the set of squares mod ``p``, - i.e a % p in set([i**2 % p for i in range(p)]). If ``p`` is an odd - prime, an iterative method is used to make the determination: - - >>> from sympy.ntheory import is_quad_residue - >>> list(set([i**2 % 7 for i in range(7)])) - [0, 1, 2, 4] - >>> [j for j in range(7) if is_quad_residue(j, 7)] - [0, 1, 2, 4] - - See Also - ======== - - legendre_symbol, jacobi_symbol - """ - a, p = as_int(a), as_int(p) - if p < 1: - raise ValueError('p must be > 0') - if a >= p or a < 0: - a = a % p - if a < 2 or p < 3: - return True - if not isprime(p): - if p % 2 and jacobi_symbol(a, p) == -1: - return False - for i in range(2, p//2 + 1): - if i**2 % p == a: - return True - return False - - def square_and_multiply(a, n, p): - if n == 1: - return a - elif n % 2 == 1: - return ((square_and_multiply(a, n // 2, p) ** 2) * a) % p - else: - return (square_and_multiply(a, n // 2, p) ** 2) % p - - return (square_and_multiply(a, (p - 1) // 2, p) % p) == 1 - -
    -
    [docs]def legendre_symbol(a, p): - """ - Returns - ======= - - 1. 0 if a is multiple of p - 2. 1 if a is a quadratic residue of p - 3. -1 otherwise - - p should be an odd prime by definition - - Examples - ======== - - >>> from sympy.ntheory import legendre_symbol - >>> [legendre_symbol(i, 7) for i in range(7)] - [0, 1, 1, -1, 1, -1, -1] - >>> list(set([i**2 % 7 for i in range(7)])) - [0, 1, 2, 4] - - See Also - ======== - - is_quad_residue, jacobi_symbol - - """ - a, p = as_int(a), as_int(p) - if not isprime(p) or p == 2: - raise ValueError("p should be an odd prime") - _, a = divmod(a, p) - if not a: - return 0 - if is_quad_residue(a, p): - return 1 - else: - return -1 - -
    -
    [docs]def jacobi_symbol(m, n): - """ - Returns the product of the legendre_symbol(m, p) - for all the prime factors, p, of n. - - Returns - ======= - - 1. 0 if m cong 0 mod(n) - 2. 1 if x**2 cong m mod(n) has a solution - 3. -1 otherwise - - Examples - ======== - - >>> from sympy.ntheory import jacobi_symbol, legendre_symbol - >>> from sympy import Mul, S - >>> jacobi_symbol(45, 77) - -1 - >>> jacobi_symbol(60, 121) - 1 - - The relationship between the jacobi_symbol and legendre_symbol can - be demonstrated as follows: - - >>> L = legendre_symbol - >>> S(45).factors() - {3: 2, 5: 1} - >>> jacobi_symbol(7, 45) == L(7, 3)**2 * L(7, 5)**1 - True - - See Also - ======== - - is_quad_residue, legendre_symbol - """ - m, n = as_int(m), as_int(n) - if not n % 2: - raise ValueError("n should be an odd integer") - if m < 0 or m > n: - m = m % n - if not m: - return int(n == 1) - if n == 1 or m == 1: - return 1 - if igcd(m, n) != 1: - return 0 - - j = 1 - s = trailing(m) - m = m >> s - if s % 2 and n % 8 in [3, 5]: - j *= -1 - - while m != 1: - if m % 4 == 3 and n % 4 == 3: - j *= -1 - m, n = n % m, m - s = trailing(m) - m = m >> s - if s % 2 and n % 8 in [3, 5]: - j *= -1 - return j
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/parsing/mathematica.html b/dev-py3k/_modules/sympy/parsing/mathematica.html deleted file mode 100644 index 67296793aee..00000000000 --- a/dev-py3k/_modules/sympy/parsing/mathematica.html +++ /dev/null @@ -1,172 +0,0 @@ - - - - - - - - - - sympy.parsing.mathematica — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.parsing.mathematica

    -from re import match
    -from sympy import sympify
    -
    -
    -
    [docs]def mathematica(s): - return sympify(parse(s)) - -
    -def parse(s): - s = s.strip() - - #Begin rules - rules = ( - (r"\A(\w+)\[([^\]]+[^\[]*)\]\Z", # Function call - lambda m: translateFunction( - m.group(1)) + "(" + parse(m.group(2)) + ")" ), - - (r"\((.+)\)\((.+)\)", # Parenthesized implied multiplication - lambda m: "(" + parse(m.group(1)) + ")*(" + parse(m.group(2)) + ")" ), - - (r"\A\((.+)\)\Z", # Parenthesized expression - lambda m: "(" + parse(m.group(1)) + ")" ), - - (r"\A(.*[\w\.])\((.+)\)\Z", # Implied multiplication - a(b) - lambda m: parse(m.group(1)) + "*(" + parse(m.group(2)) + ")" ), - - (r"\A\((.+)\)([\w\.].*)\Z", # Implied multiplication - (a)b - lambda m: "(" + parse(m.group(1)) + ")*" + parse(m.group(2)) ), - - (r"\A([\d\.]+)([a-zA-Z].*)\Z", # Implied multiplicatin - 2a - lambda m: parse(m.group(1)) + "*" + parse(m.group(2)) ), - - (r"\A([^=]+)([\^\-\*/\+=]=?)(.+)\Z", # Infix operator - lambda m: parse(m.group(1)) + translateOperator(m.group(2)) + parse(m.group(3)) )) - #End rules - - for rule, action in rules: - m = match(rule, s) - if m: - return action(m) - - return s - - -def translateFunction(s): - if s.startswith("Arc"): - return "a" + s[3:] - return s.lower() - - -def translateOperator(s): - dictionary = {'^': '**'} - if s in dictionary: - return dictionary[s] - return s -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/parsing/maxima.html b/dev-py3k/_modules/sympy/parsing/maxima.html deleted file mode 100644 index 253b3934233..00000000000 --- a/dev-py3k/_modules/sympy/parsing/maxima.html +++ /dev/null @@ -1,185 +0,0 @@ - - - - - - - - - - sympy.parsing.maxima — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.parsing.maxima

    -import re
    -from sympy import sympify, Sum, product, sin, cos
    -
    -
    -class MaximaHelpers:
    -    def maxima_expand(expr):
    -        return expr.expand()
    -
    -    def maxima_float(expr):
    -        return expr.evalf()
    -
    -    def maxima_trigexpand(expr):
    -        return expr.expand(trig=True)
    -
    -    def maxima_sum(a1, a2, a3, a4):
    -        return Sum(a1, (a2, a3, a4)).doit()
    -
    -    def maxima_product(a1, a2, a3, a4):
    -        return product(a1, (a2, a3, a4))
    -
    -    def maxima_csc(expr):
    -        return 1/sin(expr)
    -
    -    def maxima_sec(expr):
    -        return 1/cos(expr)
    -
    -sub_dict = {
    -    'pi': re.compile('%pi'),
    -    'E': re.compile('%e'),
    -    'I': re.compile('%i'),
    -    '**': re.compile('\^'),
    -    'oo': re.compile(r'\binf\b'),
    -    '-oo': re.compile(r'\bminf\b'),
    -    "'-'": re.compile(r'\bminus\b'),
    -    'maxima_expand': re.compile(r'\bexpand\b'),
    -    'maxima_float': re.compile(r'\bfloat\b'),
    -    'maxima_trigexpand': re.compile(r'\btrigexpand'),
    -    'maxima_sum': re.compile(r'\bsum\b'),
    -    'maxima_product': re.compile(r'\bproduct\b'),
    -    'cancel': re.compile(r'\bratsimp\b'),
    -    'maxima_csc': re.compile(r'\bcsc\b'),
    -    'maxima_sec': re.compile(r'\bsec\b')
    -}
    -
    -var_name = re.compile('^\s*(\w+)\s*:')
    -
    -
    -
    [docs]def parse_maxima(str, globals=None, name_dict={}): - str = str.strip() - str = str.rstrip('; ') - - for k, v in list(sub_dict.items()): - str = v.sub(k, str) - - assign_var = None - var_match = var_name.search(str) - if var_match: - assign_var = var_match.group(1) - str = str[var_match.end():].strip() - - dct = MaximaHelpers.__dict__.copy() - dct.update(name_dict) - obj = sympify(str, locals=dct) - - if assign_var and globals: - globals[assign_var] = obj - - return obj
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/parsing/sympy_parser.html b/dev-py3k/_modules/sympy/parsing/sympy_parser.html deleted file mode 100644 index a167d27286a..00000000000 --- a/dev-py3k/_modules/sympy/parsing/sympy_parser.html +++ /dev/null @@ -1,675 +0,0 @@ - - - - - - - - - - sympy.parsing.sympy_parser — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.parsing.sympy_parser

    -"""Transform a string with Python-like source code into SymPy expression. """
    -
    -from .sympy_tokenize import \
    -    generate_tokens, untokenize, TokenError, \
    -    NUMBER, STRING, NAME, OP, ENDMARKER
    -
    -from keyword import iskeyword
    -from io import StringIO
    -import re
    -import unicodedata
    -
    -from sympy.core.basic import Basic, C
    -import collections
    -
    -_re_repeated = re.compile(r"^(\d*)\.(\d*)\[(\d+)\]$")
    -UNSPLITTABLE_TOKEN_NAMES = ['_kern']
    -
    -
    -def _token_splittable(token):
    -    """
    -    Predicate for whether a token name can be split into multiple tokens.
    -
    -    A token is splittable if it does not contain an underscore charater and
    -    it is not the name of a Greek letter. This is used to implicitly convert
    -    expressions like 'xyz' into 'x*y*z'.
    -    """
    -    if '_' in token:
    -        return False
    -    elif token in UNSPLITTABLE_TOKEN_NAMES:
    -        return False
    -    else:
    -        try:
    -            return not unicodedata.lookup('GREEK SMALL LETTER ' + token)
    -        except KeyError:
    -            pass
    -    return True
    -
    -
    -def _add_factorial_tokens(name, result):
    -    if result == [] or result[-1][1] == '(':
    -        raise TokenError()
    -
    -    beginning = [(NAME, name), (OP, '(')]
    -    end = [(OP, ')')]
    -
    -    diff = 0
    -    length = len(result)
    -
    -    for index, token in enumerate(result[::-1]):
    -        toknum, tokval = token
    -        i = length - index - 1
    -
    -        if tokval == ')':
    -            diff += 1
    -        elif tokval == '(':
    -            diff -= 1
    -
    -        if diff == 0:
    -            if i - 1 >= 0 and result[i - 1][0] == NAME:
    -                return result[:i - 1] + beginning + result[i - 1:] + end
    -            else:
    -                return result[:i] + beginning + result[i:] + end
    -
    -    return result
    -
    -
    -class AppliedFunction(object):
    -    """
    -    A group of tokens representing a function and its arguments.
    -
    -    `exponent` is for handling the shorthand sin^2, ln^2, etc.
    -    """
    -    def __init__(self, function, args, exponent=None):
    -        if exponent is None:
    -            exponent = []
    -        self.function = function
    -        self.args = args
    -        self.exponent = exponent
    -        self.items = ['function', 'args', 'exponent']
    -
    -    def expand(self):
    -        """Return a list of tokens representing the function"""
    -        result = []
    -        result.append(self.function)
    -        result.extend(self.args)
    -        return result
    -
    -    def __getitem__(self, index):
    -        return getattr(self, self.items[index])
    -
    -    def __repr__(self):
    -        return "AppliedFunction(%s, %s, %s)" % (self.function, self.args,
    -                                                self.exponent)
    -
    -
    -class ParenthesisGroup(list):
    -    """List of tokens representing an expression in parentheses."""
    -    pass
    -
    -
    -def _flatten(result):
    -    result2 = []
    -    for tok in result:
    -        if isinstance(tok, AppliedFunction):
    -            result2.extend(tok.expand())
    -        else:
    -            result2.append(tok)
    -    return result2
    -
    -
    -def _group_parentheses(tokens, local_dict, global_dict):
    -    """Group tokens between parentheses with ParenthesisGroup.
    -
    -    Also processes those tokens recursively.
    -
    -    """
    -    result = []
    -    stacks = []
    -    stacklevel = 0
    -    for token in tokens:
    -        if token[0] == OP:
    -            if token[1] == '(':
    -                stacks.append(ParenthesisGroup([]))
    -                stacklevel += 1
    -            elif token[1] == ')':
    -                stacks[-1].append(token)
    -                stack = stacks.pop()
    -
    -                if len(stacks) > 0:
    -                    # We don't recurse here since the upper-level stack
    -                    # would reprocess these tokens
    -                    stacks[-1].extend(stack)
    -                else:
    -                    # Recurse here to handle nested parentheses
    -                    # Strip off the outer parentheses to avoid an infinite loop
    -                    inner = stack[1:-1]
    -                    inner = implicit_multiplication_application(inner,
    -                                                                local_dict,
    -                                                                global_dict)
    -                    parenGroup = [stack[0]] + inner + [stack[-1]]
    -                    result.append(ParenthesisGroup(parenGroup))
    -                stacklevel -= 1
    -                continue
    -        if stacklevel:
    -            stacks[-1].append(token)
    -        else:
    -            result.append(token)
    -    return result
    -
    -
    -def _apply_functions(tokens, local_dict, global_dict):
    -    """Convert a NAME token + ParenthesisGroup into an AppliedFunction.
    -
    -    Note that ParenthesisGroups, if not applied to any function, are
    -    converted back into lists of tokens.
    -
    -    """
    -    result = []
    -    symbol = None
    -    for tok in tokens:
    -        if tok[0] == NAME:
    -            symbol = tok
    -            result.append(tok)
    -        elif isinstance(tok, ParenthesisGroup):
    -            if symbol:
    -                result[-1] = AppliedFunction(symbol, tok)
    -                symbol = None
    -            else:
    -                result.extend(tok)
    -        else:
    -            symbol = None
    -            result.append(tok)
    -    return result
    -
    -
    -def _split_symbols(tokens, local_dict, global_dict):
    -    result = []
    -    for tok in tokens:
    -        if isinstance(tok, AppliedFunction):
    -            if tok.function[1] == 'Symbol' and len(tok.args) == 3:
    -                # Middle token, get value, strip quotes
    -                symbol = tok.args[1][1][1:-1]
    -                if _token_splittable(symbol):
    -                    for char in symbol:
    -                        result.append(AppliedFunction(
    -                            tok.function,
    -                            [(OP, '('), (NAME, str(repr(char))), (OP, ')')]
    -                        ))
    -                    continue
    -        result.append(tok)
    -    return result
    -
    -
    -def _implicit_multiplication(tokens, local_dict, global_dict):
    -    """Implicitly adds '*' tokens.
    -
    -    Cases:
    -
    -    - Two AppliedFunctions next to each other ("sin(x)cos(x)")
    -
    -    - AppliedFunction next to an open parenthesis ("sin x (cos x + 1)")
    -
    -    - A close parenthesis next to an AppliedFunction ("(x+2)sin x")\
    -
    -    - A closeparenthesis next to an open parenthesis ("(x+2)(x+3)")
    -
    -    - An AppliedFunction next to an implicitly applied function ("sin(x)cos
    -      x")
    -
    -    """
    -    result = []
    -    for tok, nextTok in zip(tokens, tokens[1:]):
    -        result.append(tok)
    -        if (isinstance(tok, AppliedFunction) and
    -                isinstance(nextTok, AppliedFunction)):
    -            result.append((OP, '*'))
    -        elif (isinstance(tok, AppliedFunction) and
    -              nextTok[0] == OP and nextTok[1] == '('):
    -            # Applied function followed by an open parenthesis
    -            result.append((OP, '*'))
    -        elif (tok[0] == OP and tok[1] == ')' and
    -              isinstance(nextTok, AppliedFunction)):
    -            # Close parenthesis followed by an applied function
    -            result.append((OP, '*'))
    -        elif (tok[0] == OP and tok[1] == ')' and
    -              nextTok[0] == NAME):
    -            # Close parenthesis followed by an implicitly applied function
    -            result.append((OP, '*'))
    -        elif (tok[0] == nextTok[0] == OP
    -              and tok[1] == ')' and nextTok[1] == '('):
    -            # Close parenthesis followed by an open parenthesis
    -            result.append((OP, '*'))
    -        elif (isinstance(tok, AppliedFunction) and nextTok[0] == NAME):
    -            # Applied function followed by implicitly applied function
    -            result.append((OP, '*'))
    -    result.append(tokens[-1])
    -    return result
    -
    -
    -def _implicit_application(tokens, local_dict, global_dict):
    -    """Adds parentheses as needed after functions.
    -
    -    Also processes functions raised to powers."""
    -    result = []
    -    appendParen = 0  # number of closing parentheses to add
    -    skip = False  # number of tokens to delay before adding a ')' (to
    -                  # capture **, ^, etc.)
    -    exponent = None
    -    for tok, nextTok in zip(tokens, tokens[1:]):
    -        result.append(tok)
    -        if (tok[0] == NAME and
    -            nextTok[0] != OP and
    -                nextTok[0] != ENDMARKER):
    -            func = global_dict.get(tok[1])
    -            is_Function = getattr(func, 'is_Function', False)
    -            if (is_Function or
    -                (isinstance(func, collections.Callable) and not hasattr(func, 'is_Function')) or
    -                    isinstance(nextTok, AppliedFunction)):
    -                result.append((OP, '('))
    -                appendParen += 1
    -        elif isinstance(tok, AppliedFunction) and not tok.args:
    -            # This occurs when we had a function raised to a power - it has
    -            # no arguments
    -            result[-1] = tok.function
    -            exponent = tok.exponent
    -            if nextTok[0] != OP and nextTok[1] != '(':
    -                result.append((OP, '('))
    -                appendParen += 1
    -        elif appendParen:
    -            if nextTok[0] == OP and nextTok[1] in ('^', '**'):
    -                skip = 1
    -                continue
    -            if skip:
    -                skip -= 1
    -                continue
    -            result.append((OP, ')'))
    -            appendParen -= 1
    -            if exponent and not appendParen:
    -                result.extend(exponent)
    -                exponent = None
    -
    -    result.append(tokens[-1])
    -
    -    if appendParen:
    -        result.extend([(OP, ')')] * appendParen)
    -    if exponent:
    -        result.extend(exponent)
    -
    -    return result
    -
    -
    -def _function_exponents(tokens, local_dict, global_dict):
    -    """Preprocess functions raised to powers."""
    -    result = []
    -    need_exponent = False
    -    for tok, nextTok in zip(tokens, tokens[1:]):
    -        result.append(tok)
    -        if (tok[0] == NAME and nextTok[0] == OP
    -                and nextTok[1] in ('**', '^')):
    -            if getattr(global_dict.get(tok[1]), 'is_Function', False):
    -                result[-1] = AppliedFunction(tok, [])
    -                need_exponent = True
    -        elif need_exponent:
    -            del result[-1]
    -            result[-1].exponent.append(tok)
    -            if isinstance(tok, AppliedFunction):
    -                need_exponent = False
    -    result.append(tokens[-1])
    -    return result
    -
    -
    -def implicit_multiplication_application(result, local_dict, global_dict):
    -    """Allows a slightly relaxed syntax.
    -
    -    - Parentheses for single-argument method calls are optional.
    -
    -    - Multiplication is implicit.
    -
    -    - Symbol names can be split (i.e. spaces are not needed between
    -      symbols).
    -
    -    - Functions can be exponentiated.
    -
    -    Example:
    -
    -    >>> from sympy.parsing.sympy_parser import (parse_expr,
    -    ... standard_transformations, implicit_multiplication_application)
    -    >>> parse_expr("10sin**2 x**2 + 3xyz + tan theta",
    -    ... transformations=standard_transformations +
    -    ... (implicit_multiplication_application,))
    -    3*x*y*z + 10*sin(x**2)**2 + tan(theta)
    -
    -    """
    -    # These are interdependent steps, so we don't expose them separately
    -    for step in (_group_parentheses,
    -                 _apply_functions,
    -                 _split_symbols,
    -                 _function_exponents,
    -                 _implicit_application,
    -                 _implicit_multiplication):
    -        result = step(result, local_dict, global_dict)
    -
    -    result = _flatten(result)
    -    return result
    -
    -
    -def auto_symbol(tokens, local_dict, global_dict):
    -    """Inserts calls to ``Symbol`` for undefined variables."""
    -    result = []
    -    for toknum, tokval in tokens:
    -        if toknum == NAME:
    -            name = tokval
    -
    -            if (name in ['True', 'False', 'None']
    -                or iskeyword(name)
    -                    or name in local_dict):
    -                result.append((NAME, name))
    -                continue
    -            elif name in global_dict:
    -                obj = global_dict[name]
    -
    -                if isinstance(obj, (Basic, type)) or isinstance(obj, collections.Callable):
    -                    result.append((NAME, name))
    -                    continue
    -
    -            result.extend([
    -                (NAME, 'Symbol'),
    -                (OP, '('),
    -                (NAME, repr(str(name))),
    -                (OP, ')'),
    -            ])
    -        else:
    -            result.append((toknum, tokval))
    -
    -    return result
    -
    -
    -def factorial_notation(tokens, local_dict, global_dict):
    -    """Allows standard notation for factorial."""
    -    result = []
    -    prevtoken = ''
    -    for toknum, tokval in tokens:
    -        if toknum == OP:
    -            op = tokval
    -
    -            if op == '!!':
    -                if prevtoken == '!' or prevtoken == '!!':
    -                    raise TokenError
    -                result = _add_factorial_tokens('factorial2', result)
    -            elif op == '!':
    -                if prevtoken == '!' or prevtoken == '!!':
    -                    raise TokenError
    -                result = _add_factorial_tokens('factorial', result)
    -            else:
    -                result.append((OP, op))
    -        else:
    -            result.append((toknum, tokval))
    -
    -        prevtoken = tokval
    -
    -    return result
    -
    -
    -def convert_xor(tokens, local_dict, global_dict):
    -    """Treats XOR, '^', as exponentiation, '**'."""
    -    result = []
    -    for toknum, tokval in tokens:
    -        if toknum == OP:
    -            if tokval == '^':
    -                result.append((OP, '**'))
    -            else:
    -                result.append((toknum, tokval))
    -        else:
    -            result.append((toknum, tokval))
    -
    -    return result
    -
    -
    -def auto_number(tokens, local_dict, global_dict):
    -    """Converts numeric literals to use SymPy equivalents.
    -
    -    Complex numbers use ``I``; integer literals use ``Integer``, float
    -    literals use ``Float``, and repeating decimals use ``Rational``.
    -
    -    """
    -    result = []
    -    prevtoken = ''
    -
    -    for toknum, tokval in tokens:
    -        if toknum == NUMBER:
    -            number = tokval
    -            postfix = []
    -
    -            if number.endswith('j') or number.endswith('J'):
    -                number = number[:-1]
    -                postfix = [(OP, '*'), (NAME, 'I')]
    -
    -            if '.' in number or (('e' in number or 'E' in number) and
    -                    not (number.startswith('0x') or number.startswith('0X'))):
    -                match = _re_repeated.match(number)
    -
    -                if match is not None:
    -                    # Clear repeating decimals, e.g. 3.4[31] -> (3 + 4/10 + 31/990)
    -                    pre, post, repetend = match.groups()
    -
    -                    zeros = '0'*len(post)
    -                    post, repetends = [w.lstrip('0') for w in [post, repetend]]
    -                                                # or else interpreted as octal
    -
    -                    a = pre or '0'
    -                    b, c = post or '0', '1' + zeros
    -                    d, e = repetends, ('9'*len(repetend)) + zeros
    -
    -                    seq = [
    -                        (OP, '('),
    -                        (NAME,
    -                         'Integer'), (OP, '('), (NUMBER, a), (OP, ')'),
    -                        (OP, '+'),
    -                        (NAME, 'Rational'), (OP, '('), (
    -                            NUMBER, b), (OP, ','), (NUMBER, c), (OP, ')'),
    -                        (OP, '+'),
    -                        (NAME, 'Rational'), (OP, '('), (
    -                            NUMBER, d), (OP, ','), (NUMBER, e), (OP, ')'),
    -                        (OP, ')'),
    -                    ]
    -                else:
    -                    seq = [(NAME, 'Float'), (OP, '('),
    -                           (NUMBER, repr(str(number))), (OP, ')')]
    -            else:
    -                seq = [(NAME, 'Integer'), (OP, '('), (
    -                    NUMBER, number), (OP, ')')]
    -
    -            result.extend(seq + postfix)
    -        else:
    -            result.append((toknum, tokval))
    -
    -    return result
    -
    -
    -def rationalize(tokens, local_dict, global_dict):
    -    """Converts floats into ``Rational``s. Run AFTER ``auto_number``."""
    -    result = []
    -    passed_float = False
    -    for toknum, tokval in tokens:
    -        if toknum == NAME:
    -            if tokval == 'Float':
    -                passed_float = True
    -                tokval = 'Rational'
    -            result.append((toknum, tokval))
    -        elif passed_float == True and toknum == NUMBER:
    -            passed_float = False
    -            result.append((STRING, tokval))
    -        else:
    -            result.append((toknum, tokval))
    -
    -    return result
    -
    -standard_transformations = (auto_symbol, auto_number, factorial_notation)
    -
    -
    -
    [docs]def parse_expr(s, local_dict=None, transformations=standard_transformations): - """ - Converts the string ``s`` to a SymPy expression, in ``local_dict`` - - Examples - ======== - - >>> from sympy.parsing.sympy_parser import parse_expr - - >>> parse_expr("1/2") - 1/2 - >>> type(_) - <class 'sympy.core.numbers.Half'> - - """ - - if local_dict is None: - local_dict = {} - - global_dict = {} - exec('from sympy import *', global_dict) - - # keep autosimplification from joining Integer or - # minus sign into a Mul; this modification doesn't - # prevent the 2-arg Mul from becoming an Add, however. - hit = False - if '(' in s: - kern = '_kern' - while kern in s: - kern += "_" - s = re.sub(r'(\d *\*|-) *\(', r'\1%s*(' % kern, s) - hit = kern in s - - tokens = [] - input_code = StringIO(s.strip()) - for toknum, tokval, _, _, _ in generate_tokens(input_code.readline): - tokens.append((toknum, tokval)) - - for transform in transformations: - tokens = transform(tokens, local_dict, global_dict) - - code = untokenize(tokens) - expr = eval( - code, global_dict, local_dict) # take local objects in preference - - if not hit: - return expr - rep = {C.Symbol(kern): 1} - - def _clear(expr): - if hasattr(expr, 'xreplace'): - return expr.xreplace(rep) - elif isinstance(expr, (list, tuple, set)): - return type(expr)([_clear(e) for e in expr]) - if hasattr(expr, 'subs'): - return expr.subs(rep) - return expr - return _clear(expr)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/parsing/sympy_tokenize.html b/dev-py3k/_modules/sympy/parsing/sympy_tokenize.html deleted file mode 100644 index ce9141d08e7..00000000000 --- a/dev-py3k/_modules/sympy/parsing/sympy_tokenize.html +++ /dev/null @@ -1,561 +0,0 @@ - - - - - - - - - - sympy.parsing.sympy_tokenize — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.parsing.sympy_tokenize

    -"""Tokenization help for Python programs.
    -
    -generate_tokens(readline) is a generator that breaks a stream of
    -text into Python tokens.  It accepts a readline-like method which is called
    -repeatedly to get the next line of input (or "" for EOF).  It generates
    -5-tuples with these members:
    -
    -    the token type (see token.py)
    -    the token (a string)
    -    the starting (row, column) indices of the token (a 2-tuple of ints)
    -    the ending (row, column) indices of the token (a 2-tuple of ints)
    -    the original line (string)
    -
    -It is designed to match the working of the Python tokenizer exactly, except
    -that it produces COMMENT tokens for comments and gives type OP for all
    -operators
    -
    -Older entry points
    -    tokenize_loop(readline, tokeneater)
    -    tokenize(readline, tokeneater=printtoken)
    -are the same, except instead of generating tokens, tokeneater is a callback
    -function to which the 5 fields described above are passed as 5 arguments,
    -each time a new token is found."""
    -
    -__author__ = 'Ka-Ping Yee <ping@lfw.org>'
    -__credits__ = \
    -    'GvR, ESR, Tim Peters, Thomas Wouters, Fred Drake, Skip Montanaro, Raymond Hettinger'
    -
    -import string
    -import re
    -from token import *
    -
    -import token
    -__all__ = [x for x in dir(token) if x[0] != '_'] + ["COMMENT", "tokenize",
    -           "generate_tokens", "NL", "untokenize"]
    -del token
    -
    -COMMENT = N_TOKENS
    -tok_name[COMMENT] = 'COMMENT'
    -NL = N_TOKENS + 1
    -tok_name[NL] = 'NL'
    -N_TOKENS += 2
    -
    -
    -
    [docs]def group(*choices): - return '(' + '|'.join(choices) + ')' - -
    -
    [docs]def any(*choices): - return group(*choices) + '*' - -
    -
    [docs]def maybe(*choices): - return group(*choices) + '?' -
    -Whitespace = r'[ \f\t]*' -Comment = r'#[^\r\n]*' -Ignore = Whitespace + any(r'\\\r?\n' + Whitespace) + maybe(Comment) -Name = r'[a-zA-Z_]\w*' - -Hexnumber = r'0[xX][\da-fA-F]+[lL]?' -Octnumber = r'(0[oO][0-7]+)|(0[0-7]*)[lL]?' -Binnumber = r'0[bB][01]+[lL]?' -Decnumber = r'[1-9]\d*[lL]?' -Intnumber = group(Hexnumber, Binnumber, Octnumber, Decnumber) -Exponent = r'[eE][-+]?\d+' -Pointfloat = group(r'\d+\.\d*', r'\.\d+') + maybe(Exponent) -Repeatedfloat = r'\d*\.\d*\[\d+\]' -Expfloat = r'\d+' + Exponent -Floatnumber = group(Repeatedfloat, Pointfloat, Expfloat) -Imagnumber = group(r'\d+[jJ]', Floatnumber + r'[jJ]') -Number = group(Imagnumber, Floatnumber, Intnumber) - -# Tail end of ' string. -Single = r"[^'\\]*(?:\\.[^'\\]*)*'" -# Tail end of " string. -Double = r'[^"\\]*(?:\\.[^"\\]*)*"' -# Tail end of ''' string. -Single3 = r"[^'\\]*(?:(?:\\.|'(?!''))[^'\\]*)*'''" -# Tail end of """ string. -Double3 = r'[^"\\]*(?:(?:\\.|"(?!""))[^"\\]*)*"""' -Triple = group("[uU]?[rR]?'''", '[uU]?[rR]?"""') -# Single-line ' or " string. -String = group(r"[uU]?[rR]?'[^\n'\\]*(?:\\.[^\n'\\]*)*'", - r'[uU]?[rR]?"[^\n"\\]*(?:\\.[^\n"\\]*)*"') - -# Because of leftmost-then-longest match semantics, be sure to put the -# longest operators first (e.g., if = came before ==, == would get -# recognized as two instances of =). -Operator = group(r"\*\*=?", r">>=?", r"<<=?", r"<>", r"!=", - r"//=?", - r"[+\-*/%&|^=<>]=?", - r"~") - -Bracket = '[][(){}]' -Special = group(r'\r?\n', r'[:;.,`@]', r'\!\!', r'\!') -Funny = group(Operator, Bracket, Special) - -PlainToken = group(Number, Funny, String, Name) -Token = Ignore + PlainToken - -# First (or only) line of ' or " string. -ContStr = group(r"[uU]?[rR]?'[^\n'\\]*(?:\\.[^\n'\\]*)*" + - group("'", r'\\\r?\n'), - r'[uU]?[rR]?"[^\n"\\]*(?:\\.[^\n"\\]*)*' + - group('"', r'\\\r?\n')) -PseudoExtras = group(r'\\\r?\n', Comment, Triple) -PseudoToken = Whitespace + group(PseudoExtras, Number, Funny, ContStr, Name) - -tokenprog, pseudoprog, single3prog, double3prog = list(map( - re.compile, (Token, PseudoToken, Single3, Double3))) -endprogs = {"'": re.compile(Single), '"': re.compile(Double), - "'''": single3prog, '"""': double3prog, - "r'''": single3prog, 'r"""': double3prog, - "u'''": single3prog, 'u"""': double3prog, - "ur'''": single3prog, 'ur"""': double3prog, - "R'''": single3prog, 'R"""': double3prog, - "U'''": single3prog, 'U"""': double3prog, - "uR'''": single3prog, 'uR"""': double3prog, - "Ur'''": single3prog, 'Ur"""': double3prog, - "UR'''": single3prog, 'UR"""': double3prog, - "b'''": single3prog, 'b"""': double3prog, - "br'''": single3prog, 'br"""': double3prog, - "B'''": single3prog, 'B"""': double3prog, - "bR'''": single3prog, 'bR"""': double3prog, - "Br'''": single3prog, 'Br"""': double3prog, - "BR'''": single3prog, 'BR"""': double3prog, - 'r': None, 'R': None, 'u': None, 'U': None, - 'b': None, 'B': None} - -triple_quoted = {} -for t in ("'''", '"""', - "r'''", 'r"""', "R'''", 'R"""', - "u'''", 'u"""', "U'''", 'U"""', - "ur'''", 'ur"""', "Ur'''", 'Ur"""', - "uR'''", 'uR"""', "UR'''", 'UR"""', - "b'''", 'b"""', "B'''", 'B"""', - "br'''", 'br"""', "Br'''", 'Br"""', - "bR'''", 'bR"""', "BR'''", 'BR"""'): - triple_quoted[t] = t -single_quoted = {} -for t in ("'", '"', - "r'", 'r"', "R'", 'R"', - "u'", 'u"', "U'", 'U"', - "ur'", 'ur"', "Ur'", 'Ur"', - "uR'", 'uR"', "UR'", 'UR"', - "b'", 'b"', "B'", 'B"', - "br'", 'br"', "Br'", 'Br"', - "bR'", 'bR"', "BR'", 'BR"' ): - single_quoted[t] = t - -tabsize = 8 - - -
    [docs]class TokenError(Exception): - pass - -
    -
    [docs]class StopTokenizing(Exception): - pass - -
    -
    [docs]def printtoken(type, token, srow_scol, erow_ecol, line): # for testing - srow, scol = srow_scol - erow, ecol = erow_ecol - print("%d,%d-%d,%d:\t%s\t%s" % \ - (srow, scol, erow, ecol, tok_name[type], repr(token))) - -
    -
    [docs]def tokenize(readline, tokeneater=printtoken): - """ - The tokenize() function accepts two parameters: one representing the - input stream, and one providing an output mechanism for tokenize(). - - The first parameter, readline, must be a callable object which provides - the same interface as the readline() method of built-in file objects. - Each call to the function should return one line of input as a string. - - The second parameter, tokeneater, must also be a callable object. It is - called once for each token, with five arguments, corresponding to the - tuples generated by generate_tokens(). - """ - try: - tokenize_loop(readline, tokeneater) - except StopTokenizing: - pass - -# backwards compatible interface - -
    -def tokenize_loop(readline, tokeneater): - for token_info in generate_tokens(readline): - tokeneater(*token_info) - - -class Untokenizer: - - def __init__(self): - self.tokens = [] - self.prev_row = 1 - self.prev_col = 0 - - def add_whitespace(self, start): - row, col = start - assert row <= self.prev_row - col_offset = col - self.prev_col - if col_offset: - self.tokens.append(" " * col_offset) - - def untokenize(self, iterable): - for t in iterable: - if len(t) == 2: - self.compat(t, iterable) - break - tok_type, token, start, end, line = t - self.add_whitespace(start) - self.tokens.append(token) - self.prev_row, self.prev_col = end - if tok_type in (NEWLINE, NL): - self.prev_row += 1 - self.prev_col = 0 - return "".join(self.tokens) - - def compat(self, token, iterable): - startline = False - indents = [] - toks_append = self.tokens.append - toknum, tokval = token - if toknum in (NAME, NUMBER): - tokval += ' ' - if toknum in (NEWLINE, NL): - startline = True - prevstring = False - for tok in iterable: - toknum, tokval = tok[:2] - - if toknum in (NAME, NUMBER): - tokval += ' ' - - # Insert a space between two consecutive strings - if toknum == STRING: - if prevstring: - tokval = ' ' + tokval - prevstring = True - else: - prevstring = False - - if toknum == INDENT: - indents.append(tokval) - continue - elif toknum == DEDENT: - indents.pop() - continue - elif toknum in (NEWLINE, NL): - startline = True - elif startline and indents: - toks_append(indents[-1]) - startline = False - toks_append(tokval) - - -
    [docs]def untokenize(iterable): - """Transform tokens back into Python source code. - - Each element returned by the iterable must be a token sequence - with at least two elements, a token number and token value. If - only two tokens are passed, the resulting output is poor. - - Round-trip invariant for full input: - Untokenized source will match input source exactly - - Round-trip invariant for limited intput:: - - # Output text will tokenize the back to the input - t1 = [tok[:2] for tok in generate_tokens(f.readline)] - newcode = untokenize(t1) - readline = iter(newcode.splitlines(1)).next - t2 = [tok[:2] for tok in generate_tokens(readline)] - assert t1 == t2 - """ - ut = Untokenizer() - return ut.untokenize(iterable) - -
    -
    [docs]def generate_tokens(readline): - """ - The generate_tokens() generator requires one argment, readline, which - must be a callable object which provides the same interface as the - readline() method of built-in file objects. Each call to the function - should return one line of input as a string. Alternately, readline - can be a callable function terminating with StopIteration:: - - readline = open(myfile).next # Example of alternate readline - - The generator produces 5-tuples with these members: the token type; the - token string; a 2-tuple (srow, scol) of ints specifying the row and - column where the token begins in the source; a 2-tuple (erow, ecol) of - ints specifying the row and column where the token ends in the source; - and the line on which the token was found. The line passed is the - logical line; continuation lines are included. - """ - lnum = parenlev = continued = 0 - namechars, numchars = string.ascii_letters + '_', '0123456789' - contstr, needcont = '', 0 - contline = None - indents = [0] - - while 1: # loop over lines in stream - try: - line = readline() - except StopIteration: - line = '' - lnum = lnum + 1 - pos, max = 0, len(line) - - if contstr: # continued string - if not line: - raise TokenError("EOF in multi-line string", strstart) - endmatch = endprog.match(line) - if endmatch: - pos = end = endmatch.end(0) - yield (STRING, contstr + line[:end], - strstart, (lnum, end), contline + line) - contstr, needcont = '', 0 - contline = None - elif needcont and line[-2:] != '\\\n' and line[-3:] != '\\\r\n': - yield (ERRORTOKEN, contstr + line, - strstart, (lnum, len(line)), contline) - contstr = '' - contline = None - continue - else: - contstr = contstr + line - contline = contline + line - continue - - elif parenlev == 0 and not continued: # new statement - if not line: - break - column = 0 - while pos < max: # measure leading whitespace - if line[pos] == ' ': - column = column + 1 - elif line[pos] == '\t': - column = (column/tabsize + 1)*tabsize - elif line[pos] == '\f': - column = 0 - else: - break - pos = pos + 1 - if pos == max: - break - - if line[pos] in '#\r\n': # skip comments or blank lines - if line[pos] == '#': - comment_token = line[pos:].rstrip('\r\n') - nl_pos = pos + len(comment_token) - yield (COMMENT, comment_token, - (lnum, pos), (lnum, pos + len(comment_token)), line) - yield (NL, line[nl_pos:], - (lnum, nl_pos), (lnum, len(line)), line) - else: - yield ((NL, COMMENT)[line[pos] == '#'], line[pos:], - (lnum, pos), (lnum, len(line)), line) - continue - - if column > indents[-1]: # count indents or dedents - indents.append(column) - yield (INDENT, line[:pos], (lnum, 0), (lnum, pos), line) - while column < indents[-1]: - if column not in indents: - raise IndentationError( - "unindent does not match any outer indentation level", - ("<tokenize>", lnum, pos, line)) - indents = indents[:-1] - yield (DEDENT, '', (lnum, pos), (lnum, pos), line) - - else: # continued statement - if not line: - raise TokenError("EOF in multi-line statement", (lnum, 0)) - continued = 0 - - while pos < max: - pseudomatch = pseudoprog.match(line, pos) - if pseudomatch: # scan for tokens - start, end = pseudomatch.span(1) - spos, epos, pos = (lnum, start), (lnum, end), end - token, initial = line[start:end], line[start] - - if initial in numchars or \ - (initial == '.' and token != '.'): # ordinary number - yield (NUMBER, token, spos, epos, line) - elif initial in '\r\n': - yield (NL if parenlev > 0 else NEWLINE, token, spos, epos, line) - elif initial == '#': - assert not token.endswith("\n") - yield (COMMENT, token, spos, epos, line) - elif token in triple_quoted: - endprog = endprogs[token] - endmatch = endprog.match(line, pos) - if endmatch: # all on one line - pos = endmatch.end(0) - token = line[start:pos] - yield (STRING, token, spos, (lnum, pos), line) - else: - contstr = line[start:] - contline = line - break - elif initial in single_quoted or \ - token[:2] in single_quoted or \ - token[:3] in single_quoted: - if token[-1] == '\n': # continued string - endprog = (endprogs[initial] or endprogs[token[1]] or - endprogs[token[2]]) - contstr, needcont = line[start:], 1 - contline = line - break - else: # ordinary string - yield (STRING, token, spos, epos, line) - elif initial in namechars: # ordinary name - yield (NAME, token, spos, epos, line) - elif initial == '\\': # continued stmt - continued = 1 - else: - if initial in '([{': - parenlev = parenlev + 1 - elif initial in ')]}': - parenlev = parenlev - 1 - yield (OP, token, spos, epos, line) - else: - yield (ERRORTOKEN, line[pos], - (lnum, pos), (lnum, pos + 1), line) - pos = pos + 1 - - for indent in indents[1:]: # pop remaining indent levels - yield (DEDENT, '', (lnum, 0), (lnum, 0), '') - yield (ENDMARKER, '', (lnum, 0), (lnum, 0), '') -
    -if __name__ == '__main__': # testing - import sys - if len(sys.argv) > 1: - tokenize(open(sys.argv[1]).readline) - else: - tokenize(sys.stdin.readline) -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/gaussopt.html b/dev-py3k/_modules/sympy/physics/gaussopt.html deleted file mode 100644 index 4dd40e096a9..00000000000 --- a/dev-py3k/_modules/sympy/physics/gaussopt.html +++ /dev/null @@ -1,958 +0,0 @@ - - - - - - - - - - sympy.physics.gaussopt — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.gaussopt

    -# -*- encoding: utf-8 -*-
    -"""
    -Gaussian optics.
    -
    -The module implements:
    -
    -- Ray transfer matrices for geometrical and gaussian optics.
    -
    -  See RayTransferMatrix, GeometricRay and BeamParameter
    -
    -- Conjugation relations for geometrical and gaussian optics.
    -
    -  See geometric_conj*, gauss_conj and conjugate_gauss_beams
    -
    -The conventions for the distances are as follows:
    -
    -focal distance
    -    positive for convergent lenses
    -object distance
    -    positive for real objects
    -image distance
    -    positive for real images
    -"""
    -
    -from sympy import (atan2, Expr, I, im, Matrix, oo, pi, re, sqrt, sympify,
    -    together)
    -from sympy.utilities.misc import filldedent
    -
    -###
    -# A, B, C, D matrices
    -###
    -
    -
    -
    [docs]class RayTransferMatrix(Matrix): - """ - Base class for a Ray Transfer Matrix. - - It should be used if there isn't already a more specific subclass mentioned - in See Also. - - Parameters - ========== - - parameters : A, B, C and D or 2x2 matrix (Matrix(2, 2, [A, B, C, D])) - - Examples - ======== - - >>> from sympy.physics.gaussopt import RayTransferMatrix, ThinLens - >>> from sympy import Symbol, Matrix - - >>> mat = RayTransferMatrix(1, 2, 3, 4) - >>> mat - [1, 2] - [3, 4] - - >>> RayTransferMatrix(Matrix([[1, 2], [3, 4]])) - [1, 2] - [3, 4] - - >>> mat.A - 1 - - >>> f = Symbol('f') - >>> lens = ThinLens(f) - >>> lens - [ 1, 0] - [-1/f, 1] - - >>> lens.C - -1/f - - See Also - ======== - - GeometricRay, BeamParameter, - FreeSpace, FlatRefraction, CurvedRefraction, - FlatMirror, CurvedMirror, ThinLens - - References - ========== - - [1] http://en.wikipedia.org/wiki/Ray_transfer_matrix_analysis - """ - - def __new__(cls, *args): - - if len(args) == 4: - temp = ((args[0], args[1]), (args[2], args[3])) - elif len(args) == 1 \ - and isinstance(args[0], Matrix) \ - and args[0].shape == (2, 2): - temp = args[0] - else: - raise ValueError(filldedent(''' - Expecting 2x2 Matrix or the 4 elements of - the Matrix but got %s''' % str(args))) - return Matrix.__new__(cls, temp) - - def __mul__(self, other): - if isinstance(other, RayTransferMatrix): - return RayTransferMatrix(Matrix.__mul__(self, other)) - elif isinstance(other, GeometricRay): - return GeometricRay(Matrix.__mul__(self, other)) - elif isinstance(other, BeamParameter): - temp = self*Matrix(((other.q,), (1,))) - q = (temp[0]/temp[1]).expand(complex=True) - return BeamParameter(other.wavelen, - together(re(q)), - z_r=together(im(q))) - else: - return Matrix.__mul__(self, other) - - @property -
    [docs] def A(self): - """ - The A parameter of the Matrix. - - Examples - ======== - - >>> from sympy.physics.gaussopt import RayTransferMatrix - >>> mat = RayTransferMatrix(1, 2, 3, 4) - >>> mat.A - 1 - """ - return self[0, 0] -
    - @property -
    [docs] def B(self): - """ - The B parameter of the Matrix. - - Examples - ======== - - >>> from sympy.physics.gaussopt import RayTransferMatrix - >>> mat = RayTransferMatrix(1, 2, 3, 4) - >>> mat.B - 2 - """ - return self[0, 1] -
    - @property -
    [docs] def C(self): - """ - The C parameter of the Matrix. - - Examples - ======== - - >>> from sympy.physics.gaussopt import RayTransferMatrix - >>> mat = RayTransferMatrix(1, 2, 3, 4) - >>> mat.C - 3 - """ - return self[1, 0] -
    - @property -
    [docs] def D(self): - """ - The D parameter of the Matrix. - - Examples - ======== - - >>> from sympy.physics.gaussopt import RayTransferMatrix - >>> mat = RayTransferMatrix(1, 2, 3, 4) - >>> mat.D - 4 - """ - return self[1, 1] - -
    -
    [docs]class FreeSpace(RayTransferMatrix): - """ - Ray Transfer Matrix for free space. - - Parameters - ========== - - distance - - See Also - ======== - - RayTransferMatrix - - Examples - ======== - - >>> from sympy.physics.gaussopt import FreeSpace - >>> from sympy import symbols - >>> d = symbols('d') - >>> FreeSpace(d) - [1, d] - [0, 1] - """ - def __new__(cls, d): - return RayTransferMatrix.__new__(cls, 1, d, 0, 1) - -
    -
    [docs]class FlatRefraction(RayTransferMatrix): - """ - Ray Transfer Matrix for refraction. - - Parameters - ========== - - n1 : refractive index of one medium - n2 : refractive index of other medium - - See Also - ======== - - RayTransferMatrix - - Examples - ======== - - >>> from sympy.physics.gaussopt import FlatRefraction - >>> from sympy import symbols - >>> n1, n2 = symbols('n1 n2') - >>> FlatRefraction(n1, n2) - [1, 0] - [0, n1/n2] - """ - def __new__(cls, n1, n2): - n1, n2 = list(map(sympify, (n1, n2))) - return RayTransferMatrix.__new__(cls, 1, 0, 0, n1/n2) - -
    -
    [docs]class CurvedRefraction(RayTransferMatrix): - """ - Ray Transfer Matrix for refraction on curved interface. - - Parameters - ========== - - R : radius of curvature (positive for concave) - n1 : refractive index of one medium - n2 : refractive index of other medium - - See Also - ======== - - RayTransferMatrix - - Examples - ======== - - >>> from sympy.physics.gaussopt import CurvedRefraction - >>> from sympy import symbols - >>> R, n1, n2 = symbols('R n1 n2') - >>> CurvedRefraction(R, n1, n2) - [ 1, 0] - [(n1 - n2)/(R*n2), n1/n2] - """ - def __new__(cls, R, n1, n2): - R, n1, n2 = list(map(sympify, (R, n1, n2))) - return RayTransferMatrix.__new__(cls, 1, 0, (n1 - n2)/R/n2, n1/n2) - -
    -
    [docs]class FlatMirror(RayTransferMatrix): - """ - Ray Transfer Matrix for reflection. - - See Also - ======== - - RayTransferMatrix - - Examples - ======== - - >>> from sympy.physics.gaussopt import FlatMirror - >>> FlatMirror() - [1, 0] - [0, 1] - """ - def __new__(cls): - return RayTransferMatrix.__new__(cls, 1, 0, 0, 1) - -
    -
    [docs]class CurvedMirror(RayTransferMatrix): - """ - Ray Transfer Matrix for reflection from curved surface. - - Parameters - ========== - - R : radius of curvature (positive for concave) - - See Also - ======== - - RayTransferMatrix - - Examples - ======== - - >>> from sympy.physics.gaussopt import CurvedMirror - >>> from sympy import symbols - >>> R = symbols('R') - >>> CurvedMirror(R) - [ 1, 0] - [-2/R, 1] - """ - def __new__(cls, R): - R = sympify(R) - return RayTransferMatrix.__new__(cls, 1, 0, -2/R, 1) - -
    -
    [docs]class ThinLens(RayTransferMatrix): - """ - Ray Transfer Matrix for a thin lens. - - Parameters - ========== - - f : the focal distance - - See Also - ======== - - RayTransferMatrix - - Examples - ======== - - >>> from sympy.physics.gaussopt import ThinLens - >>> from sympy import symbols - >>> f = symbols('f') - >>> ThinLens(f) - [ 1, 0] - [-1/f, 1] - """ - def __new__(cls, f): - f = sympify(f) - return RayTransferMatrix.__new__(cls, 1, 0, -1/f, 1) - - -### -# Representation for geometric ray -### -
    -
    [docs]class GeometricRay(Matrix): - """ - Representation for a geometric ray in the Ray Transfer Matrix formalism. - - Parameters - ========== - - h : height, and - angle : angle, or - matrix : a 2x1 matrix (Matrix(2, 1, [height, angle])) - - Examples - ======= - - >>> from sympy.physics.gaussopt import GeometricRay, FreeSpace - >>> from sympy import symbols, Matrix - >>> d, h, angle = symbols('d, h, angle') - - >>> GeometricRay(h, angle) - [ h] - [angle] - - >>> FreeSpace(d)*GeometricRay(h, angle) - [angle*d + h] - [ angle] - - >>> GeometricRay( Matrix( ((h,), (angle,)) ) ) - [ h] - [angle] - - See Also - ======== - - RayTransferMatrix - - """ - - def __new__(cls, *args): - if len(args) == 1 and isinstance(args[0], Matrix) \ - and args[0].shape == (2, 1): - temp = args[0] - elif len(args) == 2: - temp = ((args[0],), (args[1],)) - else: - raise ValueError(filldedent(''' - Expecting 2x1 Matrix or the 2 elements of - the Matrix but got %s''' % str(args))) - return Matrix.__new__(cls, temp) - - @property -
    [docs] def height(self): - """ - The distance from the optical axis. - - Examples - ======== - - >>> from sympy.physics.gaussopt import GeometricRay - >>> from sympy import symbols - >>> h, angle = symbols('h, angle') - >>> gRay = GeometricRay(h, angle) - >>> gRay.height - h - """ - return self[0] -
    - @property -
    [docs] def angle(self): - """ - The angle with the optical axis. - - Examples - ======== - - >>> from sympy.physics.gaussopt import GeometricRay - >>> from sympy import symbols - >>> h, angle = symbols('h, angle') - >>> gRay = GeometricRay(h, angle) - >>> gRay.angle - angle - """ - return self[1] - - -### -# Representation for gauss beam -### -
    -
    [docs]class BeamParameter(Expr): - """ - Representation for a gaussian ray in the Ray Transfer Matrix formalism. - - Parameters - ========== - - wavelen : the wavelength, - z : the distance to waist, and - w : the waist, or - z_r : the rayleigh range - - Examples - ======== - - >>> from sympy.physics.gaussopt import BeamParameter - >>> p = BeamParameter(530e-9, 1, w=1e-3) - >>> p.q - 1 + 1.88679245283019*I*pi - - >>> p.q.n() - 1.0 + 5.92753330865999*I - >>> p.w_0.n() - 0.00100000000000000 - >>> p.z_r.n() - 5.92753330865999 - - >>> from sympy.physics.gaussopt import FreeSpace - >>> fs = FreeSpace(10) - >>> p1 = fs*p - >>> p.w.n() - 0.00101413072159615 - >>> p1.w.n() - 0.00210803120913829 - - See Also - ======== - - RayTransferMatrix - - References - ========== - - [1] http://en.wikipedia.org/wiki/Complex_beam_parameter - """ - #TODO A class Complex may be implemented. The BeamParameter may - # subclass it. See: - # https://groups.google.com/d/topic/sympy/7XkU07NRBEs/discussion - - __slots__ = ['z', 'z_r', 'wavelen'] - - def __new__(cls, wavelen, z, **kwargs): - wavelen, z = list(map(sympify, (wavelen, z))) - inst = Expr.__new__(cls, wavelen, z) - inst.wavelen = wavelen - inst.z = z - if len(kwargs) != 1: - raise ValueError('Constructor expects exactly one named argument.') - elif 'z_r' in kwargs: - inst.z_r = sympify(kwargs['z_r']) - elif 'w' in kwargs: - inst.z_r = waist2rayleigh(sympify(kwargs['w']), wavelen) - else: - raise ValueError('The constructor needs named argument w or z_r') - return inst - - @property -
    [docs] def q(self): - """ - The complex parameter representing the beam. - - Examples - ======== - - >>> from sympy.physics.gaussopt import BeamParameter - >>> p = BeamParameter(530e-9, 1, w=1e-3) - >>> p.q - 1 + 1.88679245283019*I*pi - """ - return self.z + I*self.z_r -
    - @property -
    [docs] def radius(self): - """ - The radius of curvature of the phase front. - - Examples - ======== - - >>> from sympy.physics.gaussopt import BeamParameter - >>> p = BeamParameter(530e-9, 1, w=1e-3) - >>> p.radius - 0.2809/pi**2 + 1 - """ - return self.z*(1 + (self.z/self.z_r)**2) -
    - @property -
    [docs] def w(self): - """ - The beam radius at `1/e^2` intensity. - - See Also - ======== - - w_0 : the minimal radius of beam - - Examples - ======== - - >>> from sympy.physics.gaussopt import BeamParameter - >>> p = BeamParameter(530e-9, 1, w=1e-3) - >>> p.w - 0.001*sqrt(0.2809/pi**2 + 1) - """ - return self.w_0*sqrt(1 + (self.z/self.z_r)**2) -
    - @property -
    [docs] def w_0(self): - """ - The beam waist (minimal radius). - - See Also - ======== - - w : the beam radius at `1/e^2` intensity - - Examples - ======== - - >>> from sympy.physics.gaussopt import BeamParameter - >>> p = BeamParameter(530e-9, 1, w=1e-3) - >>> p.w_0 - 0.00100000000000000 - """ - return sqrt(self.z_r/pi*self.wavelen) -
    - @property -
    [docs] def divergence(self): - """ - Half of the total angular spread. - - Examples - ======== - - >>> from sympy.physics.gaussopt import BeamParameter - >>> p = BeamParameter(530e-9, 1, w=1e-3) - >>> p.divergence - 0.00053/pi - """ - return self.wavelen/pi/self.w_0 -
    - @property -
    [docs] def gouy(self): - """ - The Gouy phase. - - Examples - ======== - - >>> from sympy.physics.gaussopt import BeamParameter - >>> p = BeamParameter(530e-9, 1, w=1e-3) - >>> p.gouy - atan(0.53/pi) - """ - return atan2(self.z, self.z_r) -
    - @property -
    [docs] def waist_approximation_limit(self): - """ - The minimal waist for which the gauss beam approximation is valid. - - The gauss beam is a solution to the paraxial equation. For curvatures - that are too great it is not a valid approximation. - - Examples - ======== - - >>> from sympy.physics.gaussopt import BeamParameter - >>> p = BeamParameter(530e-9, 1, w=1e-3) - >>> p.waist_approximation_limit - 1.06e-6/pi - """ - return 2*self.wavelen/pi - - -### -# Utilities -### -
    -
    [docs]def waist2rayleigh(w, wavelen): - """ - Calculate the rayleigh range from the waist of a gaussian beam. - - See Also - ======== - - rayleigh2waist, BeamParameter - - Examples - ======== - - >>> from sympy.physics.gaussopt import waist2rayleigh - >>> from sympy import symbols - >>> w, wavelen = symbols('w wavelen') - >>> waist2rayleigh(w, wavelen) - pi*w**2/wavelen - """ - w, wavelen = list(map(sympify, (w, wavelen))) - return w**2*pi/wavelen - -
    -
    [docs]def rayleigh2waist(z_r, wavelen): - """Calculate the waist from the rayleigh range of a gaussian beam. - - See Also - ======== - - waist2rayleigh, BeamParameter - - Examples - ======== - - >>> from sympy.physics.gaussopt import rayleigh2waist - >>> from sympy import symbols - >>> z_r, wavelen = symbols('z_r wavelen') - >>> rayleigh2waist(z_r, wavelen) - sqrt(wavelen*z_r)/sqrt(pi) - """ - z_r, wavelen = list(map(sympify, (z_r, wavelen))) - return sqrt(z_r/pi*wavelen) - -
    -
    [docs]def geometric_conj_ab(a, b): - """ - Conjugation relation for geometrical beams under paraxial conditions. - - Takes the distances to the optical element and returns the needed - focal distance. - - See Also - ======== - - geometric_conj_af, geometric_conj_bf - - Examples - ======== - - >>> from sympy.physics.gaussopt import geometric_conj_ab - >>> from sympy import symbols - >>> a, b = symbols('a b') - >>> geometric_conj_ab(a, b) - a*b/(a + b) - """ - a, b = list(map(sympify, (a, b))) - if abs(a) == oo or abs(b) == oo: - return a if abs(b) == oo else b - else: - return a*b/(a + b) - -
    -
    [docs]def geometric_conj_af(a, f): - """ - Conjugation relation for geometrical beams under paraxial conditions. - - Takes the object distance (for geometric_conj_af) or the image distance - (for geometric_conj_bf) to the optical element and the focal distance. - Then it returns the other distance needed for conjugation. - - See Also - ======== - - geometric_conj_ab - - Examples - ======== - - >>> from sympy.physics.gaussopt import geometric_conj_af, geometric_conj_bf - >>> from sympy import symbols - >>> a, b, f = symbols('a b f') - >>> geometric_conj_af(a, f) - a*f/(a - f) - >>> geometric_conj_bf(b, f) - b*f/(b - f) - """ - a, f = list(map(sympify, (a, f))) - return -geometric_conj_ab(a, -f) -
    -geometric_conj_bf = geometric_conj_af - - -
    [docs]def gaussian_conj(s_in, z_r_in, f): - """ - Conjugation relation for gaussian beams. - - Parameters - ========== - - s_in : the distance to optical element from the waist - z_r_in : the rayleigh range of the incident beam - f : the focal length of the optical element - - Returns - ======= - - a tuple containing (s_out, z_r_out, m) - s_out : the distance between the new waist and the optical element - z_r_out : the rayleigh range of the emergent beam - m : the ration between the new and the old waists - - Examples - ======== - - >>> from sympy.physics.gaussopt import gaussian_conj - >>> from sympy import symbols - >>> s_in, z_r_in, f = symbols('s_in z_r_in f') - - >>> gaussian_conj(s_in, z_r_in, f)[0] - 1/(-1/(s_in + z_r_in**2/(-f + s_in)) + 1/f) - - >>> gaussian_conj(s_in, z_r_in, f)[1] - z_r_in/(1 - s_in**2/f**2 + z_r_in**2/f**2) - - >>> gaussian_conj(s_in, z_r_in, f)[2] - 1/sqrt(1 - s_in**2/f**2 + z_r_in**2/f**2) - """ - s_in, z_r_in, f = list(map(sympify, (s_in, z_r_in, f))) - s_out = 1 / ( -1/(s_in + z_r_in**2/(s_in - f)) + 1/f ) - m = 1/sqrt((1 - (s_in/f)**2) + (z_r_in/f)**2) - z_r_out = z_r_in / ((1 - (s_in/f)**2) + (z_r_in/f)**2) - return (s_out, z_r_out, m) - -
    -
    [docs]def conjugate_gauss_beams(wavelen, waist_in, waist_out, **kwargs): - """ - Find the optical setup conjugating the object/image waists. - - Parameters - ========== - - wavelen : the wavelength of the beam - waist_in and waist_out : the waists to be conjugated - f : the focal distance of the element used in the conjugation - - Returns - ======= - - a tuple containing (s_in, s_out, f) - s_in : the distance before the optical element - s_out : the distance after the optical element - f : the focal distance of the optical element - - Examples - ======== - - >>> from sympy.physics.gaussopt import conjugate_gauss_beams - >>> from sympy import symbols, factor - >>> l, w_i, w_o, f = symbols('l w_i w_o f') - - >>> conjugate_gauss_beams(l, w_i, w_o, f=f)[0] - f*(-sqrt(w_i**2/w_o**2 - pi**2*w_i**4/(f**2*l**2)) + 1) - - >>> factor(conjugate_gauss_beams(l, w_i, w_o, f=f)[1]) - f*w_o**2*(w_i**2/w_o**2 - sqrt(w_i**2/w_o**2 - - pi**2*w_i**4/(f**2*l**2)))/w_i**2 - - >>> conjugate_gauss_beams(l, w_i, w_o, f=f)[2] - f - """ - #TODO add the other possible arguments - wavelen, waist_in, waist_out = list(map(sympify, (wavelen, waist_in, waist_out))) - m = waist_out / waist_in - z = waist2rayleigh(waist_in, wavelen) - if len(kwargs) != 1: - raise ValueError("The function expects only one named argument") - elif 'dist' in kwargs: - raise NotImplementedError(filldedent(''' - Currently only focal length is supported as a parameter''')) - elif 'f' in kwargs: - f = sympify(kwargs['f']) - s_in = f * (1 - sqrt(1/m**2 - z**2/f**2)) - s_out = gaussian_conj(s_in, z, f)[0] - elif 's_in' in kwargs: - raise NotImplementedError(filldedent(''' - Currently only focal length is supported as a parameter''')) - else: - raise ValueError(filldedent(''' - The functions expects the focal length as a named argument''')) - return (s_in, s_out, f) - -#TODO -#def plot_beam(): -# """Plot the beam radius as it propagates in space.""" -# pass - -#TODO -#def plot_beam_conjugation(): -# """ -# Plot the intersection of two beams. -# -# Represents the conjugation relation. -# -# See Also -# ======== -# -# conjugate_gauss_beams -# """ -# pass
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/hydrogen.html b/dev-py3k/_modules/sympy/physics/hydrogen.html deleted file mode 100644 index 19988ee21cd..00000000000 --- a/dev-py3k/_modules/sympy/physics/hydrogen.html +++ /dev/null @@ -1,289 +0,0 @@ - - - - - - - - - - sympy.physics.hydrogen — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.hydrogen

    -from sympy import factorial, sqrt, exp, S, assoc_laguerre, Float
    -
    -
    -
    [docs]def R_nl(n, l, r, Z=1): - """ - Returns the Hydrogen radial wavefunction R_{nl}. - - n, l - quantum numbers 'n' and 'l' - r - radial coordinate - Z - atomic number (1 for Hydrogen, 2 for Helium, ...) - - Everything is in Hartree atomic units. - - Examples - ======== - - >>> from sympy.physics.hydrogen import R_nl - >>> from sympy import var - >>> var("r Z") - (r, Z) - >>> R_nl(1, 0, r, Z) - 2*sqrt(Z**3)*exp(-Z*r) - >>> R_nl(2, 0, r, Z) - sqrt(2)*(-Z*r + 2)*sqrt(Z**3)*exp(-Z*r/2)/4 - >>> R_nl(2, 1, r, Z) - sqrt(6)*Z*r*sqrt(Z**3)*exp(-Z*r/2)/12 - - For Hydrogen atom, you can just use the default value of Z=1: - - >>> R_nl(1, 0, r) - 2*exp(-r) - >>> R_nl(2, 0, r) - sqrt(2)*(-r + 2)*exp(-r/2)/4 - >>> R_nl(3, 0, r) - 2*sqrt(3)*(2*r**2/9 - 2*r + 3)*exp(-r/3)/27 - - For Silver atom, you would use Z=47: - - >>> R_nl(1, 0, r, Z=47) - 94*sqrt(47)*exp(-47*r) - >>> R_nl(2, 0, r, Z=47) - 47*sqrt(94)*(-47*r + 2)*exp(-47*r/2)/4 - >>> R_nl(3, 0, r, Z=47) - 94*sqrt(141)*(4418*r**2/9 - 94*r + 3)*exp(-47*r/3)/27 - - The normalization of the radial wavefunction is: - - >>> from sympy import integrate, oo - >>> integrate(R_nl(1, 0, r)**2 * r**2, (r, 0, oo)) - 1 - >>> integrate(R_nl(2, 0, r)**2 * r**2, (r, 0, oo)) - 1 - >>> integrate(R_nl(2, 1, r)**2 * r**2, (r, 0, oo)) - 1 - - It holds for any atomic number: - - >>> integrate(R_nl(1, 0, r, Z=2)**2 * r**2, (r, 0, oo)) - 1 - >>> integrate(R_nl(2, 0, r, Z=3)**2 * r**2, (r, 0, oo)) - 1 - >>> integrate(R_nl(2, 1, r, Z=4)**2 * r**2, (r, 0, oo)) - 1 - - """ - # sympify arguments - n, l, r, Z = S(n), S(l), S(r), S(Z) - # radial quantum number - n_r = n - l - 1 - # rescaled "r" - a = 1/Z # Bohr radius - r0 = 2 * r / (n * a) - # normalization coefficient - C = sqrt((S(2)/(n*a))**3 * factorial(n_r) / (2*n*factorial(n + l))) - # This is an equivalent normalization coefficient, that can be found in - # some books. Both coefficients seem to be the same fast: - # C = S(2)/n**2 * sqrt(1/a**3 * factorial(n_r) / (factorial(n+l))) - return C * r0**l * assoc_laguerre(n_r, 2*l + 1, r0).expand() * exp(-r0/2) - -
    -
    [docs]def E_nl(n, Z=1): - """ - Returns the energy of the state (n, l) in Hartree atomic units. - - The energy doesn't depend on "l". - - Examples - ======== - - >>> from sympy import var - >>> from sympy.physics.hydrogen import E_nl - >>> var("n Z") - (n, Z) - >>> E_nl(n, Z) - -Z**2/(2*n**2) - >>> E_nl(1) - -1/2 - >>> E_nl(2) - -1/8 - >>> E_nl(3) - -1/18 - >>> E_nl(3, 47) - -2209/18 - - """ - n, Z = S(n), S(Z) - if n.is_integer and (n < 1): - raise ValueError("'n' must be positive integer") - return -Z**2/(2*n**2) - -
    -
    [docs]def E_nl_dirac(n, l, spin_up=True, Z=1, c=Float("137.035999037")): - """ - Returns the relativistic energy of the state (n, l, spin) in Hartree atomic - units. - - The energy is calculated from the Dirac equation. The rest mass energy is - *not* included. - - n, l - quantum numbers 'n' and 'l' - spin_up - True if the electron spin is up (default), otherwise down - Z - atomic number (1 for Hydrogen, 2 for Helium, ...) - c - speed of light in atomic units. Default value is 137.035999037, - taken from: http://arxiv.org/abs/1012.3627 - - Examples - ======== - - >>> from sympy.physics.hydrogen import E_nl_dirac - >>> E_nl_dirac(1, 0) - -0.500006656595360 - - >>> E_nl_dirac(2, 0) - -0.125002080189006 - >>> E_nl_dirac(2, 1) - -0.125000416028342 - >>> E_nl_dirac(2, 1, False) - -0.125002080189006 - - >>> E_nl_dirac(3, 0) - -0.0555562951740285 - >>> E_nl_dirac(3, 1) - -0.0555558020932949 - >>> E_nl_dirac(3, 1, False) - -0.0555562951740285 - >>> E_nl_dirac(3, 2) - -0.0555556377366884 - >>> E_nl_dirac(3, 2, False) - -0.0555558020932949 - - """ - if not (l >= 0): - raise ValueError("'l' must be positive or zero") - if not (n > l): - raise ValueError("'n' must be greater than 'l'") - if (l == 0 and spin_up is False): - raise ValueError("Spin must be up for l==0.") - # skappa is sign*kappa, where sign contains the correct sign - if spin_up: - skappa = -l - 1 - else: - skappa = -l - c = S(c) - beta = sqrt(skappa**2 - Z**2/c**2) - return c**2/sqrt(1 + Z**2/(n + skappa + beta)**2/c**2) - c**2
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/matrices.html b/dev-py3k/_modules/sympy/physics/matrices.html deleted file mode 100644 index b1463850b77..00000000000 --- a/dev-py3k/_modules/sympy/physics/matrices.html +++ /dev/null @@ -1,267 +0,0 @@ - - - - - - - - - - sympy.physics.matrices — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.matrices

    -"""Known matrices related to physics"""
    -
    -from sympy import Matrix, I
    -
    -
    -
    [docs]def msigma(i): - """Returns a Pauli matrix sigma_i. i=1,2,3 - - See Also - ======== - - http://en.wikipedia.org/wiki/Pauli_matrices - - Examples - ======== - - >>> from sympy.physics.matrices import msigma - >>> msigma(1) - [0, 1] - [1, 0] - """ - if i == 1: - mat = ( ( - (0, 1), - (1, 0) - ) ) - elif i == 2: - mat = ( ( - (0, -I), - (I, 0) - ) ) - elif i == 3: - mat = ( ( - (1, 0), - (0, -1) - ) ) - else: - raise IndexError("Invalid Pauli index") - return Matrix(mat) - -
    -
    [docs]def pat_matrix(m, dx, dy, dz): - """Returns the Parallel Axis Theorem matrix to translate the inertia - matrix a distance of (dx, dy, dz) for a body of mass m. - - Examples - -------- - If the point we want the inertia about is a distance of 2 units of - length and 1 unit along the x-axis we get: - >>> from sympy.physics.matrices import pat_matrix - >>> pat_matrix(2,1,0,0) - [0, 0, 0] - [0, 2, 0] - [0, 0, 2] - - In case we want to find the inertia along a vector of (1,1,1): - >>> pat_matrix(2,1,1,1) - [ 4, -2, -2] - [-2, 4, -2] - [-2, -2, 4] - """ - dxdy = -dx*dy - dydz = -dy*dz - dzdx = -dz*dx - dxdx = dx**2 - dydy = dy**2 - dzdz = dz**2 - mat = ((dydy + dzdz, dxdy, dzdx), - (dxdy, dxdx + dzdz, dydz), - (dzdx, dydz, dydy + dxdx)) - return m*Matrix(mat) - -
    -
    [docs]def mgamma(mu, lower=False): - """Returns a Dirac gamma matrix gamma^mu in the standard - (Dirac) representation. - - If you want gamma_mu, use gamma(mu, True). - - We use a convention: - - gamma^5 = I * gamma^0 * gamma^1 * gamma^2 * gamma^3 - gamma_5 = I * gamma_0 * gamma_1 * gamma_2 * gamma_3 = - gamma^5 - - See Also - ======== - - http://en.wikipedia.org/wiki/Gamma_matrices - - Examples - ======== - - >>> from sympy.physics.matrices import mgamma - >>> mgamma(1) - [ 0, 0, 0, 1] - [ 0, 0, 1, 0] - [ 0, -1, 0, 0] - [-1, 0, 0, 0] - """ - if not mu in [0, 1, 2, 3, 5]: - raise IndexError("Invalid Dirac index") - if mu == 0: - mat = ( - (1, 0, 0, 0), - (0, 1, 0, 0), - (0, 0, -1, 0), - (0, 0, 0, -1) - ) - elif mu == 1: - mat = ( - (0, 0, 0, 1), - (0, 0, 1, 0), - (0, -1, 0, 0), - (-1, 0, 0, 0) - ) - elif mu == 2: - mat = ( - (0, 0, 0, -I), - (0, 0, I, 0), - (0, I, 0, 0), - (-I, 0, 0, 0) - ) - elif mu == 3: - mat = ( - (0, 0, 1, 0), - (0, 0, 0, -1), - (-1, 0, 0, 0), - (0, 1, 0, 0) - ) - elif mu == 5: - mat = ( - (0, 0, 1, 0), - (0, 0, 0, 1), - (1, 0, 0, 0), - (0, 1, 0, 0) - ) - m = Matrix(mat) - if lower: - if mu in [1, 2, 3, 5]: - m = -m - return m - -#Minkowski tensor using the convention (+,-,-,-) used in the Quantum Field -#Theory
    -minkowski_tensor = Matrix( ( - (1, 0, 0, 0), - (0, -1, 0, 0), - (0, 0, -1, 0), - (0, 0, 0, -1) -)) -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/mechanics/essential.html b/dev-py3k/_modules/sympy/physics/mechanics/essential.html deleted file mode 100644 index 3596b1a3723..00000000000 --- a/dev-py3k/_modules/sympy/physics/mechanics/essential.html +++ /dev/null @@ -1,2050 +0,0 @@ - - - - - - - - - - sympy.physics.mechanics.essential — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.mechanics.essential

    -__all__ = ['ReferenceFrame', 'Vector', 'Dyadic', 'dynamicsymbols',
    -           'MechanicsStrPrinter', 'MechanicsPrettyPrinter',
    -           'MechanicsLatexPrinter']
    -
    -from sympy import (
    -    Matrix, Symbol, sin, cos, eye, trigsimp, diff, sqrt, sympify,
    -    expand, zeros, Derivative, Function, symbols, Add,
    -    solve, S)
    -from sympy.core import C
    -from sympy.core.function import UndefinedFunction
    -from sympy.printing.conventions import split_super_sub
    -from sympy.printing.latex import LatexPrinter
    -from sympy.printing.pretty.pretty import PrettyPrinter
    -from sympy.printing.pretty.stringpict import prettyForm, stringPict
    -from sympy.printing.str import StrPrinter
    -from sympy.utilities import group
    -from sympy.core.compatibility import reduce
    -from functools import reduce
    -
    -
    -
    [docs]class Dyadic(object): - """A Dyadic object. - - See: - http://en.wikipedia.org/wiki/Dyadic_tensor - Kane, T., Levinson, D. Dynamics Theory and Applications. 1985 McGraw-Hill - - A more powerful way to represent a rigid body's inertia. While it is more - complex, by choosing Dyadic components to be in body fixed basis vectors, - the resulting matrix is equivalent to the inertia tensor. - - """ - - def __init__(self, inlist): - """ - Just like Vector's init, you shouldn't call this. - - Stores a Dyadic as a list of lists; the inner list has the measure number - and the two unit vectors; the outerlist holds each unique unit vector - pair. - - """ - - self.args = [] - while len(inlist) != 0: - added = 0 - for i, v in enumerate(self.args): - if ((str(inlist[0][1]) == str(self.args[i][1])) and - (str(inlist[0][2]) == str(self.args[i][2]))): - self.args[i] = (self.args[i][0] + - inlist[0][0], inlist[0][1], inlist[0][2]) - inlist.remove(inlist[0]) - added = 1 - break - if added != 1: - self.args.append(inlist[0]) - inlist.remove(inlist[0]) - i = 0 - # This code is to remove empty parts from the list - while i < len(self.args): - if ((self.args[i][0] == 0) | (self.args[i][1] == 0) | - (self.args[i][2] == 0)): - self.args.remove(self.args[i]) - i -= 1 - i += 1 - - def __add__(self, other): - """The add operator for Dyadic. """ - other = _check_dyadic(other) - return Dyadic(self.args + other.args) - - def __and__(self, other): - """The inner product operator for a Dyadic and a Dyadic or Vector. - - Parameters - ========== - - other : Dyadic or Vector - The other Dyadic or Vector to take the inner product with - - Examples - ======== - - >>> from sympy.physics.mechanics import ReferenceFrame, outer - >>> N = ReferenceFrame('N') - >>> D1 = outer(N.x, N.y) - >>> D2 = outer(N.y, N.y) - >>> D1.dot(D2) - (N.x|N.y) - >>> D1.dot(N.y) - N.x - - """ - - if isinstance(other, Dyadic): - other = _check_dyadic(other) - ol = Dyadic([]) - for i, v in enumerate(self.args): - for i2, v2 in enumerate(other.args): - ol += v[0] * v2[0] * (v[2] & v2[1]) * (v[1] | v2[2]) - else: - other = _check_vector(other) - ol = Vector([]) - for i, v in enumerate(self.args): - ol += v[0] * v[1] * (v[2] & other) - return ol - - def __div__(self, other): - """Divides the Dyadic by a sympifyable expression. """ - return self.__mul__(1 / other) - - __truediv__ = __div__ - - def __eq__(self, other): - """Tests for equality. - - Is currently weak; needs stronger comparison testing - - """ - - other = _check_dyadic(other) - if (self.args == []) and (other.args == []): - return True - elif (self.args == []) or (other.args == []): - return False - return set(self.args) == set(other.args) - - def __mul__(self, other): - """Multiplies the Dyadic by a sympifyable expression. - - Parameters - ========== - - other : Sympafiable - The scalar to multiply this Dyadic with - - Examples - ======== - - >>> from sympy.physics.mechanics import ReferenceFrame, outer - >>> N = ReferenceFrame('N') - >>> d = outer(N.x, N.x) - >>> 5 * d - 5*(N.x|N.x) - - """ - - newlist = [v for v in self.args] - for i, v in enumerate(newlist): - newlist[i] = (sympify(other) * newlist[i][0], newlist[i][1], - newlist[i][2]) - return Dyadic(newlist) - - def __ne__(self, other): - return not self.__eq__(other) - - def __neg__(self): - return self * -1 - - def _latex(self, printer=None): - ar = self.args # just to shorten things - if len(ar) == 0: - return str(0) - ol = [] # output list, to be concatenated to a string - mlp = MechanicsLatexPrinter() - for i, v in enumerate(ar): - # if the coef of the dyadic is 1, we skip the 1 - if ar[i][0] == 1: - ol.append(' + ' + mlp.doprint(ar[i][1]) + r"\otimes " + - mlp.doprint(ar[i][2])) - # if the coef of the dyadic is -1, we skip the 1 - elif ar[i][0] == -1: - ol.append(' - ' + - mlp.doprint(ar[i][1]) + - r"\otimes " + - mlp.doprint(ar[i][2])) - # If the coefficient of the dyadic is not 1 or -1, - # we might wrap it in parentheses, for readability. - elif ar[i][0] != 0: - arg_str = mlp.doprint(ar[i][0]) - if isinstance(ar[i][0], Add): - arg_str = '(%s)' % arg_str - if arg_str.startswith('-'): - arg_str = arg_str[1:] - str_start = ' - ' - else: - str_start = ' + ' - ol.append(str_start + arg_str + r" " + - mlp.doprint(ar[i][1]) + - r"\otimes " + - mlp.doprint(ar[i][2])) - outstr = ''.join(ol) - if outstr.startswith(' + '): - outstr = outstr[3:] - elif outstr.startswith(' '): - outstr = outstr[1:] - return outstr - - def _pretty(self, printer=None): - e = self - - class Fake(object): - baseline = 0 - - def render(self, *args, **kwargs): - self = e - ar = self.args # just to shorten things - mpp = MechanicsPrettyPrinter() - if len(ar) == 0: - return str(0) - ol = [] # output list, to be concatenated to a string - for i, v in enumerate(ar): - # if the coef of the dyadic is 1, we skip the 1 - if ar[i][0] == 1: - ol.append(" + " + - mpp.doprint(ar[i][1]) + - "\u2a02 " + - mpp.doprint(ar[i][2])) - # if the coef of the dyadic is -1, we skip the 1 - elif ar[i][0] == -1: - ol.append(" - " + - mpp.doprint(ar[i][1]) + - "\u2a02 " + - mpp.doprint(ar[i][2])) - # If the coefficient of the dyadic is not 1 or -1, - # we might wrap it in parentheses, for readability. - elif ar[i][0] != 0: - arg_str = mpp.doprint(ar[i][0]) - if isinstance(ar[i][0], Add): - arg_str = "(%s)" % arg_str - if arg_str.startswith("-"): - arg_str = arg_str[1:] - str_start = " - " - else: - str_start = " + " - ol.append(str_start + arg_str + " " + - mpp.doprint(ar[i][1]) + - "\u2a02 " + - mpp.doprint(ar[i][2])) - outstr = "".join(ol) - if outstr.startswith(" + "): - outstr = outstr[3:] - elif outstr.startswith(" "): - outstr = outstr[1:] - return outstr - return Fake() - - def __rand__(self, other): - """The inner product operator for a Vector or Dyadic, and a Dyadic - - This is for: Vector dot Dyadic - - Parameters - ========== - - other : Vector - The vector we are dotting with - - Examples - ======== - - >>> from sympy.physics.mechanics import ReferenceFrame, dot, outer - >>> N = ReferenceFrame('N') - >>> d = outer(N.x, N.x) - >>> dot(N.x, d) - N.x - - """ - - other = _check_vector(other) - ol = Vector([]) - for i, v in enumerate(self.args): - ol += v[0] * v[2] * (v[1] & other) - return ol - - def __rsub__(self, other): - return (-1 * self) + other - - def __rxor__(self, other): - """For a cross product in the form: Vector x Dyadic - - Parameters - ========== - - other : Vector - The Vector that we are crossing this Dyadic with - - Examples - ======== - - >>> from sympy.physics.mechanics import ReferenceFrame, outer, cross - >>> N = ReferenceFrame('N') - >>> d = outer(N.x, N.x) - >>> cross(N.y, d) - - (N.z|N.x) - - """ - - other = _check_vector(other) - ol = Dyadic([]) - for i, v in enumerate(self.args): - ol += v[0] * ((other ^ v[1]) | v[2]) - return ol - - def __str__(self, printer=None): - """Printing method. """ - ar = self.args # just to shorten things - if len(ar) == 0: - return str(0) - ol = [] # output list, to be concatenated to a string - for i, v in enumerate(ar): - # if the coef of the dyadic is 1, we skip the 1 - if ar[i][0] == 1: - ol.append(' + (' + str(ar[i][1]) + '|' + str(ar[i][2]) + ')') - # if the coef of the dyadic is -1, we skip the 1 - elif ar[i][0] == -1: - ol.append(' - (' + str(ar[i][1]) + '|' + str(ar[i][2]) + ')') - # If the coefficient of the dyadic is not 1 or -1, - # we might wrap it in parentheses, for readability. - elif ar[i][0] != 0: - arg_str = MechanicsStrPrinter().doprint(ar[i][0]) - if isinstance(ar[i][0], Add): - arg_str = "(%s)" % arg_str - if arg_str[0] == '-': - arg_str = arg_str[1:] - str_start = ' - ' - else: - str_start = ' + ' - ol.append(str_start + arg_str + '*(' + str(ar[i][1]) + - '|' + str(ar[i][2]) + ')') - outstr = ''.join(ol) - if outstr.startswith(' + '): - outstr = outstr[3:] - elif outstr.startswith(' '): - outstr = outstr[1:] - return outstr - - def __sub__(self, other): - """The subtraction operator. """ - return self.__add__(other * -1) - - def __xor__(self, other): - """For a cross product in the form: Dyadic x Vector. - - Parameters - ========== - - other : Vector - The Vector that we are crossing this Dyadic with - - Examples - ======== - - >>> from sympy.physics.mechanics import ReferenceFrame, outer, cross - >>> N = ReferenceFrame('N') - >>> d = outer(N.x, N.x) - >>> cross(d, N.y) - (N.x|N.z) - - """ - - other = _check_vector(other) - ol = Dyadic([]) - for i, v in enumerate(self.args): - ol += v[0] * (v[1] | (v[2] ^ other)) - return ol - - _sympystr = __str__ - _sympyrepr = _sympystr - __repr__ = __str__ - __radd__ = __add__ - __rmul__ = __mul__ - -
    [docs] def express(self, frame1, frame2=None): - """Expresses this Dyadic in alternate frame(s) - - The first frame is the list side expression, the second frame is the - right side; if Dyadic is in form A.x|B.y, you can express it in two - different frames. If no second frame is given, the Dyadic is expressed in - only one frame. - - Parameters - ========== - - frame1 : ReferenceFrame - The frame to express the left side of the Dyadic in - frame2 : ReferenceFrame - If provided, the frame to express the right side of the Dyadic in - - Examples - ======== - - >>> from sympy.physics.mechanics import ReferenceFrame, outer, dynamicsymbols - >>> N = ReferenceFrame('N') - >>> q = dynamicsymbols('q') - >>> B = N.orientnew('B', 'Axis', [q, N.z]) - >>> d = outer(N.x, N.x) - >>> d.express(B, N) - cos(q)*(B.x|N.x) - sin(q)*(B.y|N.x) - - """ - - if frame2 is None: - frame2 = frame1 - _check_frame(frame1) - _check_frame(frame2) - ol = S(0) - for i, v in enumerate(self.args): - ol += v[0] * (v[1].express(frame1) | v[2].express(frame2)) - return ol -
    -
    [docs] def doit(self, **hints): - """Calls .doit() on each term in the Dyadic""" - return sum([Dyadic( [ (v[0].doit(**hints), v[1], v[2]) ]) for - v in self.args]) -
    -
    [docs] def dt(self, frame): - """Take the time derivative of this Dyadic in a frame. - - Parameters - ========== - - frame : ReferenceFrame - The frame to take the time derivative in - - Examples - ======== - - >>> from sympy.physics.mechanics import ReferenceFrame, outer, dynamicsymbols - >>> N = ReferenceFrame('N') - >>> q = dynamicsymbols('q') - >>> B = N.orientnew('B', 'Axis', [q, N.z]) - >>> d = outer(N.x, N.x) - >>> d.dt(B) - - q'*(N.y|N.x) - q'*(N.x|N.y) - - """ - - _check_frame(frame) - t = dynamicsymbols._t - ol = S(0) - for i, v in enumerate(self.args): - ol += (v[0].diff(t) * (v[1] | v[2])) - ol += (v[0] * (v[1].dt(frame) | v[2])) - ol += (v[0] * (v[1] | v[2].dt(frame))) - return ol -
    -
    [docs] def simplify(self): - """Simplify the elements in the Dyadic in-place.""" - for i, v in enumerate(self.args): - self.args[i] = (v[0].simplify(), v[1], v[2]) -
    -
    [docs] def subs(self, *args, **kwargs): - """Substituion on the Dyadic. - - Examples - ======== - - >>> from sympy.physics.mechanics import ReferenceFrame - >>> from sympy import Symbol - >>> N = ReferenceFrame('N') - >>> s = Symbol('s') - >>> a = s * (N.x|N.x) - >>> a.subs({s: 2}) - 2*(N.x|N.x) - - """ - - return sum([ Dyadic([(v[0].subs(*args, **kwargs), v[1], v[2])]) - for v in self.args]) -
    - dot = __and__ - cross = __xor__ - -
    -
    [docs]class ReferenceFrame(object): - """A reference frame in classical mechanics. - - ReferenceFrame is a class used to represent a reference frame in classical - mechanics. It has a standard basis of three unit vectors in the frame's - x, y, and z directions. - - It also can have a rotation relative to a parent frame; this rotation is - defined by a direction cosine matrix relating this frame's basis vectors to - the parent frame's basis vectors. It can also have an angular velocity - vector, defined in another frame. - - """ - - def __init__(self, name, indices=None, latexs=None): - """ReferenceFrame initialization method. - - A ReferenceFrame has a set of orthonormal basis vectors, along with - orientations relative to other ReferenceFrames and angular velocities - relative to other ReferenceFrames. - - Parameters - ========== - - indices : list (of strings) - If custom indices are desired for console, pretty, and LaTeX - printing, supply three as a list. The basis vectors can then be - accessed with the get_item method. - latexs : list (of strings) - If custom names are desired for LaTeX printing of each basis - vector, supply the names here in a list. - - Examples - ======== - - >>> from sympy.physics.mechanics import ReferenceFrame, mlatex - >>> N = ReferenceFrame('N') - >>> N.x - N.x - >>> O = ReferenceFrame('O', ('1', '2', '3')) - >>> O.x - O['1'] - >>> O['1'] - O['1'] - >>> P = ReferenceFrame('P', latexs=('A1', 'A2', 'A3')) - >>> mlatex(P.x) - 'A1' - - """ - - if not isinstance(name, str): - raise TypeError('Need to supply a valid name') - # The if statements below are for custom printing of basis-vectors for - # each frame. - # First case, when custom indices are supplied - if indices is not None: - if not isinstance(indices, (tuple, list)): - raise TypeError('Supply the indices as a list') - if len(indices) != 3: - raise ValueError('Supply 3 indices') - for i in indices: - if not isinstance(i, str): - raise TypeError('Indices must be strings') - self.str_vecs = [(name + '[\'' + indices[0] + '\']'), - (name + '[\'' + indices[1] + '\']'), - (name + '[\'' + indices[2] + '\']')] - self.pretty_vecs = [("\033[94m\033[1m" + name.lower() + "_" + - indices[0] + "\033[0;0m\x1b[0;0m"), - ("\033[94m\033[1m" + name.lower() + "_" + - indices[1] + "\033[0;0m\x1b[0;0m"), - ("\033[94m\033[1m" + name.lower() + "_" + - indices[2] + "\033[0;0m\x1b[0;0m")] - self.latex_vecs = [(r"\mathbf{\hat{%s}_{%s}}" % (name.lower(), - indices[0])), (r"\mathbf{\hat{%s}_{%s}}" % - (name.lower(), indices[1])), - (r"\mathbf{\hat{%s}_{%s}}" % (name.lower(), - indices[2]))] - self.indices = indices - # Second case, when no custom indices are supplied - else: - self.str_vecs = [(name + '.x'), (name + '.y'), (name + '.z')] - self.pretty_vecs = [("\033[94m\033[1m" + name.lower() + - "_x\033[0;0m\x1b[0;0m"), - ("\033[94m\033[1m" + name.lower() + - "_y\033[0;0m\x1b[0;0m"), - ("\033[94m\033[1m" + name.lower() + - "_z\033[0;0m\x1b[0;0m")] - self.latex_vecs = [(r"\mathbf{\hat{%s}_x}" % name.lower()), - (r"\mathbf{\hat{%s}_y}" % name.lower()), - (r"\mathbf{\hat{%s}_z}" % name.lower())] - self.indices = ['x', 'y', 'z'] - # Different step, for custom latex basis vectors - if latexs is not None: - if not isinstance(latexs, (tuple, list)): - raise TypeError('Supply the indices as a list') - if len(latexs) != 3: - raise ValueError('Supply 3 indices') - for i in latexs: - if not isinstance(i, str): - raise TypeError('Latex entries must be strings') - self.latex_vecs = latexs - self.name = name - self._dcm_dict = {} - self._ang_vel_dict = {} - self._ang_acc_dict = {} - self._dlist = [self._dcm_dict, self._ang_vel_dict, self._ang_acc_dict] - self._cur = 0 - self._x = Vector([(Matrix([1, 0, 0]), self)]) - self._y = Vector([(Matrix([0, 1, 0]), self)]) - self._z = Vector([(Matrix([0, 0, 1]), self)]) - - def __getitem__(self, ind): - """Returns basis vector for the provided index (index being an str)""" - if not isinstance(ind, str): - raise TypeError('Supply a valid str for the index') - if self.indices[0] == ind: - return self.x - if self.indices[1] == ind: - return self.y - if self.indices[2] == ind: - return self.z - else: - raise ValueError('Not a defined index') - - def __iter__(self): - return iter([self.x, self.y, self.z]) - - def __str__(self): - """Returns the name of the frame. """ - return self.name - - __repr__ = __str__ - - def _dict_list(self, other, num): - """Creates a list from self to other using _dcm_dict. """ - outlist = [[self]] - oldlist = [[]] - while outlist != oldlist: - oldlist = outlist[:] - for i, v in enumerate(outlist): - templist = list(v[-1]._dlist[num].keys()) - for i2, v2 in enumerate(templist): - if not v.__contains__(v2): - littletemplist = v + [v2] - if not outlist.__contains__(littletemplist): - outlist.append(littletemplist) - for i, v in enumerate(oldlist): - if v[-1] != other: - outlist.remove(v) - outlist.sort(key=len) - if len(outlist) != 0: - return outlist[0] - raise ValueError('No Connecting Path found between ' + self.name + - ' and ' + other.name) - - def _w_diff_dcm(self, otherframe): - """Angular velocity from time differentiating the DCM. """ - dcm2diff = self.dcm(otherframe) - diffed = dcm2diff.diff(dynamicsymbols._t) - angvelmat = diffed * dcm2diff.T - w1 = trigsimp(expand(angvelmat[7]), recursive=True) - w2 = trigsimp(expand(angvelmat[2]), recursive=True) - w3 = trigsimp(expand(angvelmat[3]), recursive=True) - return -Vector([(Matrix([w1, w2, w3]), self)]) - -
    [docs] def ang_acc_in(self, otherframe): - """Returns the angular acceleration Vector of the ReferenceFrame. - - Effectively returns the Vector: - ^N alpha ^B - which represent the angular acceleration of B in N, where B is self, and - N is otherframe. - - Parameters - ========== - - otherframe : ReferenceFrame - The ReferenceFrame which the angular acceleration is returned in. - - Examples - ======== - - >>> from sympy.physics.mechanics import ReferenceFrame, Vector - >>> N = ReferenceFrame('N') - >>> A = ReferenceFrame('A') - >>> V = 10 * N.x - >>> A.set_ang_acc(N, V) - >>> A.ang_acc_in(N) - 10*N.x - - """ - - _check_frame(otherframe) - if otherframe in self._ang_acc_dict: - return self._ang_acc_dict[otherframe] - else: - return self.ang_vel_in(otherframe).dt(otherframe) -
    -
    [docs] def ang_vel_in(self, otherframe): - """Returns the angular velocity Vector of the ReferenceFrame. - - Effectively returns the Vector: - ^N omega ^B - which represent the angular velocity of B in N, where B is self, and - N is otherframe. - - Parameters - ========== - - otherframe : ReferenceFrame - The ReferenceFrame which the angular velocity is returned in. - - Examples - ======== - - >>> from sympy.physics.mechanics import ReferenceFrame, Vector - >>> N = ReferenceFrame('N') - >>> A = ReferenceFrame('A') - >>> V = 10 * N.x - >>> A.set_ang_vel(N, V) - >>> A.ang_vel_in(N) - 10*N.x - - """ - - _check_frame(otherframe) - flist = self._dict_list(otherframe, 1) - outvec = Vector([]) - for i in range(len(flist) - 1): - outvec += flist[i]._ang_vel_dict[flist[i + 1]] - return outvec -
    -
    [docs] def dcm(self, otherframe): - """The direction cosine matrix between frames. - - This gives the DCM between this frame and the otherframe. - The format is N.xyz = N.dcm(B) * B.xyz - A SymPy Matrix is returned. - - Parameters - ========== - - otherframe : ReferenceFrame - The otherframe which the DCM is generated to. - - Examples - ======== - - >>> from sympy.physics.mechanics import ReferenceFrame, Vector - >>> from sympy import symbols - >>> q1 = symbols('q1') - >>> N = ReferenceFrame('N') - >>> A = N.orientnew('A', 'Axis', [q1, N.x]) - >>> N.dcm(A) - [1, 0, 0] - [0, cos(q1), -sin(q1)] - [0, sin(q1), cos(q1)] - - """ - - _check_frame(otherframe) - flist = self._dict_list(otherframe, 0) - outdcm = eye(3) - for i in range(len(flist) - 1): - outdcm = outdcm * flist[i + 1]._dcm_dict[flist[i]] - return outdcm -
    -
    [docs] def orient(self, parent, rot_type, amounts, rot_order=''): - """Defines the orientation of this frame relative to a parent frame. - - Supported orientation types are Body, Space, Quaternion, Axis. - Examples show correct usage. - - Parameters - ========== - - parent : ReferenceFrame - The frame that this ReferenceFrame will have its orientation matrix - defined in relation to. - rot_type : str - The type of orientation matrix that is being created. - amounts : list OR value - The quantities that the orientation matrix will be defined by. - rot_order : str - If applicable, the order of a series of rotations. - - Examples - ======== - - >>> from sympy.physics.mechanics import ReferenceFrame, Vector - >>> from sympy import symbols - >>> q0, q1, q2, q3, q4 = symbols('q0 q1 q2 q3 q4') - >>> N = ReferenceFrame('N') - >>> B = ReferenceFrame('B') - - Now we have a choice of how to implement the orientation. First is - Body. Body orientation takes this reference frame through three - successive simple rotations. Acceptable rotation orders are of length - 3, expressed in XYZ or 123, and cannot have a rotation about about an - axis twice in a row. - - >>> B.orient(N, 'Body', [q1, q2, q3], '123') - >>> B.orient(N, 'Body', [q1, q2, 0], 'ZXZ') - >>> B.orient(N, 'Body', [0, 0, 0], 'XYX') - - Next is Space. Space is like Body, but the rotations are applied in the - opposite order. - - >>> B.orient(N, 'Space', [q1, q2, q3], '312') - - Next is Quaternion. This orients the new ReferenceFrame with - Quaternions, defined as a finite rotation about lambda, a unit vector, - by some amount theta. - This orientation is described by four parameters: - q0 = cos(theta/2) - q1 = lambda_x sin(theta/2) - q2 = lambda_y sin(theta/2) - q3 = lambda_z sin(theta/2) - Quaternion does not take in a rotation order. - - >>> B.orient(N, 'Quaternion', [q0, q1, q2, q3]) - - Last is Axis. This is a rotation about an arbitrary, non-time-varying - axis by some angle. The axis is supplied as a Vector. This is how - simple rotations are defined. - - >>> B.orient(N, 'Axis', [q1, N.x + 2 * N.y]) - - """ - - _check_frame(parent) - amounts = list(amounts) - for i, v in enumerate(amounts): - if not isinstance(v, Vector): - amounts[i] = sympify(v) - - def _rot(axis, angle): - """DCM for simple axis 1,2,or 3 rotations. """ - if axis == 1: - return Matrix([[1, 0, 0], - [0, cos(angle), -sin(angle)], - [0, sin(angle), cos(angle)]]) - elif axis == 2: - return Matrix([[cos(angle), 0, sin(angle)], - [0, 1, 0], - [-sin(angle), 0, cos(angle)]]) - elif axis == 3: - return Matrix([[cos(angle), -sin(angle), 0], - [sin(angle), cos(angle), 0], - [0, 0, 1]]) - - approved_orders = ('123', '231', '312', '132', '213', '321', '121', - '131', '212', '232', '313', '323', '') - rot_order = str( - rot_order).upper() # Now we need to make sure XYZ = 123 - rot_type = rot_type.upper() - rot_order = [i.replace('X', '1') for i in rot_order] - rot_order = [i.replace('Y', '2') for i in rot_order] - rot_order = [i.replace('Z', '3') for i in rot_order] - rot_order = ''.join(rot_order) - if not rot_order in approved_orders: - raise TypeError('The supplied order is not an approved type') - parent_orient = [] - - if rot_type == 'AXIS': - if not rot_order == '': - raise TypeError('Axis orientation takes no rotation order') - if not (isinstance(amounts, (list, tuple)) & (len(amounts) == 2)): - raise TypeError('Amounts are a list or tuple of length 2') - theta = amounts[0] - axis = amounts[1] - axis = _check_vector(axis) - if not axis.dt(parent) == 0: - raise ValueError('Axis cannot be time-varying') - axis = axis.express(parent).normalize() - axis = axis.args[0][0] - parent_orient = ((eye(3) - axis * axis.T) * cos(theta) + - Matrix([[0, -axis[2], axis[1]], [axis[2], 0, -axis[0]], - [-axis[1], axis[0], 0]]) * sin(theta) + axis * axis.T) - elif rot_type == 'QUATERNION': - if not rot_order == '': - raise TypeError( - 'Quaternion orientation takes no rotation order') - if not (isinstance(amounts, (list, tuple)) & (len(amounts) == 4)): - raise TypeError('Amounts are a list or tuple of length 4') - q0, q1, q2, q3 = amounts - parent_orient = (Matrix([[q0 ** 2 + q1 ** 2 - q2 ** 2 - q3 ** - 2, 2 * (q1 * q2 - q0 * q3), 2 * (q0 * q2 + q1 * q3)], - [2 * (q1 * q2 + q0 * q3), q0 ** 2 - q1 ** 2 + q2 **2 - q3 ** 2, - 2 * (q2 * q3 - q0 * q1)], [2 * (q1 * q3 - q0 * q2), 2 * (q0 * - q1 + q2 * q3), q0 ** 2 - q1 ** 2 - q2 ** 2 + q3 ** 2]])) - elif rot_type == 'BODY': - if not (len(amounts) == 3 & len(rot_order) == 3): - raise TypeError('Body orientation takes 3 values & 3 orders') - a1 = int(rot_order[0]) - a2 = int(rot_order[1]) - a3 = int(rot_order[2]) - parent_orient = (_rot(a1, amounts[0]) * _rot(a2, amounts[1]) - * _rot(a3, amounts[2])) - elif rot_type == 'SPACE': - if not (len(amounts) == 3 & len(rot_order) == 3): - raise TypeError('Space orientation takes 3 values & 3 orders') - a1 = int(rot_order[0]) - a2 = int(rot_order[1]) - a3 = int(rot_order[2]) - parent_orient = (_rot(a3, amounts[2]) * _rot(a2, amounts[1]) - * _rot(a1, amounts[0])) - else: - raise NotImplementedError('That is not an implemented rotation') - self._dcm_dict.update({parent: parent_orient}) - parent._dcm_dict.update({self: parent_orient.T}) - if rot_type == 'QUATERNION': - t = dynamicsymbols._t - q0, q1, q2, q3 = amounts - q0d = diff(q0, t) - q1d = diff(q1, t) - q2d = diff(q2, t) - q3d = diff(q3, t) - w1 = 2 * (q1d * q0 + q2d * q3 - q3d * q2 - q0d * q1) - w2 = 2 * (q2d * q0 + q3d * q1 - q1d * q3 - q0d * q2) - w3 = 2 * (q3d * q0 + q1d * q2 - q2d * q1 - q0d * q3) - wvec = Vector([(Matrix([w1, w2, w3]), self)]) - elif rot_type == 'AXIS': - thetad = (amounts[0]).diff(dynamicsymbols._t) - wvec = thetad * amounts[1].express(parent).normalize() - else: - try: - from sympy.polys.polyerrors import CoercionFailed - from sympy.physics.mechanics.functions import kinematic_equations - q1, q2, q3 = amounts - u1, u2, u3 = dynamicsymbols('u1, u2, u3') - templist = kinematic_equations([u1, u2, u3], [q1, q2, q3], - rot_type, rot_order) - templist = [expand(i) for i in templist] - td = solve(templist, [u1, u2, u3]) - u1 = expand(td[u1]) - u2 = expand(td[u2]) - u3 = expand(td[u3]) - wvec = u1 * self.x + u2 * self.y + u3 * self.z - except (CoercionFailed, AssertionError): - wvec = self._w_diff_dcm(parent) - self._ang_vel_dict.update({parent: wvec}) - parent._ang_vel_dict.update({self: -wvec}) -
    -
    [docs] def orientnew(self, newname, rot_type, amounts, rot_order='', indices=None, - latexs=None): - """Creates a new ReferenceFrame oriented with respect to this Frame. - - See ReferenceFrame.orient() for acceptable rotation types, amounts, - and orders. Parent is going to be self. - - Parameters - ========== - - newname : str - The name for the new ReferenceFrame - rot_type : str - The type of orientation matrix that is being created. - amounts : list OR value - The quantities that the orientation matrix will be defined by. - rot_order : str - If applicable, the order of a series of rotations. - - - Examples - ======== - - >>> from sympy.physics.mechanics import ReferenceFrame, Vector - >>> from sympy import symbols - >>> q1 = symbols('q1') - >>> N = ReferenceFrame('N') - >>> A = N.orientnew('A', 'Axis', [q1, N.x]) - - - .orient() documentation:\n - ======================== - - """ - - newframe = ReferenceFrame(newname, indices, latexs) - newframe.orient(self, rot_type, amounts, rot_order) - return newframe -
    - orientnew.__doc__ += orient.__doc__ - -
    [docs] def set_ang_acc(self, otherframe, value): - """Define the angular acceleration Vector in a ReferenceFrame. - - Defines the angular acceleration of this ReferenceFrame, in another. - Angular acceleration can be defined with respect to multiple different - ReferenceFrames. Care must be taken to not create loops which are - inconsistent. - - Parameters - ========== - - otherframe : ReferenceFrame - A ReferenceFrame to define the angular acceleration in - value : Vector - The Vector representing angular acceleration - - Examples - ======== - - >>> from sympy.physics.mechanics import ReferenceFrame, Vector - >>> N = ReferenceFrame('N') - >>> A = ReferenceFrame('A') - >>> V = 10 * N.x - >>> A.set_ang_acc(N, V) - >>> A.ang_acc_in(N) - 10*N.x - - """ - - value = _check_vector(value) - _check_frame(otherframe) - self._ang_acc_dict.update({otherframe: value}) - otherframe._ang_acc_dict.update({self: -value}) -
    -
    [docs] def set_ang_vel(self, otherframe, value): - """Define the angular velocity vector in a ReferenceFrame. - - Defines the angular velocity of this ReferenceFrame, in another. - Angular velocity can be defined with respect to multiple different - ReferenceFrames. Care must be taken to not create loops which are - inconsistent. - - Parameters - ========== - - otherframe : ReferenceFrame - A ReferenceFrame to define the angular velocity in - value : Vector - The Vector representing angular velocity - - Examples - ======== - - >>> from sympy.physics.mechanics import ReferenceFrame, Vector - >>> N = ReferenceFrame('N') - >>> A = ReferenceFrame('A') - >>> V = 10 * N.x - >>> A.set_ang_vel(N, V) - >>> A.ang_vel_in(N) - 10*N.x - - """ - - value = _check_vector(value) - _check_frame(otherframe) - self._ang_vel_dict.update({otherframe: value}) - otherframe._ang_vel_dict.update({self: -value}) -
    - @property -
    [docs] def x(self): - """The basis Vector for the ReferenceFrame, in the x direction. """ - return self._x -
    - @property -
    [docs] def y(self): - """The basis Vector for the ReferenceFrame, in the y direction. """ - return self._y -
    - @property -
    [docs] def z(self): - """The basis Vector for the ReferenceFrame, in the z direction. """ - return self._z - -
    -
    [docs]class Vector(object): - """The class used to define vectors. - - It along with ReferenceFrame are the building blocks of describing a - classical mechanics system in PyDy. - - Attributes - ========== - - simp : Boolean - Let certain methods use trigsimp on their outputs - - """ - - simp = False - - def __init__(self, inlist): - """This is the constructor for the Vector class. You shouldn't be - calling this, it should only be used by other functions. You should be - treating Vectors like you would with if you were doing the math by - hand, and getting the first 3 from the standard basis vectors from a - ReferenceFrame. - - """ - - self.args = [] - while len(inlist) != 0: - added = 0 - for i, v in enumerate(self.args): - if inlist[0][1] == self.args[i][1]: - self.args[i] = (self.args[i][0] + - inlist[0][0], inlist[0][1]) - inlist.remove(inlist[0]) - added = 1 - break - if added != 1: - self.args.append(inlist[0]) - inlist.remove(inlist[0]) - i = 0 - # This code is to remove empty frames from the list - while i < len(self.args): - if self.args[i][0] == Matrix([0, 0, 0]): - self.args.remove(self.args[i]) - i -= 1 - i += 1 - - def __hash__(self): - return hash(tuple(self.args)) - - def __add__(self, other): - """The add operator for Vector. """ - other = _check_vector(other) - return Vector(self.args + other.args) - - def __and__(self, other): - """Dot product of two vectors. - - Returns a scalar, the dot product of the two Vectors - - Parameters - ========== - - other : Vector - The Vector which we are dotting with - - Examples - ======== - - >>> from sympy.physics.mechanics import ReferenceFrame, Vector, dot - >>> from sympy import symbols - >>> q1 = symbols('q1') - >>> N = ReferenceFrame('N') - >>> dot(N.x, N.x) - 1 - >>> dot(N.x, N.y) - 0 - >>> A = N.orientnew('A', 'Axis', [q1, N.x]) - >>> dot(N.y, A.y) - cos(q1) - - """ - - if isinstance(other, Dyadic): - return NotImplemented - other = _check_vector(other) - out = S(0) - for i, v1 in enumerate(self.args): - for j, v2 in enumerate(other.args): - out += ((v2[0].T) - * (v2[1].dcm(v1[1])) - * (v1[0]))[0] - if Vector.simp is True: - return trigsimp(sympify(out), recursive=True) - else: - return sympify(out) - - def __div__(self, other): - """This uses mul and inputs self and 1 divided by other. """ - return self.__mul__(1 / other) - - __truediv__ = __div__ - - def __eq__(self, other): - """Tests for equality. - - It is very import to note that this is only as good as the SymPy - equality test; False does not always mean they are not equivalent - Vectors. - If other is 0, and self is empty, returns True. - If other is 0 and self is not empty, returns False. - If none of the above, only accepts other as a Vector. - - """ - - other = _check_vector(other) - if (self.args == []) and (other.args == []): - return True - elif (self.args == []) or (other.args == []): - return False - - frame = self.args[0][1] - for v in frame: - if expand((self - other) & v) != 0: - return False - return True - - def __mul__(self, other): - """Multiplies the Vector by a sympifyable expression. - - Parameters - ========== - - other : Sympifyable - The scalar to multiply this Vector with - - Examples - ======== - - >>> from sympy.physics.mechanics import ReferenceFrame, Vector - >>> from sympy import Symbol - >>> N = ReferenceFrame('N') - >>> b = Symbol('b') - >>> V = 10 * b * N.x - >>> print(V) - 10*b*N.x - - """ - - newlist = [v for v in self.args] - for i, v in enumerate(newlist): - newlist[i] = (sympify(other) * newlist[i][0], newlist[i][1]) - return Vector(newlist) - - def __ne__(self, other): - return not self.__eq__(other) - - def __neg__(self): - return self * -1 - - def __or__(self, other): - """Outer product between two Vectors. - - A rank increasing operation, which returns a Dyadic from two Vectors - - Parameters - ========== - - other : Vector - The Vector to take the outer product with - - Examples - ======== - - >>> from sympy.physics.mechanics import ReferenceFrame, outer - >>> N = ReferenceFrame('N') - >>> outer(N.x, N.x) - (N.x|N.x) - - """ - - other = _check_vector(other) - ol = Dyadic([]) - for i, v in enumerate(self.args): - for i2, v2 in enumerate(other.args): - # it looks this way because if we are in the same frame and - # use the enumerate function on the same frame in a nested - # fashion, then bad things happen - ol += Dyadic([(v[0][0] * v2[0][0], v[1].x, v2[1].x)]) - ol += Dyadic([(v[0][0] * v2[0][1], v[1].x, v2[1].y)]) - ol += Dyadic([(v[0][0] * v2[0][2], v[1].x, v2[1].z)]) - ol += Dyadic([(v[0][1] * v2[0][0], v[1].y, v2[1].x)]) - ol += Dyadic([(v[0][1] * v2[0][1], v[1].y, v2[1].y)]) - ol += Dyadic([(v[0][1] * v2[0][2], v[1].y, v2[1].z)]) - ol += Dyadic([(v[0][2] * v2[0][0], v[1].z, v2[1].x)]) - ol += Dyadic([(v[0][2] * v2[0][1], v[1].z, v2[1].y)]) - ol += Dyadic([(v[0][2] * v2[0][2], v[1].z, v2[1].z)]) - return ol - - def _latex(self, printer=None): - """Latex Printing method. """ - ar = self.args # just to shorten things - if len(ar) == 0: - return str(0) - ol = [] # output list, to be concatenated to a string - for i, v in enumerate(ar): - for j in 0, 1, 2: - # if the coef of the basis vector is 1, we skip the 1 - if ar[i][0][j] == 1: - ol.append(' + ' + ar[i][1].latex_vecs[j]) - # if the coef of the basis vector is -1, we skip the 1 - elif ar[i][0][j] == -1: - ol.append(' - ' + ar[i][1].latex_vecs[j]) - elif ar[i][0][j] != 0: - # If the coefficient of the basis vector is not 1 or -1; - # also, we might wrap it in parentheses, for readability. - arg_str = MechanicsStrPrinter().doprint(ar[i][0][j]) - if isinstance(ar[i][0][j], Add): - arg_str = "(%s)" % arg_str - if arg_str[0] == '-': - arg_str = arg_str[1:] - str_start = ' - ' - else: - str_start = ' + ' - ol.append(str_start + arg_str + '*' + - ar[i][1].latex_vecs[j]) - outstr = ''.join(ol) - if outstr.startswith(' + '): - outstr = outstr[3:] - elif outstr.startswith(' '): - outstr = outstr[1:] - return outstr - - def _pretty(self, printer=None): - """Pretty Printing method. """ - e = self - - class Fake(object): - baseline = 0 - - def render(self, *args, **kwargs): - self = e - ar = self.args # just to shorten things - if len(ar) == 0: - return str(0) - ol = [] # output list, to be concatenated to a string - for i, v in enumerate(ar): - for j in 0, 1, 2: - # if the coef of the basis vector is 1, we skip the 1 - if ar[i][0][j] == 1: - ol.append(" + " + ar[i][1].pretty_vecs[j]) - # if the coef of the basis vector is -1, we skip the 1 - elif ar[i][0][j] == -1: - ol.append(" - " + ar[i][1].pretty_vecs[j]) - elif ar[i][0][j] != 0: - # If the basis vector coeff is not 1 or -1, - # we might wrap it in parentheses, for readability. - arg_str = (MechanicsPrettyPrinter().doprint( - ar[i][0][j])) - if isinstance(ar[i][0][j], Add): - arg_str = "(%s)" % arg_str - if arg_str[0] == "-": - arg_str = arg_str[1:] - str_start = " - " - else: - str_start = " + " - ol.append(str_start + arg_str + '*' + - ar[i][1].pretty_vecs[j]) - outstr = "".join(ol) - if outstr.startswith(" + "): - outstr = outstr[3:] - elif outstr.startswith(" "): - outstr = outstr[1:] - return outstr - return Fake() - - def __ror__(self, other): - """Outer product between two Vectors. - - A rank increasing operation, which returns a Dyadic from two Vectors - - Parameters - ========== - - other : Vector - The Vector to take the outer product with - - Examples - ======== - - >>> from sympy.physics.mechanics import ReferenceFrame, outer - >>> N = ReferenceFrame('N') - >>> outer(N.x, N.x) - (N.x|N.x) - - """ - - other = _check_vector(other) - ol = Dyadic([]) - for i, v in enumerate(other.args): - for i2, v2 in enumerate(self.args): - # it looks this way because if we are in the same frame and - # use the enumerate function on the same frame in a nested - # fashion, then bad things happen - ol += Dyadic([(v[0][0] * v2[0][0], v[1].x, v2[1].x)]) - ol += Dyadic([(v[0][0] * v2[0][1], v[1].x, v2[1].y)]) - ol += Dyadic([(v[0][0] * v2[0][2], v[1].x, v2[1].z)]) - ol += Dyadic([(v[0][1] * v2[0][0], v[1].y, v2[1].x)]) - ol += Dyadic([(v[0][1] * v2[0][1], v[1].y, v2[1].y)]) - ol += Dyadic([(v[0][1] * v2[0][2], v[1].y, v2[1].z)]) - ol += Dyadic([(v[0][2] * v2[0][0], v[1].z, v2[1].x)]) - ol += Dyadic([(v[0][2] * v2[0][1], v[1].z, v2[1].y)]) - ol += Dyadic([(v[0][2] * v2[0][2], v[1].z, v2[1].z)]) - return ol - - def __rsub__(self, other): - return (-1 * self) + other - - def __str__(self, printer=None): - """Printing method. """ - ar = self.args # just to shorten things - if len(ar) == 0: - return str(0) - ol = [] # output list, to be concatenated to a string - for i, v in enumerate(ar): - for j in 0, 1, 2: - # if the coef of the basis vector is 1, we skip the 1 - if ar[i][0][j] == 1: - ol.append(' + ' + ar[i][1].str_vecs[j]) - # if the coef of the basis vector is -1, we skip the 1 - elif ar[i][0][j] == -1: - ol.append(' - ' + ar[i][1].str_vecs[j]) - elif ar[i][0][j] != 0: - # If the coefficient of the basis vector is not 1 or -1; - # also, we might wrap it in parentheses, for readability. - arg_str = MechanicsStrPrinter().doprint(ar[i][0][j]) - if isinstance(ar[i][0][j], Add): - arg_str = "(%s)" % arg_str - if arg_str[0] == '-': - arg_str = arg_str[1:] - str_start = ' - ' - else: - str_start = ' + ' - ol.append(str_start + arg_str + '*' + ar[i][1].str_vecs[j]) - outstr = ''.join(ol) - if outstr.startswith(' + '): - outstr = outstr[3:] - elif outstr.startswith(' '): - outstr = outstr[1:] - return outstr - - def __sub__(self, other): - """The subraction operator. """ - return self.__add__(other * -1) - - def __xor__(self, other): - """The cross product operator for two Vectors. - - Returns a Vector, expressed in the same ReferenceFrames as self. - - Parameters - ========== - - other : Vector - The Vector which we are crossing with - - Examples - ======== - - >>> from sympy.physics.mechanics import ReferenceFrame, Vector - >>> from sympy import symbols - >>> q1 = symbols('q1') - >>> N = ReferenceFrame('N') - >>> N.x ^ N.y - N.z - >>> A = N.orientnew('A', 'Axis', [q1, N.x]) - >>> A.x ^ N.y - N.z - >>> N.y ^ A.x - - sin(q1)*A.y - cos(q1)*A.z - - """ - - if isinstance(other, Dyadic): - return NotImplemented - other = _check_vector(other) - if other.args == []: - return self * S(0) - - def _det(mat): - """This is needed as a little method for to find the determinant - of a list in python; needs to work for a 3x3 list. - SymPy's Matrix won't take in Vector, so need a custom function. - You shouldn't be calling this. - - """ - - return (mat[0][0] * (mat[1][1] * mat[2][2] - mat[1][2] * mat[2][1]) - + mat[0][1] * (mat[1][2] * mat[2][0] - mat[1][0] * - mat[2][2]) + mat[0][2] * (mat[1][0] * mat[2][1] - - mat[1][1] * mat[2][0])) - - outvec = Vector([]) - ar = other.args # For brevity - for i, v in enumerate(ar): - tempx = v[1].x - tempy = v[1].y - tempz = v[1].z - tempm = ([[tempx, tempy, tempz], [self & tempx, self & tempy, - self & tempz], [Vector([ar[i]]) & tempx, - Vector([ar[i]]) & tempy, Vector([ar[i]]) & tempz]]) - outvec += _det(tempm) - return outvec - - _sympystr = __str__ - _sympyrepr = _sympystr - __repr__ = __str__ - __radd__ = __add__ - __rand__ = __and__ - __rmul__ = __mul__ - -
    [docs] def dot(self, other): - return self & other
    - dot.__doc__ = __and__.__doc__ - -
    [docs] def cross(self, other): - return self ^ other
    - cross.__doc__ = __xor__.__doc__ - -
    [docs] def outer(self, other): - return self | other
    - outer.__doc__ = __or__.__doc__ - -
    [docs] def diff(self, wrt, otherframe): - """Takes the partial derivative, with respect to a value, in a frame. - - Returns a Vector. - - Parameters - ========== - - wrt : Symbol - What the partial derivative is taken with respect to. - otherframe : ReferenceFrame - The ReferenceFrame that the partial derivative is taken in. - - Examples - ======== - - >>> from sympy.physics.mechanics import ReferenceFrame, Vector, dynamicsymbols - >>> from sympy import Symbol - >>> Vector.simp = True - >>> t = Symbol('t') - >>> q1 = dynamicsymbols('q1') - >>> N = ReferenceFrame('N') - >>> A = N.orientnew('A', 'Axis', [q1, N.y]) - >>> A.x.diff(t, N) - - q1'*A.z - - """ - - wrt = sympify(wrt) - _check_frame(otherframe) - outvec = S(0) - for i, v in enumerate(self.args): - if v[1] == otherframe: - outvec += Vector([(v[0].diff(wrt), otherframe)]) - else: - if otherframe.dcm(v[1]).diff(wrt) == zeros(3, 3): - d = v[0].diff(wrt) - outvec += Vector([(d, v[1])]) - else: - d = (Vector([v]).express(otherframe)).args[0][0].diff(wrt) - outvec += Vector([(d, otherframe)]).express(v[1]) - return outvec -
    -
    [docs] def doit(self, **hints): - """Calls .doit() on each term in the Vector""" - ov = S(0) - for i, v in enumerate(self.args): - ov += Vector([(v[0].applyfunc(lambda x: x.doit(**hints)), v[1])]) - return ov -
    -
    [docs] def dt(self, otherframe): - """Returns the time derivative of the Vector in a ReferenceFrame. - - Returns a Vector which is the time derivative of the self Vector, taken - in frame otherframe. - - Parameters - ========== - - otherframe : ReferenceFrame - The ReferenceFrame that the partial derivative is taken in. - - Examples - ======== - - >>> from sympy.physics.mechanics import ReferenceFrame, Vector, dynamicsymbols - >>> from sympy import Symbol - >>> q1 = Symbol('q1') - >>> u1 = dynamicsymbols('u1') - >>> N = ReferenceFrame('N') - >>> A = N.orientnew('A', 'Axis', [q1, N.x]) - >>> v = u1 * N.x - >>> A.set_ang_vel(N, 10*A.x) - >>> A.x.dt(N) == 0 - True - >>> v.dt(N) - u1'*N.x - - """ - - outvec = S(0) - _check_frame(otherframe) - for i, v in enumerate(self.args): - if v[1] == otherframe: - outvec += Vector([(v[0].diff(dynamicsymbols._t), otherframe)]) - else: - outvec += (Vector([v]).dt(v[1]) + - (v[1].ang_vel_in(otherframe) ^ Vector([v]))) - return outvec -
    -
    [docs] def express(self, otherframe): - """Returns a vector, expressed in the other frame. - - A new Vector is returned, equalivalent to this Vector, but its - components are all defined in only the otherframe. - - Parameters - ========== - - otherframe : ReferenceFrame - The frame for this Vector to be described in - - Examples - ======== - - >>> from sympy.physics.mechanics import ReferenceFrame, Vector, dynamicsymbols - >>> q1 = dynamicsymbols('q1') - >>> N = ReferenceFrame('N') - >>> A = N.orientnew('A', 'Axis', [q1, N.y]) - >>> A.x.express(N) - cos(q1)*N.x - sin(q1)*N.z - - """ - - _check_frame(otherframe) - outvec = Vector(self.args + []) - for i, v in enumerate(self.args): - if v[1] != otherframe: - temp = otherframe.dcm(v[1]) * v[0] - for i2, v2 in enumerate(temp): - if Vector.simp is True: - temp[i2] = trigsimp(v2, recursive=True) - else: - temp[i2] = v2 - outvec += Vector([(temp, otherframe)]) - outvec -= Vector([v]) - return outvec -
    -
    [docs] def simplify(self): - """Simplify the elements in the Vector in place. """ - for i in self.args: - i[0].simplify() -
    -
    [docs] def subs(self, *args, **kwargs): - """Substituion on the Vector. - - Examples - ======== - - >>> from sympy.physics.mechanics import ReferenceFrame - >>> from sympy import Symbol - >>> N = ReferenceFrame('N') - >>> s = Symbol('s') - >>> a = N.x * s - >>> a.subs({s: 2}) - 2*N.x - - """ - - ov = S(0) - for i, v in enumerate(self.args): - ov += Vector([(v[0].subs(*args, **kwargs), v[1])]) - return ov -
    -
    [docs] def magnitude(self): - """Returns the magnitude (Euclidean norm) of self.""" - return sqrt(self & self) -
    -
    [docs] def normalize(self): - """Returns a Vector of magnitude 1, codirectional with self.""" - return Vector(self.args + []) / self.magnitude() - -
    -class MechanicsStrPrinter(StrPrinter): - """String Printer for mechanics. """ - - def _print_Derivative(self, e): - t = dynamicsymbols._t - if (bool(sum([i == t for i in e.variables])) & - isinstance(type(e.args[0]), UndefinedFunction)): - ol = str(e.args[0].func) - for i, v in enumerate(e.variables): - ol += dynamicsymbols._str - return ol - else: - return StrPrinter().doprint(e) - - def _print_Function(self, e): - t = dynamicsymbols._t - if isinstance(type(e), UndefinedFunction): - return StrPrinter().doprint(e).replace("(%s)" % t, '') - return e.func.__name__ + "(%s)" % self.stringify(e.args, ", ") - - -class MechanicsLatexPrinter(LatexPrinter): - """Latex Printer for mechanics. """ - - def _print_Function(self, expr, exp=None): - func = expr.func.__name__ - t = dynamicsymbols._t - - if hasattr(self, '_print_' + func): - return getattr(self, '_print_' + func)(expr, exp) - elif isinstance(type(expr), UndefinedFunction) and (expr.args == (t,)): - name, sup, sub = split_super_sub(func) - if len(sup) != 0: - sup = r"^{%s}" % "".join(sup) - else: - sup = r"" - if len(sub) != 0: - sub = r"_{%s}" % "".join(sub) - else: - sub = r"" - return r"%s" % (name + sup + sub) - 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: - name = r"\operatorname{%s}^{-1}" % func - elif exp is not None: - name = r"\operatorname{%s}^{%s}" % (func, exp) - else: - name = r"\operatorname{%s}" % func - - if can_fold_brackets: - name += r"%s" - else: - name += r"\left(%s\right)" - - if inv_trig_power_case and exp is not None: - name += r"^{%s}" % exp - - return name % ",".join(args) - - def _print_Derivative(self, der_expr): - # make sure it is an the right form - der_expr = der_expr.doit() - if not isinstance(der_expr, Derivative): - return self.doprint(der_expr) - - # check if expr is a dynamicsymbol - from sympy.core.function import AppliedUndef - t = dynamicsymbols._t - expr = der_expr.expr - red = expr.atoms(AppliedUndef) - syms = der_expr.variables - test1 = not all([True for i in red if i.atoms() == set([t])]) - test2 = not all([(t == i) for i in syms]) - if test1 or test2: - return LatexPrinter().doprint(der_expr) - - # done checking - dots = len(syms) - base = self._print_Function(expr) - base_split = base.split('_', 1) - base = base_split[0] - if dots == 1: - base = r"\dot{%s}" % base - elif dots == 2: - base = r"\ddot{%s}" % base - elif dots == 3: - base = r"\dddot{%s}" % base - if len(base_split) is not 1: - base += '_' + base_split[1] - return base - - -class MechanicsPrettyPrinter(PrettyPrinter): - """Pretty Printer for mechanics. """ - - def _print_Derivative(self, deriv): - # XXX use U('PARTIAL DIFFERENTIAL') here ? - t = dynamicsymbols._t - dots = 0 - can_break = True - syms = list(reversed(deriv.variables)) - x = None - - while len(syms) > 0: - if syms[-1] == t: - syms.pop() - dots += 1 - else: - break - - f = prettyForm(binding=prettyForm.FUNC, *self._print(deriv.expr)) - if not (isinstance(type(deriv.expr), UndefinedFunction) - and (deriv.expr.args == (t,))): - dots = 0 - can_break = False - f = prettyForm(binding=prettyForm.FUNC, - *self._print(deriv.expr).parens()) - - if dots == 0: - dots = "" - elif dots == 1: - dots = "\u0307" - elif dots == 2: - dots = "\u0308" - elif dots == 3: - dots = "\u20db" - elif dots == 4: - dots = "\u20dc" - - uni_subs = ["\u2080", "\u2081", "\u2082", "\u2083", "\u2084", - "\u2085", "\u2086", "\u2087", "\u2088", "\u2089", - "\u208a", "\u208b", "\u208c", "\u208d", "\u208e", - "\u208f", "\u2090", "\u2091", "\u2092", "\u2093", - "\u2094", "\u2095", "\u2096", "\u2097", "\u2098", - "\u2099", "\u209a", "\u209b", "\u209c", "\u209d", - "\u209e", "\u209f"] - - fpic = f.__dict__['picture'] - funi = f.__dict__['unicode'] - ind = len(funi) - val = "" - - for i in uni_subs: - cur_ind = funi.find(i) - if (cur_ind != -1) and (cur_ind < ind): - ind = cur_ind - val = i - if ind == len(funi): - funi += dots - else: - funi = funi.replace(val, dots + val) - - if f.__dict__['picture'] == [f.__dict__['unicode']]: - fpic = [funi] - f.__dict__['picture'] = fpic - f.__dict__['unicode'] = funi - - if (len(syms)) == 0 and can_break: - return f - - for sym, num in group(syms, multiple=False): - s = self._print(sym) - ds = prettyForm(*s.left('d')) - - if num > 1: - ds = ds**prettyForm(str(num)) - - if x is None: - x = ds - else: - x = prettyForm(*x.right(' ')) - x = prettyForm(*x.right(ds)) - pform = prettyForm('d') - if len(syms) > 1: - pform = pform**prettyForm(str(len(syms))) - pform = prettyForm(*pform.below(stringPict.LINE, x)) - pform.baseline = pform.baseline + 1 - pform = prettyForm(*stringPict.next(pform, f)) - return pform - - def _print_Function(self, e): - t = dynamicsymbols._t - # XXX works only for applied functions - func = e.func - args = e.args - func_name = func.__name__ - prettyFunc = self._print(C.Symbol(func_name)) - prettyArgs = prettyForm(*self._print_seq(args).parens()) - # If this function is an Undefined function of t, it is probably a - # dynamic symbol, so we'll skip the (t). The rest of the code is - # identical to the normal PrettyPrinter code - if isinstance(func, UndefinedFunction) and (args == (t,)): - pform = prettyForm(binding=prettyForm.FUNC, - *stringPict.next(prettyFunc)) - else: - pform = prettyForm(binding=prettyForm.FUNC, - *stringPict.next(prettyFunc, prettyArgs)) - # store pform parts so it can be reassembled e.g. when powered - pform.prettyFunc = prettyFunc - pform.prettyArgs = prettyArgs - return pform - - -def _check_dyadic(other): - if not isinstance(other, Dyadic): - other = sympify(other) - if other != S(0): - raise TypeError('A Dyadic must be supplied') - else: - other = Dyadic([]) - return other - - -def _check_frame(other): - if not isinstance(other, ReferenceFrame): - raise TypeError('A ReferenceFrame must be supplied') - - -def _check_vector(other): - if not isinstance(other, Vector): - other = sympify(other) - if other != S(0): - raise TypeError('A Vector must be supplied') - else: - other = Vector([]) - return other - - -
    [docs]def dynamicsymbols(names, level=0): - """Uses symbols and Function for functions of time. - - Creates a SymPy UndefinedFunction, which is then initialized as a function - of a variable, the default being Symbol('t'). - - Parameters - ========== - - names : str - Names of the dynamic symbols you want to create; works the same way as - inputs to symbols - level : int - Level of differentiation of the returned function; d/dt once of t, - twice of t, etc. - - Examples - ======= - - >>> from sympy.physics.mechanics import dynamicsymbols - >>> from sympy import diff, Symbol - >>> q1 = dynamicsymbols('q1') - >>> q1 - q1(t) - >>> diff(q1, Symbol('t')) - Derivative(q1(t), t) - - """ - - esses = symbols(names, cls=Function) - t = dynamicsymbols._t - if hasattr(esses, '__iter__'): - esses = [reduce(diff, [t]*level, e(t)) for e in esses] - return esses - else: - return reduce(diff, [t]*level, esses(t)) -
    -dynamicsymbols._t = Symbol('t') -dynamicsymbols._str = '\'' -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/mechanics/functions.html b/dev-py3k/_modules/sympy/physics/mechanics/functions.html deleted file mode 100644 index b118736e91a..00000000000 --- a/dev-py3k/_modules/sympy/physics/mechanics/functions.html +++ /dev/null @@ -1,872 +0,0 @@ - - - - - - - - - - sympy.physics.mechanics.functions — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.mechanics.functions

    -__all__ = ['cross',
    -           'dot',
    -           'express',
    -           'outer',
    -           'inertia',
    -           'mechanics_printing',
    -           'mprint',
    -           'mpprint',
    -           'mlatex',
    -           'kinematic_equations',
    -           'inertia_of_point_mass',
    -           'partial_velocity',
    -           'linear_momentum',
    -           'angular_momentum',
    -           'kinetic_energy',
    -           'potential_energy',
    -           'Lagrangian']
    -
    -from sympy.physics.mechanics.essential import (Vector, Dyadic, ReferenceFrame,
    -                                               MechanicsStrPrinter,
    -                                               MechanicsPrettyPrinter,
    -                                               MechanicsLatexPrinter,
    -                                               dynamicsymbols)
    -from sympy.physics.mechanics.particle import Particle
    -from sympy.physics.mechanics.rigidbody import RigidBody
    -from sympy.physics.mechanics.point import Point
    -from sympy import sympify, diff, sin, cos, Matrix
    -from sympy.core.basic import S
    -
    -
    -
    [docs]def cross(vec1, vec2): - """Cross product convenience wrapper for Vector.cross(): \n""" - if not isinstance(vec1, (Vector, Dyadic)): - raise TypeError('Cross product is between two vectors') - return vec1 ^ vec2
    -cross.__doc__ += Vector.cross.__doc__ - - -
    [docs]def dot(vec1, vec2): - """Dot product convenience wrapper for Vector.dot(): \n""" - if not isinstance(vec1, (Vector, Dyadic)): - raise TypeError('Dot product is between two vectors') - return vec1 & vec2
    -dot.__doc__ += Vector.dot.__doc__ - - -
    [docs]def express(vec, frame, frame2=None): - """Express convenience wrapper for Vector.express(): \n""" - if not isinstance(vec, (Vector, Dyadic)): - raise TypeError('Can only express Vectors') - if isinstance(vec, Vector): - return vec.express(frame) - else: - return vec.express(frame, frame2) -
    -express.__doc__ += Vector.express.__doc__ - - -
    [docs]def outer(vec1, vec2): - """Outer prodcut convenience wrapper for Vector.outer():\n""" - if not isinstance(vec1, Vector): - raise TypeError('Outer product is between two Vectors') - return vec1 | vec2
    -outer.__doc__ += Vector.express.__doc__ - - -
    [docs]def inertia(frame, ixx, iyy, izz, ixy=0, iyz=0, izx=0): - """Simple way to create inertia Dyadic object. - - If you don't know what a Dyadic is, just treat this like the inertia - tensor. Then, do the easy thing and define it in a body-fixed frame. - - Parameters - ========== - - frame : ReferenceFrame - The frame the inertia is defined in - ixx : Sympifyable - the xx element in the inertia dyadic - iyy : Sympifyable - the yy element in the inertia dyadic - izz : Sympifyable - the zz element in the inertia dyadic - ixy : Sympifyable - the xy element in the inertia dyadic - iyz : Sympifyable - the yz element in the inertia dyadic - izx : Sympifyable - the zx element in the inertia dyadic - - Examples - ======== - - >>> from sympy.physics.mechanics import ReferenceFrame, inertia - >>> N = ReferenceFrame('N') - >>> inertia(N, 1, 2, 3) - (N.x|N.x) + 2*(N.y|N.y) + 3*(N.z|N.z) - - """ - - if not isinstance(frame, ReferenceFrame): - raise TypeError('Need to define the inertia in a frame') - ol = sympify(ixx) * (frame.x | frame.x) - ol += sympify(ixy) * (frame.x | frame.y) - ol += sympify(izx) * (frame.x | frame.z) - ol += sympify(ixy) * (frame.y | frame.x) - ol += sympify(iyy) * (frame.y | frame.y) - ol += sympify(iyz) * (frame.y | frame.z) - ol += sympify(izx) * (frame.z | frame.x) - ol += sympify(iyz) * (frame.z | frame.y) - ol += sympify(izz) * (frame.z | frame.z) - return ol - -
    -
    [docs]def inertia_of_point_mass(mass, pos_vec, frame): - """Inertia dyadic of a point mass realtive to point O. - - Parameters - ========== - - mass : Sympifyable - Mass of the point mass - pos_vec : Vector - Position from point O to point mass - frame : ReferenceFrame - Reference frame to express the dyadic in - - Examples - ======== - - >>> from sympy import symbols - >>> from sympy.physics.mechanics import ReferenceFrame, inertia_of_point_mass - >>> N = ReferenceFrame('N') - >>> r, m = symbols('r m') - >>> px = r * N.x - >>> inertia_of_point_mass(m, px, N) - m*r**2*(N.y|N.y) + m*r**2*(N.z|N.z) - - """ - - return mass * (((frame.x | frame.x) + (frame.y | frame.y) + - (frame.z | frame.z)) * (pos_vec & pos_vec) - - (pos_vec | pos_vec)) - -
    -
    [docs]def mechanics_printing(): - """Sets up interactive printing for mechanics' derivatives. - - The main benefit of this is for printing of time derivatives; - instead of displaying as Derivative(f(t),t), it will display f' - This is only actually needed for when derivatives are present and are not - in a physics.mechanics object. - - Examples - ======== - - >>> # 2 lines below are for tests to function properly - >>> import sys - >>> sys.displayhook = sys.__displayhook__ - >>> from sympy import Function, Symbol, diff - >>> from sympy.physics.mechanics import mechanics_printing - >>> f = Function('f') - >>> t = Symbol('t') - >>> x = Symbol('x') - >>> diff(f(t), t) - Derivative(f(t), t) - >>> mechanics_printing() - >>> diff(f(t), t) - f' - >>> diff(f(x), x) - Derivative(f(x), x) - >>> # 2 lines below are for tests to function properly - >>> import sys - >>> sys.displayhook = sys.__displayhook__ - - """ - - import sys - sys.displayhook = mprint - -
    -
    [docs]def mprint(expr, **settings): - r"""Function for printing of expressions generated in mechanics. - - Extends SymPy's StrPrinter; mprint is equivalent to: - print sstr() - mprint takes the same options as sstr. - - Parameters - ========== - - expr : valid sympy object - SymPy expression to print - settings : args - Same as print for SymPy - - Examples - ======== - - >>> from sympy.physics.mechanics import mprint, dynamicsymbols - >>> u1 = dynamicsymbols('u1') - >>> print(u1) - u1(t) - >>> mprint(u1) - u1 - - """ - - pr = MechanicsStrPrinter(settings) - outstr = pr.doprint(expr) - - import builtins - if (outstr != 'None'): - builtins._ = outstr - print(outstr) - -
    -
    [docs]def mpprint(expr, **settings): - r"""Function for pretty printing of expressions generated in mechanics. - - Mainly used for expressions not inside a vector; the output of running - scripts and generating equations of motion. Takes the same options as - SymPy's pretty_print(); see that function for more information. - - Parameters - ========== - - expr : valid sympy object - SymPy expression to pretty print - settings : args - Same as pretty print - - Examples - ======== - - Use in the same way as pprint - - """ - - mp = MechanicsPrettyPrinter(settings) - print((mp.doprint(expr))) - -
    -
    [docs]def mlatex(expr, **settings): - r"""Function for printing latex representation of mechanics objects. - - For latex representation of Vectors, Dyadics, and dynamicsymbols. Takes the - same options as SymPy's latex(); see that function for more information; - - Parameters - ========== - - expr : valid sympy object - SymPy expression to represent in LaTeX form - settings : args - Same as latex() - - Examples - ======== - - >>> from sympy.physics.mechanics import mlatex, ReferenceFrame, dynamicsymbols - >>> N = ReferenceFrame('N') - >>> q1, q2 = dynamicsymbols('q1 q2') - >>> q1d, q2d = dynamicsymbols('q1 q2', 1) - >>> q1dd, q2dd = dynamicsymbols('q1 q2', 2) - >>> mlatex(N.x + N.y) - '\\mathbf{\\hat{n}_x} + \\mathbf{\\hat{n}_y}' - >>> mlatex(q1 + q2) - 'q_{1} + q_{2}' - >>> mlatex(q1d) - '\\dot{q}_{1}' - >>> mlatex(q1 * q2d) - 'q_{1} \\dot{q}_{2}' - >>> mlatex(q1dd * q1 / q1d) - '\\frac{q_{1} \\ddot{q}_{1}}{\\dot{q}_{1}}' - - """ - - return MechanicsLatexPrinter(settings).doprint(expr) - -
    -
    [docs]def kinematic_equations(speeds, coords, rot_type, rot_order=''): - """Gives equations relating the qdot's to u's for a rotation type. - - Supply rotation type and order as in orient. Speeds are assumed to be - body-fixed; if we are defining the orientation of B in A using by rot_type, - the angular velocity of B in A is assumed to be in the form: speed[0]*B.x + - speed[1]*B.y + speed[2]*B.z - - Parameters - ========== - - speeds : list of length 3 - The body fixed angular velocity measure numbers. - coords : list of length 3 or 4 - The coordinates used to define the orientation of the two frames. - rot_type : str - The type of rotation used to create the equations. Body, Space, or - Quaternion only - rot_order : str - If applicable, the order of a series of rotations. - - Examples - ======== - - >>> from sympy.physics.mechanics import dynamicsymbols - >>> from sympy.physics.mechanics import kinematic_equations, mprint - >>> u1, u2, u3 = dynamicsymbols('u1 u2 u3') - >>> q1, q2, q3 = dynamicsymbols('q1 q2 q3') - >>> mprint(kinematic_equations([u1,u2,u3], [q1,q2,q3], 'body', '313'), - ... order=None) - [-(u1*sin(q3) + u2*cos(q3))/sin(q2) + q1', -u1*cos(q3) + u2*sin(q3) + q2', (u1*sin(q3) + u2*cos(q3))*cos(q2)/sin(q2) - u3 + q3'] - - """ - - # Code below is checking and sanitizing input - approved_orders = ('123', '231', '312', '132', '213', '321', '121', '131', - '212', '232', '313', '323', '1', '2', '3', '') - rot_order = str(rot_order).upper() # Now we need to make sure XYZ = 123 - rot_type = rot_type.upper() - rot_order = [i.replace('X', '1') for i in rot_order] - rot_order = [i.replace('Y', '2') for i in rot_order] - rot_order = [i.replace('Z', '3') for i in rot_order] - rot_order = ''.join(rot_order) - - if not isinstance(speeds, (list, tuple)): - raise TypeError('Need to supply speeds in a list') - if len(speeds) != 3: - raise TypeError('Need to supply 3 body-fixed speeds') - if not isinstance(coords, (list, tuple)): - raise TypeError('Need to supply coordinates in a list') - if rot_type.lower() in ['body', 'space']: - if rot_order not in approved_orders: - raise ValueError('Not an acceptable rotation order') - if len(coords) != 3: - raise ValueError('Need 3 coordinates for body or space') - # Actual hard-coded kinematic differential equations - q1, q2, q3 = coords - q1d, q2d, q3d = [diff(i, dynamicsymbols._t) for i in coords] - w1, w2, w3 = speeds - s1, s2, s3 = [sin(q1), sin(q2), sin(q3)] - c1, c2, c3 = [cos(q1), cos(q2), cos(q3)] - if rot_type.lower() == 'body': - if rot_order == '123': - return [q1d - (w1 * c3 - w2 * s3) / c2, q2d - w1 * s3 - w2 * - c3, q3d - (-w1 * c3 + w2 * s3) * s2 / c2 - w3] - if rot_order == '231': - return [q1d - (w2 * c3 - w3 * s3) / c2, q2d - w2 * s3 - w3 * - c3, q3d - w1 - (- w2 * c3 + w3 * s3) * s2 / c2] - if rot_order == '312': - return [q1d - (-w1 * s3 + w3 * c3) / c2, q2d - w1 * c3 - w3 * - s3, q3d - (w1 * s3 - w3 * c3) * s2 / c2 - w2] - if rot_order == '132': - return [q1d - (w1 * c3 + w3 * s3) / c2, q2d + w1 * s3 - w3 * - c3, q3d - (w1 * c3 + w3 * s3) * s2 / c2 - w2] - if rot_order == '213': - return [q1d - (w1 * s3 + w2 * c3) / c2, q2d - w1 * c3 + w2 * - s3, q3d - (w1 * s3 + w2 * c3) * s2 / c2 - w3] - if rot_order == '321': - return [q1d - (w2 * s3 + w3 * c3) / c2, q2d - w2 * c3 + w3 * - s3, q3d - w1 - (w2 * s3 + w3 * c3) * s2 / c2] - if rot_order == '121': - return [q1d - (w2 * s3 + w3 * c3) / s2, q2d - w2 * c3 + w3 * - s3, q3d - w1 + (w2 * s3 + w3 * c3) * c2 / s2] - if rot_order == '131': - return [q1d - (-w2 * c3 + w3 * s3) / s2, q2d - w2 * s3 - w3 * - c3, q3d - w1 - (w2 * c3 - w3 * s3) * c2 / s2] - if rot_order == '212': - return [q1d - (w1 * s3 - w3 * c3) / s2, q2d - w1 * c3 - w3 * - s3, q3d - (-w1 * s3 + w3 * c3) * c2 / s2 - w2] - if rot_order == '232': - return [q1d - (w1 * c3 + w3 * s3) / s2, q2d + w1 * s3 - w3 * - c3, q3d + (w1 * c3 + w3 * s3) * c2 / s2 - w2] - if rot_order == '313': - return [q1d - (w1 * s3 + w2 * c3) / s2, q2d - w1 * c3 + w2 * - s3, q3d + (w1 * s3 + w2 * c3) * c2 / s2 - w3] - if rot_order == '323': - return [q1d - (-w1 * c3 + w2 * s3) / s2, q2d - w1 * s3 - w2 * - c3, q3d - (w1 * c3 - w2 * s3) * c2 / s2 - w3] - if rot_type.lower() == 'space': - if rot_order == '123': - return [q1d - w1 - (w2 * s1 + w3 * c1) * s2 / c2, q2d - w2 * - c1 + w3 * s1, q3d - (w2 * s1 + w3 * c1) / c2] - if rot_order == '231': - return [q1d - (w1 * c1 + w3 * s1) * s2 / c2 - w2, q2d + w1 * - s1 - w3 * c1, q3d - (w1 * c1 + w3 * s1) / c2] - if rot_order == '312': - return [q1d - (w1 * s1 + w2 * c1) * s2 / c2 - w3, q2d - w1 * - c1 + w2 * s1, q3d - (w1 * s1 + w2 * c1) / c2] - if rot_order == '132': - return [q1d - w1 - (-w2 * c1 + w3 * s1) * s2 / c2, q2d - w2 * - s1 - w3 * c1, q3d - (w2 * c1 - w3 * s1) / c2] - if rot_order == '213': - return [q1d - (w1 * s1 - w3 * c1) * s2 / c2 - w2, q2d - w1 * - c1 - w3 * s1, q3d - (-w1 * s1 + w3 * c1) / c2] - if rot_order == '321': - return [q1d - (-w1 * c1 + w2 * s1) * s2 / c2 - w3, q2d - w1 * - s1 - w2 * c1, q3d - (w1 * c1 - w2 * s1) / c2] - if rot_order == '121': - return [q1d - w1 + (w2 * s1 + w3 * c1) * c2 / s2, q2d - w2 * - c1 + w3 * s1, q3d - (w2 * s1 + w3 * c1) / s2] - if rot_order == '131': - return [q1d - w1 - (w2 * c1 - w3 * s1) * c2 / s2, q2d - w2 * - s1 - w3 * c1, q3d - (-w2 * c1 + w3 * s1) / s2] - if rot_order == '212': - return [q1d - (-w1 * s1 + w3 * c1) * c2 / s2 - w2, q2d - w1 * - c1 - w3 * s1, q3d - (w1 * s1 - w3 * c1) / s2] - if rot_order == '232': - return [q1d + (w1 * c1 + w3 * s1) * c2 / s2 - w2, q2d + w1 * - s1 - w3 * c1, q3d - (w1 * c1 + w3 * s1) / s2] - if rot_order == '313': - return [q1d + (w1 * s1 + w2 * c1) * c2 / s2 - w3, q2d - w1 * - c1 + w2 * s1, q3d - (w1 * s1 + w2 * c1) / s2] - if rot_order == '323': - return [q1d - (w1 * c1 - w2 * s1) * c2 / s2 - w3, q2d - w1 * - s1 - w2 * c1, q3d - (-w1 * c1 + w2 * s1) / s2] - elif rot_type.lower() == 'quaternion': - if rot_order != '': - raise ValueError('Cannot have rotation order for quaternion') - if len(coords) != 4: - raise ValueError('Need 4 coordinates for quaternion') - # Actual hard-coded kinematic differential equations - e0, e1, e2, e3 = coords - w = Matrix(speeds + [0]) - E = Matrix([[e0, -e3, e2, e1], [e3, e0, -e1, e2], [-e2, e1, e0, e3], - [-e1, -e2, -e3, e0]]) - edots = Matrix([diff(i, dynamicsymbols._t) for i in [e1, e2, e3, e0]]) - return list(edots.T - 0.5 * w.T * E.T) - else: - raise ValueError('Not an approved rotation type for this function') - -
    -
    [docs]def partial_velocity(vel_list, u_list, frame): - """Returns a list of partial velocities. - - For a list of velocity or angular velocity vectors the partial derivatives - with respect to the supplied generalized speeds are computed, in the - specified ReferenceFrame. - - The output is a list of lists. The outer list has a number of elements - equal to the number of supplied velocity vectors. The inner lists are, for - each velocity vector, the partial derivatives of that velocity vector with - respect to the generalized speeds supplied. - - Parameters - ========== - - vel_list : list - List of velocities of Point's and angular velocities of ReferenceFrame's - u_list : list - List of independent generalized speeds. - frame : ReferenceFrame - The ReferenceFrame the partial derivatives are going to be taken in. - - Examples - ======== - - >>> from sympy.physics.mechanics import Point, ReferenceFrame - >>> from sympy.physics.mechanics import dynamicsymbols - >>> from sympy.physics.mechanics import partial_velocity - >>> u = dynamicsymbols('u') - >>> N = ReferenceFrame('N') - >>> P = Point('P') - >>> P.set_vel(N, u * N.x) - >>> vel_list = [P.vel(N)] - >>> u_list = [u] - >>> partial_velocity(vel_list, u_list, N) - [[N.x]] - - """ - if not hasattr(vel_list, '__iter__'): - raise TypeError('Provide velocities in an iterable') - if not hasattr(u_list, '__iter__'): - raise TypeError('Provide speeds in an iterable') - list_of_pvlists = [] - for i in vel_list: - pvlist = [] - for j in u_list: - vel = i.diff(j, frame) - pvlist += [vel] - list_of_pvlists += [pvlist] - return list_of_pvlists - -
    -
    [docs]def linear_momentum(frame, *body): - """Linear momentum of the system. - - This function returns the linear momentum of a system of Particle's and/or - RigidBody's. The linear momentum of a system is equal to the vector sum of - the linear momentum of its constituents. Consider a system, S, comprised of - a rigid body, A, and a particle, P. The linear momentum of the system, L, - is equal to the vector sum of the linear momentum of the particle, L1, and - the linear momentum of the rigid body, L2, i.e- - - L = L1 + L2 - - Parameters - ========== - - frame : ReferenceFrame - The frame in which linear momentum is desired. - body1, body2, body3... : Particle and/or RigidBody - The body (or bodies) whose kinetic energy is required. - - Examples - ======== - - >>> from sympy.physics.mechanics import Point, Particle, ReferenceFrame - >>> from sympy.physics.mechanics import RigidBody, outer, linear_momentum - >>> N = ReferenceFrame('N') - >>> P = Point('P') - >>> P.set_vel(N, 10 * N.x) - >>> Pa = Particle('Pa', P, 1) - >>> Ac = Point('Ac') - >>> Ac.set_vel(N, 25 * N.y) - >>> I = outer(N.x, N.x) - >>> A = RigidBody('A', Ac, N, 20, (I, Ac)) - >>> linear_momentum(N, A, Pa) - 10*N.x + 500*N.y - - """ - - if not isinstance(frame, ReferenceFrame): - raise TypeError('Please specify a valid ReferenceFrame') - else: - linear_momentum_sys = S(0) - for e in body: - if isinstance(e, (RigidBody, Particle)): - linear_momentum_sys += e.linear_momentum(frame) - else: - raise TypeError('*body must have only Particle or RigidBody') - return linear_momentum_sys - -
    -
    [docs]def angular_momentum(point, frame, *body): - """Angular momentum of a system - - This function returns the angular momentum of a system of Particle's and/or - RigidBody's. The angular momentum of such a system is equal to the vector - sum of the angular momentum of its constituents. Consider a system, S, - comprised of a rigid body, A, and a particle, P. The angular momentum of - the system, H, is equal to the vector sum of the linear momentum of the - particle, H1, and the linear momentum of the rigid body, H2, i.e- - - H = H1 + H2 - - Parameters - ========== - - point : Point - The point about which angular momentum of the system is desired. - frame : ReferenceFrame - The frame in which angular momentum is desired. - body1, body2, body3... : Particle and/or RigidBody - The body (or bodies) whose kinetic energy is required. - - Examples - ======== - - >>> from sympy.physics.mechanics import Point, Particle, ReferenceFrame - >>> from sympy.physics.mechanics import RigidBody, outer, angular_momentum - >>> N = ReferenceFrame('N') - >>> O = Point('O') - >>> O.set_vel(N, 0 * N.x) - >>> P = O.locatenew('P', 1 * N.x) - >>> P.set_vel(N, 10 * N.x) - >>> Pa = Particle('Pa', P, 1) - >>> Ac = O.locatenew('Ac', 2 * N.y) - >>> Ac.set_vel(N, 5 * N.y) - >>> a = ReferenceFrame('a') - >>> a.set_ang_vel(N, 10 * N.z) - >>> I = outer(N.z, N.z) - >>> A = RigidBody('A', Ac, a, 20, (I, Ac)) - >>> angular_momentum(O, N, Pa, A) - 10*N.z - - """ - - if not isinstance(frame, ReferenceFrame): - raise TypeError('Please enter a valid ReferenceFrame') - if not isinstance(point, Point): - raise TypeError('Please specify a valid Point') - else: - angular_momentum_sys = S(0) - for e in body: - if isinstance(e, (RigidBody, Particle)): - angular_momentum_sys += e.angular_momentum(point, frame) - else: - raise TypeError('*body must have only Particle or RigidBody') - return angular_momentum_sys - -
    -
    [docs]def kinetic_energy(frame, *body): - """Kinetic energy of a multibody system. - - This function returns the kinetic energy of a system of Particle's and/or - RigidBody's. The kinetic energy of such a system is equal to the sum of - the kinetic energies of its constituents. Consider a system, S, comprising - a rigid body, A, and a particle, P. The kinetic energy of the system, T, - is equal to the vector sum of the kinetic energy of the particle, T1, and - the kinetic energy of the rigid body, T2, i.e. - - T = T1 + T2 - - Kinetic energy is a scalar. - - Parameters - ========== - - frame : ReferenceFrame - The frame in which the velocity or angular velocity of the body is - defined. - body1, body2, body3... : Particle and/or RigidBody - The body (or bodies) whose kinetic energy is required. - - Examples - ======== - - >>> from sympy.physics.mechanics import Point, Particle, ReferenceFrame - >>> from sympy.physics.mechanics import RigidBody, outer, kinetic_energy - >>> N = ReferenceFrame('N') - >>> O = Point('O') - >>> O.set_vel(N, 0 * N.x) - >>> P = O.locatenew('P', 1 * N.x) - >>> P.set_vel(N, 10 * N.x) - >>> Pa = Particle('Pa', P, 1) - >>> Ac = O.locatenew('Ac', 2 * N.y) - >>> Ac.set_vel(N, 5 * N.y) - >>> a = ReferenceFrame('a') - >>> a.set_ang_vel(N, 10 * N.z) - >>> I = outer(N.z, N.z) - >>> A = RigidBody('A', Ac, a, 20, (I, Ac)) - >>> kinetic_energy(N, Pa, A) - 350 - - """ - - if not isinstance(frame, ReferenceFrame): - raise TypeError('Please enter a valid ReferenceFrame') - ke_sys = S(0) - for e in body: - if isinstance(e, (RigidBody, Particle)): - ke_sys += e.kinetic_energy(frame) - else: - raise TypeError('*body must have only Particle or RigidBody') - return ke_sys - -
    -
    [docs]def potential_energy(*body): - """Potential energy of a multibody system. - - This function returns the potential energy of a system of Particle's and/or - RigidBody's. The potential energy of such a system is equal to the sum of - the potential energy of its constituents. Consider a system, S, comprising - a rigid body, A, and a particle, P. The potential energy of the system, V, - is equal to the vector sum of the potential energy of the particle, V1, and - the potential energy of the rigid body, V2, i.e. - - V = V1 + V2 - - Potential energy is a scalar. - - Parameters - ========== - - body1, body2, body3... : Particle and/or RigidBody - The body (or bodies) whose potential energy is required. - - Examples - ======== - - >>> from sympy.physics.mechanics import Point, Particle, ReferenceFrame - >>> from sympy.physics.mechanics import RigidBody, outer, potential_energy - >>> from sympy import symbols - >>> M, m, g, h = symbols('M m g h') - >>> N = ReferenceFrame('N') - >>> O = Point('O') - >>> O.set_vel(N, 0 * N.x) - >>> P = O.locatenew('P', 1 * N.x) - >>> Pa = Particle('Pa', P, m) - >>> Ac = O.locatenew('Ac', 2 * N.y) - >>> a = ReferenceFrame('a') - >>> I = outer(N.z, N.z) - >>> A = RigidBody('A', Ac, a, M, (I, Ac)) - >>> Pa.set_potential_energy(m * g * h) - >>> A.set_potential_energy(M * g * h) - >>> potential_energy(Pa, A) - M*g*h + g*h*m - - """ - - pe_sys = S(0) - for e in body: - if isinstance(e, (RigidBody, Particle)): - pe_sys += e.potential_energy - else: - raise TypeError('*body must have only Particle or RigidBody') - return pe_sys - -
    -
    [docs]def Lagrangian(frame, *body): - """Lagrangian of a multibody system. - - This function returns the Lagrangian of a system of Particle's and/or - RigidBody's. The Lagrangian of such a system is equal to the difference - between the kinetic energies and potential energies of its constituents. If - T and V are the kinetic and potential energies of a system then it's - Lagrangian, L, is defined as - - L = T - V - - The Lagrangian is a scalar. - - Parameters - ========== - - frame : ReferenceFrame - The frame in which the velocity or angular velocity of the body is - defined to determine the kinetic energy. - - body1, body2, body3... : Particle and/or RigidBody - The body (or bodies) whose kinetic energy is required. - - Examples - ======== - - >>> from sympy.physics.mechanics import Point, Particle, ReferenceFrame - >>> from sympy.physics.mechanics import RigidBody, outer, Lagrangian - >>> from sympy import symbols - >>> M, m, g, h = symbols('M m g h') - >>> N = ReferenceFrame('N') - >>> O = Point('O') - >>> O.set_vel(N, 0 * N.x) - >>> P = O.locatenew('P', 1 * N.x) - >>> P.set_vel(N, 10 * N.x) - >>> Pa = Particle('Pa', P, 1) - >>> Ac = O.locatenew('Ac', 2 * N.y) - >>> Ac.set_vel(N, 5 * N.y) - >>> a = ReferenceFrame('a') - >>> a.set_ang_vel(N, 10 * N.z) - >>> I = outer(N.z, N.z) - >>> A = RigidBody('A', Ac, a, 20, (I, Ac)) - >>> Pa.set_potential_energy(m * g * h) - >>> A.set_potential_energy(M * g * h) - >>> Lagrangian(N, Pa, A) - -M*g*h - g*h*m + 350 - - """ - - if not isinstance(frame, ReferenceFrame): - raise TypeError('Please supply a valid ReferenceFrame') - for e in body: - if not isinstance(e, (RigidBody, Particle)): - raise TypeError('*body must have only Particle or RigidBody') - return kinetic_energy(frame, *body) - potential_energy(*body)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/mechanics/kane.html b/dev-py3k/_modules/sympy/physics/mechanics/kane.html deleted file mode 100644 index c97d3542959..00000000000 --- a/dev-py3k/_modules/sympy/physics/mechanics/kane.html +++ /dev/null @@ -1,964 +0,0 @@ - - - - - - - - - - sympy.physics.mechanics.kane — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.mechanics.kane

    -__all__ = ['KanesMethod']
    -
    -from sympy import Symbol, zeros, Matrix, diff, solve_linear_system_LU, eye
    -from sympy.utilities import default_sort_key
    -from sympy.physics.mechanics.essential import ReferenceFrame, dynamicsymbols
    -from sympy.physics.mechanics.particle import Particle
    -from sympy.physics.mechanics.point import Point
    -from sympy.physics.mechanics.rigidbody import RigidBody
    -from sympy.physics.mechanics.functions import (inertia_of_point_mass,
    -                                               partial_velocity)
    -from functools import reduce
    -
    -
    -
    [docs]class KanesMethod(object): - """Kane's method object. - - This object is used to do the "book-keeping" as you go through and form - equations of motion in the way Kane presents in: - Kane, T., Levinson, D. Dynamics Theory and Applications. 1985 McGraw-Hill - - The attributes are for equations in the form [M] udot = forcing. - - Attributes - ========== - - auxiliary : Matrix - If applicable, the set of auxiliary Kane's - equations used to solve for non-contributing - forces. - mass_matrix : Matrix - The system's mass matrix - forcing : Matrix - The system's forcing vector - mass_matrix_full : Matrix - The "mass matrix" for the u's and q's - forcing_full : Matrix - The "forcing vector" for the u's and q's - - Examples - ======== - - This is a simple example for a one degree of freedom translational - spring-mass-damper. - - In this example, we first need to do the kinematics. - This involves creating generalized speeds and coordinates and their - derivatives. - Then we create a point and set its velocity in a frame:: - - >>> from sympy import symbols - >>> from sympy.physics.mechanics import dynamicsymbols, ReferenceFrame - >>> from sympy.physics.mechanics import Point, Particle, KanesMethod - >>> q, u = dynamicsymbols('q u') - >>> qd, ud = dynamicsymbols('q u', 1) - >>> m, c, k = symbols('m c k') - >>> N = ReferenceFrame('N') - >>> P = Point('P') - >>> P.set_vel(N, u * N.x) - - Next we need to arrange/store information in the way that KanesMethod - requires. The kinematic differential equations need to be stored in a - dict. A list of forces/torques must be constructed, where each entry in - the list is a (Point, Vector) or (ReferenceFrame, Vector) tuple, where the - Vectors represent the Force or Torque. - Next a particle needs to be created, and it needs to have a point and mass - assigned to it. - Finally, a list of all bodies and particles needs to be created:: - - >>> kd = [qd - u] - >>> FL = [(P, (-k * q - c * u) * N.x)] - >>> pa = Particle('pa', P, m) - >>> BL = [pa] - - Finally we can generate the equations of motion. - First we create the KanesMethod object and supply an inertial frame, - coordinates, generalized speeds, and the kinematic differential equations. - Additional quantities such as configuration and motion constraints, - dependent coordinates and speeds, and auxiliary speeds are also supplied - here (see the online documentation). - Next we form FR* and FR to complete: Fr + Fr* = 0. - We have the equations of motion at this point. - It makes sense to rearrnge them though, so we calculate the mass matrix and - the forcing terms, for E.o.M. in the form: [MM] udot = forcing, where MM is - the mass matrix, udot is a vector of the time derivatives of the - generalized speeds, and forcing is a vector representing "forcing" terms:: - - >>> KM = KanesMethod(N, q_ind=[q], u_ind=[u], kd_eqs=kd) - >>> (fr, frstar) = KM.kanes_equations(FL, BL) - >>> MM = KM.mass_matrix - >>> forcing = KM.forcing - >>> rhs = MM.inv() * forcing - >>> rhs - [(-c*u(t) - k*q(t))/m] - >>> KM.linearize()[0] - [ 0, 1] - [-k, -c] - - Please look at the documentation pages for more information on how to - perform linearization and how to deal with dependent coordinates & speeds, - and how do deal with bringing non-contributing forces into evidence. - - """ - - simp = True - - def __init__(self, frame, q_ind, u_ind, kd_eqs=None, q_dependent=[], - configuration_constraints=[], u_dependent=[], - velocity_constraints=[], acceleration_constraints=None, - u_auxiliary=[]): - - """Please read the online documentation. """ - # Big storage things - if not isinstance(frame, ReferenceFrame): - raise TypeError('An intertial ReferenceFrame must be supplied') - self._inertial = frame - self._forcelist = None - self._bodylist = None - self._fr = None - self._frstar = None - self._rhs = None - self._aux_eq = None - - # States - self._q = None - self._qdep = [] - self._qdot = None - self._u = None - self._udep = [] - self._udot = None - self._uaux = None - - # Differential Equations Matrices - self._k_d = None - self._f_d = None - self._k_kqdot = None - self._k_ku = None - self._f_k = None - - # Constraint Matrices - self._f_h = Matrix([]) - self._k_nh = Matrix([]) - self._f_nh = Matrix([]) - self._k_dnh = Matrix([]) - self._f_dnh = Matrix([]) - - self._coords(q_ind, q_dependent, configuration_constraints) - self._speeds(u_ind, u_dependent, velocity_constraints, - acceleration_constraints, u_auxiliary) - if kd_eqs is not None: - self._kindiffeq(kd_eqs) - - def _find_dynamicsymbols(self, inlist, insyms=[]): - """Finds all non-supplied dynamicsymbols in the expressions.""" - from sympy.core.function import AppliedUndef, Derivative - t = dynamicsymbols._t - return reduce(set.union, [set([i]) for j in inlist - for i in j.atoms(AppliedUndef, Derivative) - if i.atoms() == set([t])], set()) - insyms - - temp_f = set().union(*[i.atoms(AppliedUndef) for i in inlist]) - temp_d = set().union(*[i.atoms(Derivative) for i in inlist]) - set_f = set([a for a in temp_f if a.args == (t,)]) - set_d = set([a for a in temp_d if ((a.args[0] in set_f) and all([i == t - for i in a.variables]))]) - return list(set.union(set_f, set_d) - set(insyms)) - - def _find_othersymbols(self, inlist, insyms=[]): - """Finds all non-dynamic symbols in the expressions.""" - return list(reduce(set.union, [i.atoms(Symbol) for i in inlist]) - - set(insyms)) - - def _mat_inv_mul(self, A, B): - """Internal Function - - Computes A^-1 * B symbolically w/ substitution, where B is not - necessarily a vector, but can be a matrix. - - """ - - r1, c1 = A.shape - r2, c2 = B.shape - temp1 = Matrix(r1, c1, lambda i, j: Symbol('x' + str(j + r1 * i))) - temp2 = Matrix(r2, c2, lambda i, j: Symbol('y' + str(j + r2 * i))) - for i in range(len(temp1)): - if A[i] == 0: - temp1[i] = 0 - for i in range(len(temp2)): - if B[i] == 0: - temp2[i] = 0 - temp3 = [] - for i in range(c2): - temp3.append(temp1.LDLsolve(temp2[:, i])) - temp3 = Matrix([i.T for i in temp3]).T - return temp3.subs(dict(list(zip(temp1, A)))).subs(dict(list(zip(temp2, B)))) - - def _coords(self, qind, qdep=[], coneqs=[]): - """Supply all the generalized coordiantes in a list. - - If some coordinates are dependent, supply them as part of qdep. Their - dependent nature will only show up in the linearization process though. - - Parameters - ========== - - qind : list - A list of independent generalized coords - qdep : list - List of dependent coordinates - coneq : list - List of expressions which are equal to zero; these are the - configuration constraint equations - """ - - if not isinstance(qind, (list, tuple)): - raise TypeError('Generalized coords. must be supplied in a list.') - self._q = qind + qdep - self._qdot = [diff(i, dynamicsymbols._t) for i in self._q] - - if not isinstance(qdep, (list, tuple)): - raise TypeError('Dependent coordinates and constraints must each be ' - 'provided in their own list.') - if len(qdep) != len(coneqs): - raise ValueError('There must be an equal number of dependent ' - 'coordinates and constraints.') - coneqs = Matrix(coneqs) - self._qdep = qdep - self._f_h = coneqs - - def _speeds(self, uind, udep=[], coneqs=[], diffconeqs=None, u_auxiliary=[]): - """Supply all the generalized speeds in a list. - - If there are motion constraints or auxiliary speeds, they are provided - here as well (as well as motion constraints). - - Parameters - ========== - - uind : list - A list of independent generalized speeds - udep : list - Optional list of dependent speeds - coneqs : list - Optional List of constraint expressions; these are expressions - which are equal to zero which define a speed (motion) constraint. - diffconeqs : list - Optional, calculated automatically otherwise; list of constraint - equations; again equal to zero, but define an acceleration - constraint. - u_auxiliary : list - An optional list of auxiliary speeds used for brining - non-contributing forces into evidence - - """ - - if not hasattr(uind, '__iter__'): - raise TypeError('Supply generalized speeds in an iterable.') - self._u = uind + udep - self._udot = [diff(i, dynamicsymbols._t) for i in self._u] - self._uaux = u_auxiliary - - if not hasattr(udep, '__iter__'): - raise TypeError('Supply dependent speeds in an iterable.') - if len(udep) != len(coneqs): - raise ValueError('There must be an equal number of dependent ' - 'speeds and constraints.') - if diffconeqs is not None: - if len(udep) != len(diffconeqs): - raise ValueError('There must be an equal number of dependent ' - 'speeds and constraints.') - if len(udep) != 0: - u = self._u - uzero = dict(list(zip(u, [0] * len(u)))) - coneqs = Matrix(coneqs) - udot = self._udot - udotzero = dict(list(zip(udot, [0] * len(udot)))) - - self._udep = udep - self._f_nh = coneqs.subs(uzero) - self._k_nh = (coneqs - self._f_nh).jacobian(u) - # if no differentiated non holonomic constraints were given, calculate - if diffconeqs is None: - self._k_dnh = self._k_nh - self._f_dnh = (self._k_nh.diff(dynamicsymbols._t) * Matrix(u) + - self._f_nh.diff(dynamicsymbols._t)) - else: - self._f_dnh = diffconeqs.subs(udotzero) - self._k_dnh = (diffconeqs - self._f_dnh).jacobian(udot) - - o = len(u) # number of generalized speeds - m = len(udep) # number of motion constraints - p = o - m # number of independent speeds - # For a reminder, form of non-holonomic constraints is: - # B u + C = 0 - B = self._k_nh[:m, :] - C = self._f_nh[:m, 0] - - # We partition B into indenpendent and dependent columns - # Ars is then -Bdep.inv() * Bind, and it relates depedent speeds to - # independent speeds as: udep = Ars uind, neglecting the C term here. - self._depB = B - self._depC = C - mr1 = B[:m, :p] - ml1 = B[:m, p:o] - self._Ars = - self._mat_inv_mul(ml1, mr1) - -
    [docs] def kindiffdict(self): - """Returns the qdot's in a dictionary. """ - if self._k_kqdot is None: - raise ValueError('Kin. diff. eqs need to be supplied first.') - sub_dict = solve_linear_system_LU(Matrix([self._k_kqdot.T, - -(self._k_ku * Matrix(self._u) + self._f_k).T]).T, self._qdot) - return sub_dict -
    - def _kindiffeq(self, kdeqs): - """Supply all the kinematic differential equations in a list. - - They should be in the form [Expr1, Expr2, ...] where Expri is equal to - zero - - Parameters - ========== - - kdeqs : list (of Expr) - The listof kinematic differential equations - - """ - if len(self._q) != len(kdeqs): - raise ValueError('There must be an equal number of kinematic ' - 'differential equations and coordinates.') - - uaux = self._uaux - # dictionary of auxiliary speeds which are equal to zero - uaz = dict(list(zip(uaux, [0] * len(uaux)))) - - kdeqs = Matrix(kdeqs).subs(uaz) - - qdot = self._qdot - qdotzero = dict(list(zip(qdot, [0] * len(qdot)))) - u = self._u - uzero = dict(list(zip(u, [0] * len(u)))) - - f_k = kdeqs.subs(uzero).subs(qdotzero) - k_kqdot = (kdeqs.subs(uzero) - f_k).jacobian(Matrix(qdot)) - k_ku = (kdeqs.subs(qdotzero) - f_k).jacobian(Matrix(u)) - - self._k_ku = self._mat_inv_mul(k_kqdot, k_ku) - self._f_k = self._mat_inv_mul(k_kqdot, f_k) - self._k_kqdot = eye(len(qdot)) - - def _form_fr(self, fl): - """Form the generalized active force. - - Computes the vector of the generalized active force vector. - Used to compute E.o.M. in the form Fr + Fr* = 0. - - Parameters - ========== - - fl : list - Takes in a list of (Point, Vector) or (ReferenceFrame, Vector) - tuples which represent the force at a point or torque on a frame. - - """ - - if not hasattr(fl, '__iter__'): - raise TypeError('Force pairs must be supplied in an iterable.') - - N = self._inertial - self._forcelist = fl[:] - u = self._u - o = len(u) # number of gen. speeds - b = len(fl) # number of forces - - FR = zeros(o, 1) - - # pull out relevant velocities for constructing partial velocities - vel_list = [] - f_list = [] - for i in fl: - if isinstance(i[0], ReferenceFrame): - vel_list += [i[0].ang_vel_in(N)] - elif isinstance(i[0], Point): - vel_list += [i[0].vel(N)] - else: - raise TypeError('First entry in pair must be point or frame.') - f_list += [i[1]] - partials = partial_velocity(vel_list, u, N) - - # Fill Fr with dot product of partial velocities and forces - for i in range(o): - for j in range(b): - FR[i] -= partials[j][i] & f_list[j] - - # In case there are dependent speeds - m = len(self._udep) # number of dependent speeds - if m != 0: - p = o - m - FRtilde = FR[:p, 0] - FRold = FR[p:o, 0] - FRtilde += self._Ars.T * FRold - FR = FRtilde - - self._fr = FR - return FR - - def _form_frstar(self, bl): - """Form the generalized inertia force. - - Computes the vector of the generalized inertia force vector. - Used to compute E.o.M. in the form Fr + Fr* = 0. - - Parameters - ========== - - bl : list - A list of all RigidBody's and Particle's in the system. - - """ - - if not hasattr(bl, '__iter__'): - raise TypeError('Bodies must be supplied in an iterable.') - t = dynamicsymbols._t - N = self._inertial - self._bodylist = bl - u = self._u # all speeds - udep = self._udep # dependent speeds - o = len(u) - m = len(udep) - p = o - m - udot = self._udot - udotzero = dict(list(zip(udot, [0] * o))) - # auxiliary speeds - uaux = self._uaux - uauxdot = [diff(i, t) for i in uaux] - # dictionary of auxiliary speeds which are equal to zero - uaz = dict(list(zip(uaux, [0] * len(uaux)))) - uadz = dict(list(zip(uauxdot, [0] * len(uauxdot)))) - - MM = zeros(o, o) - nonMM = zeros(o, 1) - partials = [] - # Fill up the list of partials: format is a list with no. elements - # equal to number of entries in body list. Each of these elements is a - # list - either of length 1 for the translational components of - # particles or of length 2 for the translational and rotational - # components of rigid bodies. The inner most list is the list of - # partial velocities. - for v in bl: - if isinstance(v, RigidBody): - partials += [partial_velocity([v.masscenter.vel(N), - v.frame.ang_vel_in(N)], u, N)] - elif isinstance(v, Particle): - partials += [partial_velocity([v.point.vel(N)], u, N)] - else: - raise TypeError('The body list needs RigidBody or ' - 'Particle as list elements.') - - # This section does 2 things - computes the parts of Fr* that are - # associated with the udots, and the parts that are not associated with - # the udots. This happens for RigidBody and Particle a little - # differently, but similar process overall. - for i, v in enumerate(bl): - if isinstance(v, RigidBody): - M = v.mass.subs(uaz).doit() - I, P = v.inertia - if P != v.masscenter: - # redefine I about the center of mass - # have I S/O, want I S/S* - # I S/O = I S/S* + I S*/O; I S/S* = I S/O - I S*/O - f = v.frame - d = v.masscenter.pos_from(P) - I -= inertia_of_point_mass(M, d, f) - I = I.subs(uaz).doit() - for j in range(o): - for k in range(o): - # translational - MM[j, k] += M * (partials[i][0][j].subs(uaz).doit() & - partials[i][0][k]) - # rotational - temp = (I & partials[i][1][j].subs(uaz).doit()) - MM[j, k] += (temp & - partials[i][1][k]) - # translational components - nonMM[j] += ( (M.diff(t) * - v.masscenter.vel(N)).subs(uaz).doit() & - partials[i][0][j]) - nonMM[j] += (M * - v.masscenter.acc(N).subs(udotzero).subs(uaz).doit() - & partials[i][0][j]) - # rotational components - omega = v.frame.ang_vel_in(N).subs(uaz).doit() - nonMM[j] += ((I.dt(v.frame) & omega).subs(uaz).doit() & - partials[i][1][j]) - nonMM[j] += ((I & - v.frame.ang_acc_in(N)).subs(udotzero).subs(uaz).doit() - & partials[i][1][j]) - nonMM[j] += ((omega ^ (I & omega)).subs(uaz).doit() & - partials[i][1][j]) - - if isinstance(v, Particle): - M = v.mass.subs(uaz).doit() - for j in range(o): - for k in range(o): - MM[j, k] += M * (partials[i][0][j].subs(uaz).doit() & - partials[i][0][k]) - nonMM[j] += M.diff(t) * (v.point.vel(N).subs(uaz).doit() & - partials[i][0][j]) - nonMM[j] += (M * - v.point.acc(N).subs(udotzero).subs(uaz).doit() & - partials[i][0][j]) - FRSTAR = MM * Matrix(udot).subs(uadz) + nonMM - - # For motion constraints, m is the number of constraints - # Really, one should just look at Kane's book for descriptions of this - # process - if m != 0: - FRSTARtilde = FRSTAR[:p, 0] - FRSTARold = FRSTAR[p:o, 0] - FRSTARtilde += self._Ars.T * FRSTARold - FRSTAR = FRSTARtilde - - MMi = MM[:p, :] - MMd = MM[p:o, :] - MM = MMi + self._Ars.T * MMd - self._frstar = FRSTAR - - zeroeq = self._fr + self._frstar - zeroeq = zeroeq.subs(udotzero) - - self._k_d = MM - self._f_d = zeroeq - return FRSTAR - -
    [docs] def kanes_equations(self, FL, BL): - """ Method to form Kane's equations, Fr + Fr* = 0. - - Returns (Fr, Fr*). In the case where auxiliary generalized speeds are - present (say, s auxiliary speeds, o generalized speeds, and m motion - constraints) the length of the returned vectors will be o - m + s in - length. The first o - m equations will be the constrained Kane's - equations, then the s auxiliary Kane's equations. These auxiliary - equations can be accessed with the auxiliary_eqs(). - - Parameters - ========== - - FL : list - Takes in a list of (Point, Vector) or (ReferenceFrame, Vector) - tuples which represent the force at a point or torque on a frame. - BL : list - A list of all RigidBody's and Particle's in the system. - - """ - - if (self._q is None) or (self._u is None): - raise ValueError('Speeds and coordinates must be supplied first.') - if (self._k_kqdot is None): - raise ValueError( - 'Supply kinematic differential equations, please.') - - fr = self._form_fr(FL) - frstar = self._form_frstar(BL) - if self._uaux != []: - km = KanesMethod(self._inertial, self._q, self._uaux, - u_auxiliary=self._uaux) - fraux = km._form_fr(FL) - frstaraux = km._form_frstar(BL) - self._aux_eq = fraux + frstaraux - self._fr = fr.col_join(fraux) - self._frstar = frstar.col_join(frstaraux) - return (self._fr, self._frstar) - else: - return (fr, frstar) -
    -
    [docs] def linearize(self): - """ Method used to generate linearized equations. - - Note that for linearization, it is assumed that time is not perturbed, - but only coordinates and positions. The "forcing" vector's jacobian is - computed with respect to the state vector in the form [Qi, Qd, Ui, Ud]. - This is the "f_lin_A" matrix. - - It also finds any non-state dynamicsymbols and computes the jacobian of - the "forcing" vector with respect to them. This is the "f_lin_B" - matrix; if this is empty, an empty matrix is created. - - Consider the following: - If our equations are: [M]qudot = f, where [M] is the full mass matrix, - qudot is a vector of the deriatives of the coordinates and speeds, and - f in the full forcing vector, the linearization process is as follows: - [M]qudot = [f_lin_A]qu + [f_lin_B]y, where qu is the state vector, - f_lin_A is the jacobian of the full forcing vector with respect to the - state vector, f_lin_B is the jacobian of the full forcing vector with - respect to any non-speed/coordinate dynamicsymbols which show up in the - full forcing vector, and y is a vector of those dynamic symbols (each - column in f_lin_B corresponds to a row of the y vector, each of which - is a non-speed/coordinate dynamicsymbol). - - To get the traditional state-space A and B matrix, you need to multiply - the f_lin_A and f_lin_B matrices by the inverse of the mass matrix. - Caution needs to be taken when inverting large symbolic matrices; - substituting in numerical values before inverting will work better. - - A tuple of (f_lin_A, f_lin_B, other_dynamicsymbols) is returned. - - """ - - if (self._fr is None) or (self._frstar is None): - raise ValueError('Need to compute Fr, Fr* first.') - - # Note that this is now unneccessary, and it should never be - # encountered; I still think it should be in here in case the user - # manually sets these matrices incorrectly. - for i in self._q: - if self._k_kqdot.diff(i) != 0 * self._k_kqdot: - raise ValueError('Matrix K_kqdot must not depend on any q.') - - t = dynamicsymbols._t - uaux = self._uaux - uauxdot = [diff(i, t) for i in uaux] - # dictionary of auxiliary speeds & derivatives which are equal to zero - subdict = dict(list(zip(uaux + uauxdot, [0] * (len(uaux) + len(uauxdot))))) - - # Checking for dynamic symbols outside the dynamic differential - # equations; throws error if there is. - insyms = set( - self._q + self._qdot + self._u + self._udot + uaux + uauxdot) - if any(self._find_dynamicsymbols(i, insyms) for i in [self._k_kqdot, - self._k_ku, - self._f_k, - self._k_dnh, - self._f_dnh, - self._k_d]): - raise ValueError('Cannot have dynamicsymbols outside dynamic ' - 'forcing vector.') - other_dyns = list(self._find_dynamicsymbols(self._f_d.subs(subdict), - insyms)) - - # make it canonically ordered so the jacobian is canonical - other_dyns.sort(key=default_sort_key) - - for i in other_dyns: - if diff(i, dynamicsymbols._t) in other_dyns: - raise ValueError('Cannot have derivatives of specified ' - 'quantities when linearizing forcing terms.') - - o = len(self._u) # number of speeds - n = len(self._q) # number of coordinates - l = len(self._qdep) # number of configuration constraints - m = len(self._udep) # number of motion constraints - qi = Matrix(self._q[: n - l]) # independent coords - qd = Matrix(self._q[n - l: n]) # dependent coords; could be empty - ui = Matrix(self._u[: o - m]) # independent speeds - ud = Matrix(self._u[o - m: o]) # dependent speeds; could be empty - qdot = Matrix(self._qdot) # time derivatives of coordinates - - # with equations in the form MM udot = forcing, expand that to: - # MM_full [q,u].T = forcing_full. This combines coordinates and - # speeds together for the linearization, which is necessary for the - # linearization process, due to dependent coordinates. f1 is the rows - # from the kinematic differential equations, f2 is the rows from the - # dynamic differential equations (and differentiated non-holonomic - # constraints). - f1 = self._k_ku * Matrix(self._u) + self._f_k - f2 = self._f_d - # Only want to do this if these matrices have been filled in, which - # occurs when there are dependent speeds - if m != 0: - f2 = self._f_d.col_join(self._f_dnh) - fnh = self._f_nh + self._k_nh * Matrix(self._u) - f1 = f1.subs(subdict) - f2 = f2.subs(subdict) - fh = self._f_h.subs(subdict) - fku = (self._k_ku * Matrix(self._u)).subs(subdict) - fkf = self._f_k.subs(subdict) - - # In the code below, we are applying the chain rule by hand on these - # things. All the matrices have been changed into vectors (by - # multiplying the dynamic symbols which it is paired with), so we can - # take the jacobian of them. The basic operation is take the jacobian - # of the f1, f2 vectors wrt all of the q's and u's. f1 is a function of - # q, u, and t; f2 is a function of q, qdot, u, and t. In the code - # below, we are not considering perturbations in t. So if f1 is a - # function of the q's, u's but some of the q's or u's could be - # dependent on other q's or u's (qd's might be dependent on qi's, ud's - # might be dependent on ui's or qi's), so what we do is take the - # jacobian of the f1 term wrt qi's and qd's, the jacobian wrt the qd's - # gets multiplied by the jacobian of qd wrt qi, this is extended for - # the ud's as well. dqd_dqi is computed by taking a taylor expansion of - # the holonomic constraint equations about q*, treating q* - q as dq, - # seperating into dqd (depedent q's) and dqi (independent q's) and the - # rearranging for dqd/dqi. This is again extended for the speeds. - - # First case: configuration and motion constraints - if (l != 0) and (m != 0): - fh_jac_qi = fh.jacobian(qi) - fh_jac_qd = fh.jacobian(qd) - fnh_jac_qi = fnh.jacobian(qi) - fnh_jac_qd = fnh.jacobian(qd) - fnh_jac_ui = fnh.jacobian(ui) - fnh_jac_ud = fnh.jacobian(ud) - fku_jac_qi = fku.jacobian(qi) - fku_jac_qd = fku.jacobian(qd) - fku_jac_ui = fku.jacobian(ui) - fku_jac_ud = fku.jacobian(ud) - fkf_jac_qi = fkf.jacobian(qi) - fkf_jac_qd = fkf.jacobian(qd) - f1_jac_qi = f1.jacobian(qi) - f1_jac_qd = f1.jacobian(qd) - f1_jac_ui = f1.jacobian(ui) - f1_jac_ud = f1.jacobian(ud) - f2_jac_qi = f2.jacobian(qi) - f2_jac_qd = f2.jacobian(qd) - f2_jac_ui = f2.jacobian(ui) - f2_jac_ud = f2.jacobian(ud) - f2_jac_qdot = f2.jacobian(qdot) - - dqd_dqi = - self._mat_inv_mul(fh_jac_qd, fh_jac_qi) - dud_dqi = self._mat_inv_mul(fnh_jac_ud, (fnh_jac_qd * - dqd_dqi - fnh_jac_qi)) - dud_dui = - self._mat_inv_mul(fnh_jac_ud, fnh_jac_ui) - dqdot_dui = - self._k_kqdot.inv() * (fku_jac_ui + - fku_jac_ud * dud_dui) - dqdot_dqi = - self._k_kqdot.inv() * (fku_jac_qi + fkf_jac_qi + - (fku_jac_qd + fkf_jac_qd) * dqd_dqi + fku_jac_ud * dud_dqi) - f1_q = f1_jac_qi + f1_jac_qd * dqd_dqi + f1_jac_ud * dud_dqi - f1_u = f1_jac_ui + f1_jac_ud * dud_dui - f2_q = (f2_jac_qi + f2_jac_qd * dqd_dqi + f2_jac_qdot * dqdot_dqi + - f2_jac_ud * dud_dqi) - f2_u = f2_jac_ui + f2_jac_ud * dud_dui + f2_jac_qdot * dqdot_dui - # Second case: configuration constraints only - elif l != 0: - dqd_dqi = - self._mat_inv_mul(fh.jacobian(qd), fh.jacobian(qi)) - dqdot_dui = - self._k_kqdot.inv() * fku.jacobian(ui) - dqdot_dqi = - self._k_kqdot.inv() * (fku.jacobian(qi) + - fkf.jacobian(qi) + (fku.jacobian(qd) + fkf.jacobian(qd)) * - dqd_dqi) - f1_q = (f1.jacobian(qi) + f1.jacobian(qd) * dqd_dqi) - f1_u = f1.jacobian(ui) - f2_jac_qdot = f2.jacobian(qdot) - f2_q = (f2.jacobian(qi) + f2.jacobian(qd) * dqd_dqi + - f2.jac_qdot * dqdot_dqi) - f2_u = f2.jacobian(ui) + f2_jac_qdot * dqdot_dui - # Third case: motion constraints only - elif m != 0: - dud_dqi = self._mat_inv_mul(fnh.jacobian(ud), - fnh.jacobian(qi)) - dud_dui = - self._mat_inv_mul(fnh.jacobian(ud), fnh.jacobian(ui)) - dqdot_dui = - self._k_kqdot.inv() * (fku.jacobian(ui) + - fku.jacobian(ud) * dud_dui) - dqdot_dqi = - self._k_kqdot.inv() * (fku.jacobian(qi) + - fkf.jacobian(qi) + fku.jacobian(ud) * dud_dqi) - f1_jac_ud = f1.jacobian(ud) - f2_jac_qdot = f2.jacobian(qdot) - f2_jac_ud = f2.jacobian(ud) - f1_q = f1.jacobian(qi) + f1_jac_ud * dud_dqi - f1_u = f1.jacobian(ui) + f1_jac_ud * dud_dui - f2_q = (f2.jacobian(qi) + f2_jac_qdot * dqdot_dqi + f2_jac_ud - * dud_dqi) - f2_u = (f2.jacobian(ui) + f2_jac_ud * dud_dui + f2_jac_qdot * - dqdot_dui) - # Fourth case: No constraints - else: - dqdot_dui = - self._k_kqdot.inv() * fku.jacobian(ui) - dqdot_dqi = - self._k_kqdot.inv() * (fku.jacobian(qi) + - fkf.jacobian(qi)) - f1_q = f1.jacobian(qi) - f1_u = f1.jacobian(ui) - f2_jac_qdot = f2.jacobian(qdot) - f2_q = f2.jacobian(qi) + f2_jac_qdot * dqdot_dqi - f2_u = f2.jacobian(ui) + f2_jac_qdot * dqdot_dui - f_lin_A = -(f1_q.row_join(f1_u)).col_join(f2_q.row_join(f2_u)) - if other_dyns: - f1_oths = f1.jacobian(other_dyns) - f2_oths = f2.jacobian(other_dyns) - f_lin_B = -f1_oths.col_join(f2_oths) - else: - f_lin_B = Matrix([]) - return (f_lin_A, f_lin_B, Matrix(other_dyns)) -
    -
    [docs] def rhs(self, inv_method=None): - """ Returns the system's equations of motion in first order form. - - The output of this will be the right hand side of: - - [qdot, udot].T = f(q, u, t) - - Or, the equations of motion in first order form. The right hand side - is what is needed by most numerical ODE integrators. - - Parameters - ========== - - inv_method : str - The specific sympy inverse matrix calculation method to use. - - """ - - if inv_method is None: - self._rhs = self._mat_inv_mul(self.mass_matrix_full, - self.forcing_full) - else: - self._rhs = (self.mass_matrix_full.inv(inv_method, - try_block_diag=True) * self.forcing_full) - return self._rhs -
    - @property - def auxiliary_eqs(self): - if (self._fr is None) or (self._frstar is None): - raise ValueError('Need to compute Fr, Fr* first.') - if self._uaux == []: - raise ValueError('No auxiliary speeds have been declared.') - return self._aux_eq - - @property - def mass_matrix(self): - # Returns the mass matrix, which is augmented by the differentiated non - # holonomic equations if necessary - if (self._frstar is None) & (self._fr is None): - raise ValueError('Need to compute Fr, Fr* first.') - return Matrix([self._k_d, self._k_dnh]) - - @property - def mass_matrix_full(self): - # Returns the mass matrix from above, augmented by kin diff's k_kqdot - if (self._frstar is None) & (self._fr is None): - raise ValueError('Need to compute Fr, Fr* first.') - o = len(self._u) - n = len(self._q) - return ((self._k_kqdot).row_join(zeros(n, o))).col_join((zeros(o, - n)).row_join(self.mass_matrix)) - - @property - def forcing(self): - # Returns the forcing vector, which is augmented by the differentiated - # non holonomic equations if necessary - if (self._frstar is None) & (self._fr is None): - raise ValueError('Need to compute Fr, Fr* first.') - return -Matrix([self._f_d, self._f_dnh]) - - @property - def forcing_full(self): - # Returns the forcing vector, which is augmented by the differentiated - # non holonomic equations if necessary - if (self._frstar is None) & (self._fr is None): - raise ValueError('Need to compute Fr, Fr* first.') - f1 = self._k_ku * Matrix(self._u) + self._f_k - return -Matrix([f1, self._f_d, self._f_dnh])
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/mechanics/lagrange.html b/dev-py3k/_modules/sympy/physics/mechanics/lagrange.html deleted file mode 100644 index 642cddaf1d4..00000000000 --- a/dev-py3k/_modules/sympy/physics/mechanics/lagrange.html +++ /dev/null @@ -1,450 +0,0 @@ - - - - - - - - - - sympy.physics.mechanics.lagrange — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.mechanics.lagrange

    -__all__ = ['LagrangesMethod']
    -
    -from sympy import diff, zeros, Matrix, eye, sympify
    -from sympy.physics.mechanics import (dynamicsymbols, ReferenceFrame, Point)
    -
    -
    -
    [docs]class LagrangesMethod(object): - """Lagrange's method object. - - This object generates the equations of motion in a two step procedure. The - first step involves the initialization of LagrangesMethod by supplying the - Lagrangian and a list of the generalized coordinates, at the bare minimum. - If there are any constraint equations, they can be supplied as keyword - arguments. The Lagrangian multipliers are automatically generated and are - equal in number to the constraint equations.Similarly any non-conservative - forces can be supplied in a list (as described below and also shown in the - example) along with a ReferenceFrame. This is also discussed further in the - __init__ method. - - Attributes - ========== - - mass_matrix : Matrix - The system's mass matrix - - forcing : Matrix - The system's forcing vector - - mass_matrix_full : Matrix - The "mass matrix" for the qdot's, qdoubledot's, and the - lagrange multipliers (lam) - - forcing_full : Matrix - The forcing vector for the qdot's, qdoubledot's and - lagrange multipliers (lam) - - Examples - ======== - - This is a simple example for a one degree of freedom translational - spring-mass-damper. - - In this example, we first need to do the kinematics.$ - This involves creating generalized coordinates and its derivative. - Then we create a point and set its velocity in a frame:: - - >>> from sympy.physics.mechanics import LagrangesMethod, Lagrangian - >>> from sympy.physics.mechanics import ReferenceFrame, Particle, Point - >>> from sympy.physics.mechanics import dynamicsymbols, kinetic_energy - >>> from sympy import symbols - >>> q = dynamicsymbols('q') - >>> qd = dynamicsymbols('q', 1) - >>> m, k, b = symbols('m k b') - >>> N = ReferenceFrame('N') - >>> P = Point('P') - >>> P.set_vel(N, qd * N.x) - - We need to then prepare the information as required by LagrangesMethod to - generate equations of motion. - First we create the Particle, which has a point attached to it. - Following this the lagrangian is created from the kinetic and potential - energies. - Then, a list of nonconservative forces/torques must be constructed, where - each entry in is a (Point, Vector) or (ReferenceFrame, Vector) tuple, where - the Vectors represent the nonconservative force or torque. - - >>> Pa = Particle('Pa', P, m) - >>> Pa.set_potential_energy(k * q**2 / 2.0) - >>> L = Lagrangian(N, Pa) - >>> fl = [(P, -b * qd * N.x)] - - Finally we can generate the equations of motion. - First we create the LagrangesMethod object.To do this one must supply an - the Lagrangian, the list of generalized coordinates. Also supplied are the - constraint equations, the forcelist and the inertial frame, if relevant. - Next we generate Lagrange's equations of motion, such that: - Lagrange's equations of motion = 0. - We have the equations of motion at this point. - - >>> l = LagrangesMethod(L, [q], forcelist = fl, frame = N) - >>> print(l.form_lagranges_equations()) - [b*Derivative(q(t), t) + 1.0*k*q(t) + m*Derivative(q(t), t, t)] - - We can also solve for the states using the 'rhs' method. - - >>> print(l.rhs()) - [ Derivative(q(t), t)] - [(-b*Derivative(q(t), t) - 1.0*k*q(t))/m] - - Please refer to the docstrings on each method for more details. - - """ - - def __init__(self, Lagrangian, q_list, coneqs=None, forcelist=None, frame=None): - """Supply the following for the initialization of LagrangesMethod - - Lagrangian : Sympifyable - - q_list : list - A list of the generalized coordinates - - coneqs : list - A list of the holonomic and non-holonomic constraint equations. - VERY IMPORTANT NOTE- The holonomic constraints must be - differentiated with respect to time and then included in coneqs. - - forcelist : list - Takes a list of (Point, Vector) or (ReferenceFrame, Vector) tuples - which represent the force at a point or torque on a frame. This - feature is primarily to account for the nonconservative forces - amd/or moments. - - frame : ReferenceFrame - Supply the inertial frame. This is used to determine the - generalized forces due to non-sonservative forces. - - """ - - self._L = sympify(Lagrangian) - self.eom = None # initializing the eom Matrix - self._m_cd = Matrix([]) # Mass Matrix of differentiated coneqs - self._m_d = Matrix([]) # Mass Matrix of dynamic equations - self._f_cd = Matrix([]) # Forcing part of the diff coneqs - self._f_d = Matrix([]) # Forcing part of the dynamic equations - self.lam_coeffs = Matrix([]) # Initializing the coeffecients of lams - - self.forcelist = forcelist - self.inertial = frame - - self.lam_vec = Matrix([]) - - self._term1 = Matrix([]) - self._term2 = Matrix([]) - self._term3 = Matrix([]) - self._term4 = Matrix([]) - - # Creating the qs, qdots and qdoubledots - - q_list = list(q_list) - if not isinstance(q_list, list): - raise TypeError('Generalized coords. must be supplied in a list') - self._q = q_list - self._qdots = [diff(i, dynamicsymbols._t) for i in self._q] - self._qdoubledots = [diff(i, dynamicsymbols._t) for i in self._qdots] - - self.coneqs = coneqs - -
    [docs] def form_lagranges_equations(self): - """Method to form Lagrange's equations of motion. - - Returns a vector of equations of motion using Lagrange's equations of - the second kind. - - """ - - q = self._q - qd = self._qdots - qdd = self._qdoubledots - n = len(q) - - #Putting the Lagrangian in a Matrix - L = Matrix([self._L]) - - #Determining the first term in Lagrange's EOM - self._term1 = L.jacobian(qd) - self._term1 = ((self._term1).diff(dynamicsymbols._t)).transpose() - - #Determining the second term in Lagrange's EOM - self._term2 = (L.jacobian(q)).transpose() - - #term1 and term2 will be there no matter what so leave them as they are - - if self.coneqs is not None: - coneqs = self.coneqs - #If there are coneqs supplied then the following will be created - coneqs = list(coneqs) - if not isinstance(coneqs, list): - raise TypeError('Enter the constraint equations in a list') - - o = len(coneqs) - - #Creating the multipliers - self.lam_vec = Matrix(dynamicsymbols('lam1:' + str(o + 1))) - - #Extracting the coeffecients of the multipliers - coneqs_mat = Matrix(coneqs) - qd = self._qdots - self.lam_coeffs = -coneqs_mat.jacobian(qd) - - #Determining the third term in Lagrange's EOM - #term3 = ((self.lam_vec).transpose() * self.lam_coeffs).transpose() - self._term3 = self.lam_coeffs.transpose() * self.lam_vec - - #Taking the time derivative of the constraint equations - diffconeqs = [diff(i, dynamicsymbols._t) for i in coneqs] - - #Extracting the coeffecients of the qdds from the diff coneqs - diffconeqs_mat = Matrix(diffconeqs) - qdd = self._qdoubledots - self._m_cd = diffconeqs_mat.jacobian(qdd) - - #The remaining terms i.e. the 'forcing' terms in diff coneqs - qddzero = dict(list(zip(qdd, [0] * n))) - self._f_cd = -diffconeqs_mat.subs(qddzero) - - else: - self._term3 = zeros(n, 1) - - if self.forcelist is not None: - forcelist = self.forcelist - N = self.inertial - if not isinstance(N, ReferenceFrame): - raise TypeError('Enter a valid ReferenceFrame') - if not isinstance(forcelist, (list, tuple)): - raise TypeError('Forces must be supplied in a list of: lists' - ' or tuples') - self._term4 = zeros(n, 1) - for i, v in enumerate(qd): - for j, w in enumerate(forcelist): - if isinstance(w[0], ReferenceFrame): - speed = w[0].ang_vel_in(N) - self._term4[i] += speed.diff(v, N) & w[1] - if isinstance(w[0], Point): - speed = w[0].vel(N) - self._term4[i] += speed.diff(v, N) & w[1] - else: - raise TypeError('First entry in force pair is a point' - ' or frame') - - else: - self._term4 = zeros(n, 1) - - self.eom = self._term1 - self._term2 - self._term3 - self._term4 - - return self.eom -
    - @property -
    [docs] def mass_matrix(self): - """ Returns the mass matrix, which is augmented by the Lagrange - multipliers, if necessary. - - If the system is described by 'n' generalized coordinates and there are - no constraint equations then an n X n matrix is returned. - - If there are 'n' generalized coordinates and 'm' constraint equations - have been supplied during initialization then an n X (n+m) matrix is - returned. The (n + m - 1)th and (n + m)th columns contain the - coefficients of the Lagrange multipliers. - - """ - - if self.eom is None: - raise ValueError('Need to compute the equations of motion first') - - #The 'dynamic' mass matrix is generated by the following - self._m_d = (self.eom).jacobian(self._qdoubledots) - - if len(self.lam_coeffs) != 0: - return (self._m_d).row_join((self.lam_coeffs).transpose()) - else: - return self._m_d -
    - @property -
    [docs] def mass_matrix_full(self): - """ Augments the coefficients of qdots to the mass_matrix. """ - - n = len(self._q) - if self.eom is None: - raise ValueError('Need to compute the equations of motion first') - #THE FIRST TWO ROWS OF THE MATRIX - row1 = eye(n).row_join(zeros(n, n)) - row2 = zeros(n, n).row_join(self.mass_matrix) - if self.coneqs is not None: - m = len(self.coneqs) - I = eye(n).row_join(zeros(n, n + m)) - below_eye = zeros(n + m, n) - A = (self.mass_matrix).col_join((self._m_cd).row_join(zeros(m, m))) - below_I = below_eye.row_join(A) - return I.col_join(below_I) - else: - A = row1.col_join(row2) - return A -
    - @property -
    [docs] def forcing(self): - """ Returns the forcing vector from 'lagranges_equations' method. """ - - if self.eom is None: - raise ValueError('Need to compute the equations of motion first') - - qdd = self._qdoubledots - qddzero = dict(list(zip(qdd, [0] * len(qdd)))) - - if self.coneqs is not None: - lam = self.lam_vec - lamzero = dict(list(zip(lam, [0] * len(lam)))) - - #The forcing terms from the eoms - self._f_d = -((self.eom).subs(qddzero)).subs(lamzero) - - else: - #The forcing terms from the eoms - self._f_d = -(self.eom).subs(qddzero) - - return self._f_d -
    - @property -
    [docs] def forcing_full(self): - """ Augments qdots to the forcing vector above. """ - - if self.eom is None: - raise ValueError('Need to compute the equations of motion first') - if self.coneqs is not None: - return (Matrix(self._qdots)).col_join((self.forcing).col_join(self._f_cd)) - else: - return (Matrix(self._qdots)).col_join(self.forcing) -
    -
    [docs] def rhs(self, method="GE"): - """ Returns equations that can be solved numerically - - Parameters - ========== - - method : string - The method by which matrix inversion of mass_matrix_full must be - performed such as Gauss Elimination or LU decomposition. - - """ - - # TODO- should probably use the matinvmul method from Kane - - return ((self.mass_matrix_full).inv(method, try_block_diag=True) * - self.forcing_full)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/mechanics/particle.html b/dev-py3k/_modules/sympy/physics/mechanics/particle.html deleted file mode 100644 index b50dc581071..00000000000 --- a/dev-py3k/_modules/sympy/physics/mechanics/particle.html +++ /dev/null @@ -1,339 +0,0 @@ - - - - - - - - - - sympy.physics.mechanics.particle — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.mechanics.particle

    -__all__ = ['Particle']
    -
    -from sympy import sympify
    -from sympy.physics.mechanics.point import Point
    -
    -
    -
    [docs]class Particle(object): - """A particle. - - Particles have a non-zero mass and lack spatial extension; they take up no - space. - - Values need to be supplied on initialization, but can be changed later. - - Parameters - ========== - name : str - Name of particle - point : Point - A physics/mechanics Point which represents the position, velocity, and - acceleration of this Particle - mass : sympifyable - A SymPy expression representing the Particle's mass - - Examples - ======== - - >>> from sympy.physics.mechanics import Particle, Point - >>> from sympy import Symbol - >>> po = Point('po') - >>> m = Symbol('m') - >>> pa = Particle('pa', po, m) - >>> # Or you could change these later - >>> pa.mass = m - >>> pa.point = po - - """ - - def __init__(self, name, point, mass): - if not isinstance(name, str): - raise TypeError('Supply a valid name.') - self._name = name - self.set_mass(mass) - self.set_point(point) - self._pe = sympify(0) - - def __str__(self): - return self._name - - __repr__ = __str__ - -
    [docs] def get_mass(self): - """Mass of the particle.""" - return self._mass -
    - def set_mass(self, mass): - self._mass = sympify(mass) - - mass = property(get_mass, set_mass) - -
    [docs] def get_point(self): - """Point of the particle.""" - return self._point -
    - def set_point(self, p): - if not isinstance(p, Point): - raise TypeError("Particle point attribute must be a Point object.") - self._point = p - - point = property(get_point, set_point) - -
    [docs] def linear_momentum(self, frame): - """Linear momentum of the particle. - - The linear momentum L, of a particle P, with respect to frame N is - given by - - L = m * v - - where m is the mass of the particle, and v is the velocity of the - particle in the frame N. - - Parameters - ========== - - frame : ReferenceFrame - The frame in which linear momentum is desired. - - Examples - ======== - - >>> from sympy.physics.mechanics import Particle, Point, ReferenceFrame - >>> from sympy.physics.mechanics import dynamicsymbols - >>> m, v = dynamicsymbols('m v') - >>> N = ReferenceFrame('N') - >>> P = Point('P') - >>> A = Particle('A', P, m) - >>> P.set_vel(N, v * N.x) - >>> A.linear_momentum(N) - m*v*N.x - - """ - - return self.mass * self.point.vel(frame) -
    -
    [docs] def angular_momentum(self, point, frame): - """Angular momentum of the particle about the point. - - The angular momentum H, about some point O of a particle, P, is given - by: - - H = r x m * v - - where r is the position vector from point O to the particle P, m is - the mass of the particle, and v is the velocity of the particle in - the inertial frame, N. - - Parameters - ========== - - point : Point - The point about which angular momentum of the particle is desired. - - frame : ReferenceFrame - The frame in which angular momentum is desired. - - Examples - ======== - - >>> from sympy.physics.mechanics import Particle, Point, ReferenceFrame - >>> from sympy.physics.mechanics import dynamicsymbols - >>> m, v, r = dynamicsymbols('m v r') - >>> N = ReferenceFrame('N') - >>> O = Point('O') - >>> A = O.locatenew('A', r * N.x) - >>> P = Particle('P', A, m) - >>> P.point.set_vel(N, v * N.y) - >>> P.angular_momentum(O, N) - m*r*v*N.z - - """ - - return self.point.pos_from(point) ^ (self.mass * self.point.vel(frame)) -
    -
    [docs] def kinetic_energy(self, frame): - """Kinetic energy of the particle - - The kinetic energy, T, of a particle, P, is given by - - 'T = 1/2 m v^2' - - where m is the mass of particle P, and v is the velocity of the - particle in the supplied ReferenceFrame. - - Parameters - ========== - - frame : ReferenceFrame - The Particle's velocity is typically defined with respect to - an inertial frame but any relevant frame in which the velocity is - known can be supplied. - - Examples - ======== - - >>> from sympy.physics.mechanics import Particle, Point, ReferenceFrame - >>> from sympy import symbols - >>> m, v, r = symbols('m v r') - >>> N = ReferenceFrame('N') - >>> O = Point('O') - >>> P = Particle('P', O, m) - >>> P.point.set_vel(N, v * N.y) - >>> P.kinetic_energy(N) - m*v**2/2 - - """ - - return (self.mass / sympify(2) * self.point.vel(frame) & - self.point.vel(frame)) -
    -
    [docs] def set_potential_energy(self, scalar): - """Used to set the potential energy of the Particle. - - Parameters - ========== - - scalar : Sympifyable - The potential energy (a scalar) of the Particle. - - Examples - ======== - - >>> from sympy.physics.mechanics import Particle, Point - >>> from sympy import symbols - >>> m, g, h = symbols('m g h') - >>> O = Point('O') - >>> P = Particle('P', O, m) - >>> P.set_potential_energy(m * g * h) - - """ - - self._pe = sympify(scalar) -
    - @property -
    [docs] def potential_energy(self): - """The potential energy of the Particle. - - Examples - ======== - - >>> from sympy.physics.mechanics import Particle, Point - >>> from sympy import symbols - >>> m, g, h = symbols('m g h') - >>> O = Point('O') - >>> P = Particle('P', O, m) - >>> P.set_potential_energy(m * g * h) - >>> P.potential_energy - g*h*m - - """ - - return self._pe
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/mechanics/point.html b/dev-py3k/_modules/sympy/physics/mechanics/point.html deleted file mode 100644 index 71e7ec63646..00000000000 --- a/dev-py3k/_modules/sympy/physics/mechanics/point.html +++ /dev/null @@ -1,560 +0,0 @@ - - - - - - - - - - sympy.physics.mechanics.point — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.mechanics.point

    -__all__ = ['Point']
    -
    -from sympy.physics.mechanics.essential import _check_frame, _check_vector
    -
    -
    -
    [docs]class Point(object): - """This object represents a point in a dynamic system. - - It stores the: position, velocity, and acceleration of a point. - The position is a vector defined as the vector distance from a parent - point to this point. - - """ - - def __init__(self, name): - """Initialization of a Point object. """ - self.name = name - self._pos_dict = {} - self._vel_dict = {} - self._acc_dict = {} - self._pdlist = [self._pos_dict, self._vel_dict, self._acc_dict] - - def __str__(self): - return self.name - - __repr__ = __str__ - - def _check_point(self, other): - if not isinstance(other, Point): - raise TypeError('A Point must be supplied') - - def _pdict_list(self, other, num): - """Creates a list from self to other using _dcm_dict. """ - outlist = [[self]] - oldlist = [[]] - while outlist != oldlist: - oldlist = outlist[:] - for i, v in enumerate(outlist): - templist = list(v[-1]._pdlist[num].keys()) - for i2, v2 in enumerate(templist): - if not v.__contains__(v2): - littletemplist = v + [v2] - if not outlist.__contains__(littletemplist): - outlist.append(littletemplist) - for i, v in enumerate(oldlist): - if v[-1] != other: - outlist.remove(v) - outlist.sort(key=len) - if len(outlist) != 0: - return outlist[0] - raise ValueError('No Connecting Path found between ' + other.name + - ' and ' + self.name) - -
    [docs] def a1pt_theory(self, otherpoint, outframe, interframe): - """Sets the acceleration of this point with the 1-point theory. - - The 1-point theory for point acceleration looks like this: - - ^N a^P = ^B a^P + ^N a^O + ^N alpha^B x r^OP + ^N omega^B x (^N omega^B - x r^OP) + 2 ^N omega^B x ^B v^P - - where O is a point fixed in B, P is a point moving in B, and B is - rotating in frame N. - - Parameters - ========== - - otherpoint : Point - The first point of the 1-point theory (O) - outframe : ReferenceFrame - The frame we want this point's acceleration defined in (N) - fixedframe : ReferenceFrame - The intermediate frame in this calculation (B) - - Examples - ======== - - >>> from sympy.physics.mechanics import Point, ReferenceFrame, dynamicsymbols - >>> q = dynamicsymbols('q') - >>> q2 = dynamicsymbols('q2') - >>> qd = dynamicsymbols('q', 1) - >>> q2d = dynamicsymbols('q2', 1) - >>> N = ReferenceFrame('N') - >>> B = ReferenceFrame('B') - >>> B.set_ang_vel(N, 5 * B.y) - >>> O = Point('O') - >>> P = O.locatenew('P', q * B.x) - >>> P.set_vel(B, qd * B.x + q2d * B.y) - >>> O.set_vel(N, 0) - >>> P.a1pt_theory(O, N, B) - (-25*q + q'')*B.x + q2''*B.y - 10*q'*B.z - - """ - - _check_frame(outframe) - _check_frame(interframe) - self._check_point(otherpoint) - dist = self.pos_from(otherpoint) - v = self.vel(interframe) - a1 = otherpoint.acc(outframe) - a2 = self.acc(interframe) - omega = interframe.ang_vel_in(outframe) - alpha = interframe.ang_acc_in(outframe) - self.set_acc(outframe, a2 + 2 * (omega ^ v) + a1 + (alpha ^ dist) + - (omega ^ (omega ^ dist))) - return self.acc(outframe) -
    -
    [docs] def a2pt_theory(self, otherpoint, outframe, fixedframe): - """Sets the acceleration of this point with the 2-point theory. - - The 2-point theory for point acceleration looks like this: - - ^N a^P = ^N a^O + ^N alpha^B x r^OP + ^N omega^B x (^N omega^B x r^OP) - - where O and P are both points fixed in frame B, which is rotating in - frame N. - - Parameters - ========== - - otherpoint : Point - The first point of the 2-point theory (O) - outframe : ReferenceFrame - The frame we want this point's acceleration defined in (N) - fixedframe : ReferenceFrame - The frame in which both points are fixed (B) - - Examples - ======== - - >>> from sympy.physics.mechanics import Point, ReferenceFrame, dynamicsymbols - >>> q = dynamicsymbols('q') - >>> qd = dynamicsymbols('q', 1) - >>> N = ReferenceFrame('N') - >>> B = N.orientnew('B', 'Axis', [q, N.z]) - >>> O = Point('O') - >>> P = O.locatenew('P', 10 * B.x) - >>> O.set_vel(N, 5 * N.x) - >>> P.a2pt_theory(O, N, B) - - 10*q'**2*B.x + 10*q''*B.y - - """ - - _check_frame(outframe) - _check_frame(fixedframe) - self._check_point(otherpoint) - dist = self.pos_from(otherpoint) - a = otherpoint.acc(outframe) - omega = fixedframe.ang_vel_in(outframe) - alpha = fixedframe.ang_acc_in(outframe) - self.set_acc(outframe, a + (alpha ^ dist) + (omega ^ (omega ^ dist))) - return self.acc(outframe) -
    -
    [docs] def acc(self, frame): - """The acceleration Vector of this Point in a ReferenceFrame. - - Parameters - ========== - - frame : ReferenceFrame - The frame in which the returned acceleration vector will be defined in - - Examples - ======== - - >>> from sympy.physics.mechanics import Point, ReferenceFrame - >>> N = ReferenceFrame('N') - >>> p1 = Point('p1') - >>> p1.set_acc(N, 10 * N.x) - >>> p1.acc(N) - 10*N.x - - """ - - _check_frame(frame) - if not (frame in self._acc_dict): - if self._vel_dict[frame] != 0: - return (self._vel_dict[frame]).dt(frame) - else: - return 0 - return self._acc_dict[frame] -
    -
    [docs] def locatenew(self, name, value): - """Creates a new point with a position defined from this point. - - Parameters - ========== - - name : str - The name for the new point - value : Vector - The position of the new point relative to this point - - Examples - ======== - - >>> from sympy.physics.mechanics import ReferenceFrame, Point - >>> N = ReferenceFrame('N') - >>> P1 = Point('P1') - >>> P2 = P1.locatenew('P2', 10 * N.x) - - """ - - if not isinstance(name, str): - raise TypeError('Must supply a valid name') - value = _check_vector(value) - p = Point(name) - p.set_pos(self, value) - self.set_pos(p, -value) - return p -
    -
    [docs] def pos_from(self, otherpoint): - """Returns a Vector distance between this Point and the other Point. - - Parameters - ========== - - otherpoint : Point - The otherpoint we are locating this one relative to - - Examples - ======== - - >>> from sympy.physics.mechanics import Point, ReferenceFrame - >>> N = ReferenceFrame('N') - >>> p1 = Point('p1') - >>> p2 = Point('p2') - >>> p1.set_pos(p2, 10 * N.x) - >>> p1.pos_from(p2) - 10*N.x - - """ - - outvec = 0 - plist = self._pdict_list(otherpoint, 0) - for i in range(len(plist) - 1): - outvec += plist[i]._pos_dict[plist[i + 1]] - return outvec -
    -
    [docs] def set_acc(self, frame, value): - """Used to set the acceleration of this Point in a ReferenceFrame. - - Parameters - ========== - - value : Vector - The vector value of this point's acceleration in the frame - frame : ReferenceFrame - The frame in which this point's acceleration is defined - - Examples - ======== - - >>> from sympy.physics.mechanics import Point, ReferenceFrame - >>> N = ReferenceFrame('N') - >>> p1 = Point('p1') - >>> p1.set_acc(N, 10 * N.x) - >>> p1.acc(N) - 10*N.x - - """ - - value = _check_vector(value) - _check_frame(frame) - self._acc_dict.update({frame: value}) -
    -
    [docs] def set_pos(self, otherpoint, value): - """Used to set the position of this point w.r.t. another point. - - Parameters - ========== - - value : Vector - The vector which defines the location of this point - point : Point - The other point which this point's location is defined relative to - - Examples - ======== - - >>> from sympy.physics.mechanics import Point, ReferenceFrame - >>> N = ReferenceFrame('N') - >>> p1 = Point('p1') - >>> p2 = Point('p2') - >>> p1.set_pos(p2, 10 * N.x) - >>> p1.pos_from(p2) - 10*N.x - - """ - - value = _check_vector(value) - self._check_point(otherpoint) - self._pos_dict.update({otherpoint: value}) - otherpoint._pos_dict.update({self: -value}) -
    -
    [docs] def set_vel(self, frame, value): - """Sets the velocity Vector of this Point in a ReferenceFrame. - - Parameters - ========== - - value : Vector - The vector value of this point's velocity in the frame - frame : ReferenceFrame - The frame in which this point's velocity is defined - - Examples - ======== - - >>> from sympy.physics.mechanics import Point, ReferenceFrame - >>> N = ReferenceFrame('N') - >>> p1 = Point('p1') - >>> p1.set_vel(N, 10 * N.x) - >>> p1.vel(N) - 10*N.x - - """ - - value = _check_vector(value) - _check_frame(frame) - self._vel_dict.update({frame: value}) -
    -
    [docs] def v1pt_theory(self, otherpoint, outframe, interframe): - """Sets the velocity of this point with the 1-point theory. - - The 1-point theory for point velocity looks like this: - - ^N v^P = ^B v^P + ^N v^O + ^N omega^B x r^OP - - where O is a point fixed in B, P is a point moving in B, and B is - rotating in frame N. - - Parameters - ========== - - otherpoint : Point - The first point of the 2-point theory (O) - outframe : ReferenceFrame - The frame we want this point's velocity defined in (N) - interframe : ReferenceFrame - The intermediate frame in this calculation (B) - - Examples - ======== - - >>> from sympy.physics.mechanics import Point, ReferenceFrame, dynamicsymbols - >>> q = dynamicsymbols('q') - >>> q2 = dynamicsymbols('q2') - >>> qd = dynamicsymbols('q', 1) - >>> q2d = dynamicsymbols('q2', 1) - >>> N = ReferenceFrame('N') - >>> B = ReferenceFrame('B') - >>> B.set_ang_vel(N, 5 * B.y) - >>> O = Point('O') - >>> P = O.locatenew('P', q * B.x) - >>> P.set_vel(B, qd * B.x + q2d * B.y) - >>> O.set_vel(N, 0) - >>> P.v1pt_theory(O, N, B) - q'*B.x + q2'*B.y - 5*q*B.z - - """ - - _check_frame(outframe) - _check_frame(interframe) - self._check_point(otherpoint) - dist = self.pos_from(otherpoint) - v1 = self.vel(interframe) - v2 = otherpoint.vel(outframe) - omega = interframe.ang_vel_in(outframe) - self.set_vel(outframe, v1 + v2 + (omega ^ dist)) - return self.vel(outframe) -
    -
    [docs] def v2pt_theory(self, otherpoint, outframe, fixedframe): - """Sets the velocity of this point with the 2-point theory. - - The 2-point theory for point velocity looks like this: - - ^N v^P = ^N v^O + ^N omega^B x r^OP - - where O and P are both points fixed in frame B, which is rotating in - frame N. - - Parameters - ========== - - otherpoint : Point - The first point of the 2-point theory (O) - outframe : ReferenceFrame - The frame we want this point's velocity defined in (N) - fixedframe : ReferenceFrame - The frame in which both points are fixed (B) - - Examples - ======== - - >>> from sympy.physics.mechanics import Point, ReferenceFrame, dynamicsymbols - >>> q = dynamicsymbols('q') - >>> qd = dynamicsymbols('q', 1) - >>> N = ReferenceFrame('N') - >>> B = N.orientnew('B', 'Axis', [q, N.z]) - >>> O = Point('O') - >>> P = O.locatenew('P', 10 * B.x) - >>> O.set_vel(N, 5 * N.x) - >>> P.v2pt_theory(O, N, B) - 5*N.x + 10*q'*B.y - - """ - - _check_frame(outframe) - _check_frame(fixedframe) - self._check_point(otherpoint) - dist = self.pos_from(otherpoint) - v = otherpoint.vel(outframe) - omega = fixedframe.ang_vel_in(outframe) - self.set_vel(outframe, v + (omega ^ dist)) - return self.vel(outframe) -
    -
    [docs] def vel(self, frame): - """The velocity Vector of this Point in the ReferenceFrame. - - Parameters - ========== - - frame : ReferenceFrame - The frame in which the returned velocity vector will be defined in - - Examples - ======== - - >>> from sympy.physics.mechanics import Point, ReferenceFrame - >>> N = ReferenceFrame('N') - >>> p1 = Point('p1') - >>> p1.set_vel(N, 10 * N.x) - >>> p1.vel(N) - 10*N.x - - """ - - _check_frame(frame) - if not (frame in self._vel_dict): - raise ValueError('Velocity of point ' + self.name + ' has not been' - ' defined in ReferenceFrame ' + frame.name) - return self._vel_dict[frame]
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/mechanics/rigidbody.html b/dev-py3k/_modules/sympy/physics/mechanics/rigidbody.html deleted file mode 100644 index b6382e02449..00000000000 --- a/dev-py3k/_modules/sympy/physics/mechanics/rigidbody.html +++ /dev/null @@ -1,398 +0,0 @@ - - - - - - - - - - sympy.physics.mechanics.rigidbody — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.mechanics.rigidbody

    -__all__ = ['RigidBody']
    -
    -from sympy import sympify
    -from sympy.physics.mechanics.point import Point
    -from sympy.physics.mechanics.essential import ReferenceFrame, Dyadic
    -
    -
    -
    [docs]class RigidBody(object): - """An idealized rigid body. - - This is essentially a container which holds the various components which - describe a rigid body: a name, mass, center of mass, reference frame, and - inertia. - - All of these need to be supplied on creation, but can be changed - afterwards. - - Attributes - ========== - name : string - The body's name. - masscenter : Point - The point which represents the center of mass of the rigid body. - frame : ReferenceFrame - The ReferenceFrame which the rigid body is fixed in. - mass : Sympifyable - The body's mass. - inertia : (Dyadic, Point) - The body's inertia about a point; stored in a tuple as shown above. - - Examples - ======== - - >>> from sympy import Symbol - >>> from sympy.physics.mechanics import ReferenceFrame, Point, RigidBody - >>> from sympy.physics.mechanics import outer - >>> m = Symbol('m') - >>> A = ReferenceFrame('A') - >>> P = Point('P') - >>> I = outer (A.x, A.x) - >>> inertia_tuple = (I, P) - >>> B = RigidBody('B', P, A, m, inertia_tuple) - >>> # Or you could change them afterwards - >>> m2 = Symbol('m2') - >>> B.mass = m2 - - """ - - def __init__(self, name, masscenter, frame, mass, inertia): - if not isinstance(name, str): - raise TypeError('Supply a valid name.') - self._name = name - self.set_masscenter(masscenter) - self.set_mass(mass) - self.set_frame(frame) - self.set_inertia(inertia) - self._pe = sympify(0) - - def __str__(self): - return self._name - - __repr__ = __str__ - - def get_frame(self): - return self._frame - - def set_frame(self, F): - if not isinstance(F, ReferenceFrame): - raise TypeError("RigdBody frame must be a ReferenceFrame object.") - self._frame = F - - frame = property(get_frame, set_frame) - - def get_masscenter(self): - return self._masscenter - - def set_masscenter(self, p): - if not isinstance(p, Point): - raise TypeError("RigidBody center of mass must be a Point object.") - self._masscenter = p - - masscenter = property(get_masscenter, set_masscenter) - - def get_mass(self): - return self._mass - - def set_mass(self, m): - self._mass = sympify(m) - - mass = property(get_mass, set_mass) - - def get_inertia(self): - return (self._inertia, self._inertia_point) - - def set_inertia(self, I): - if not isinstance(I[0], Dyadic): - raise TypeError("RigidBody inertia must be a Dyadic object.") - if not isinstance(I[1], Point): - raise TypeError("RigidBody inertia must be about a Point.") - self._inertia = I[0] - self._inertia_point = I[1] - - inertia = property(get_inertia, set_inertia) - -
    [docs] def linear_momentum(self, frame): - """ Linear momentum of the rigid body. - - The linear momentum L, of a rigid body B, with respect to frame N is - given by - - L = M * v* - - where M is the mass of the rigid body and v* is the velocity of - the mass center of B in the frame, N. - - Parameters - ========== - - frame : ReferenceFrame - The frame in which linear momentum is desired. - - Examples - ======== - - >>> from sympy.physics.mechanics import Point, ReferenceFrame, outer - >>> from sympy.physics.mechanics import RigidBody, dynamicsymbols - >>> M, v = dynamicsymbols('M v') - >>> N = ReferenceFrame('N') - >>> P = Point('P') - >>> P.set_vel(N, v * N.x) - >>> I = outer (N.x, N.x) - >>> Inertia_tuple = (I, P) - >>> B = RigidBody('B', P, N, M, Inertia_tuple) - >>> B.linear_momentum(N) - M*v*N.x - - """ - - return self.mass * self.masscenter.vel(frame) -
    -
    [docs] def angular_momentum(self, point, frame): - """ Angular momentum of the rigid body. - - The angular momentum H, about some point O, of a rigid body B, in a - frame N is given by - - H = I* . omega + r* x (M * v) - - where I* is the central inertia dyadic of B, omega is the angular - velocity of body B in the frame, N, r* is the position vector from - point O to the mass center of B, and v is the velocity of point O in - the frame, N. - - Parameters - ========== - - point : Point - The point about which angular momentum is desired. - - frame : ReferenceFrame - The frame in which angular momentum is desired. - - Examples - ======== - - >>> from sympy.physics.mechanics import Point, ReferenceFrame, outer - >>> from sympy.physics.mechanics import RigidBody, dynamicsymbols - >>> M, v, r, omega = dynamicsymbols('M v r omega') - >>> N = ReferenceFrame('N') - >>> b = ReferenceFrame('b') - >>> b.set_ang_vel(N, omega * b.x) - >>> P = Point('P') - >>> P.set_vel(N, 1 * N.x) - >>> I = outer (b.x, b.x) - >>> Inertia_tuple = (I, P) - >>> B = RigidBody('B', P, b, M, Inertia_tuple) - >>> B.angular_momentum(P, N) - omega*b.x - - """ - - return ((self.inertia[0] & self.frame.ang_vel_in(frame)) + - (point.vel(frame) ^ -self.masscenter.pos_from(point)) * - self.mass) -
    -
    [docs] def kinetic_energy(self, frame): - """Kinetic energy of the rigid body - - The kinetic energy, T, of a rigid body, B, is given by - - 'T = 1/2 (I omega^2 + m v^2)' - - where I and m are the central inertia dyadic and mass of rigid body B, - respectively, omega is the body's angular velocity and v is the - velocity of the body's mass center in the supplied ReferenceFrame. - - Parameters - ========== - - frame : ReferenceFrame - The RigidBody's angular velocity and the velocity of it's mass - center is typically defined with respect to an inertial frame but - any relevant frame in which the velocity is known can be supplied. - - Examples - ======== - - >>> from sympy.physics.mechanics import Point, ReferenceFrame, outer - >>> from sympy.physics.mechanics import RigidBody - >>> from sympy import symbols - >>> M, v, r, omega = symbols('M v r omega') - >>> N = ReferenceFrame('N') - >>> b = ReferenceFrame('b') - >>> b.set_ang_vel(N, omega * b.x) - >>> P = Point('P') - >>> P.set_vel(N, v * N.x) - >>> I = outer (b.x, b.x) - >>> inertia_tuple = (I, P) - >>> B = RigidBody('B', P, b, M, inertia_tuple) - >>> B.kinetic_energy(N) - M*v**2/2 + omega**2/2 - - """ - - rotational_KE = (self.frame.ang_vel_in(frame) & (self.inertia[0] & - self.frame.ang_vel_in(frame)) / sympify(2)) - - translational_KE = (self.mass * (self.masscenter.vel(frame) & - self.masscenter.vel(frame)) / sympify(2)) - - return rotational_KE + translational_KE -
    -
    [docs] def set_potential_energy(self, scalar): - """Used to set the potential energy of this RigidBody. - - Parameters - ========== - - scalar: Sympifyable - The potential energy (a scalar) of the RigidBody. - - Examples - ======== - - >>> from sympy.physics.mechanics import Particle, Point, outer - >>> from sympy.physics.mechanics import RigidBody, ReferenceFrame - >>> from sympy import symbols - >>> b = ReferenceFrame('b') - >>> M, g, h = symbols('M g h') - >>> P = Point('P') - >>> I = outer (b.x, b.x) - >>> Inertia_tuple = (I, P) - >>> B = RigidBody('B', P, b, M, Inertia_tuple) - >>> B.set_potential_energy(M * g * h) - - """ - - self._pe = sympify(scalar) -
    - @property -
    [docs] def potential_energy(self): - """The potential energy of the RigidBody. - - Examples - ======== - - >>> from sympy.physics.mechanics import RigidBody, Point, outer, ReferenceFrame - >>> from sympy import symbols - >>> M, g, h = symbols('M g h') - >>> b = ReferenceFrame('b') - >>> P = Point('P') - >>> I = outer (b.x, b.x) - >>> Inertia_tuple = (I, P) - >>> B = RigidBody('B', P, b, M, Inertia_tuple) - >>> B.set_potential_energy(M * g * h) - >>> B.potential_energy - M*g*h - - """ - - return self._pe
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/paulialgebra.html b/dev-py3k/_modules/sympy/physics/paulialgebra.html deleted file mode 100644 index 9fa06eefd90..00000000000 --- a/dev-py3k/_modules/sympy/physics/paulialgebra.html +++ /dev/null @@ -1,279 +0,0 @@ - - - - - - - - - - sympy.physics.paulialgebra — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.paulialgebra

    -"""
    -This module implements Pauli algebra by subclassing Symbol. Only algebraic
    -properties of Pauli matrices are used (we don't use the Matrix class).
    -
    -See the documentation to the class Pauli for examples.
    -
    -See also:
    -    http://en.wikipedia.org/wiki/Pauli_matrices
    -"""
    -
    -from sympy import Symbol, I
    -
    -__all__ = ['evaluate_pauli_product']
    -
    -
    -def delta(i, j):
    -    """
    -    Returns 1 if i == j, else 0.
    -
    -    This is used in the multiplication of Pauli matrices.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.physics.paulialgebra import delta
    -    >>> delta(1, 1)
    -    1
    -    >>> delta(2, 3)
    -    0
    -    """
    -    if i == j:
    -        return 1
    -    else:
    -        return 0
    -
    -
    -def epsilon(i, j, k):
    -    """
    -    Return 1 if i,j,k is equal to (1,2,3), (2,3,1), or (3,1,2);
    -    -1 if i,j,k is equal to (1,3,2), (3,2,1), or (2,1,3);
    -    else return 0.
    -
    -    This is used in the multiplication of Pauli matrices.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.physics.paulialgebra import epsilon
    -    >>> epsilon(1, 2, 3)
    -    1
    -    >>> epsilon(1, 3, 2)
    -    -1
    -    """
    -    if (i, j, k) in [(1, 2, 3), (2, 3, 1), (3, 1, 2)]:
    -        return 1
    -    elif (i, j, k) in [(1, 3, 2), (3, 2, 1), (2, 1, 3)]:
    -        return -1
    -    else:
    -        return 0
    -
    -
    -class Pauli(Symbol):
    -    """The class representing algebraic properties of Pauli matrices
    -
    -    If the left multiplication of symbol or number with Pauli matrix is needed,
    -    please use parentheses  to separate Pauli and symbolic multiplication
    -    (for example: 2*I*(Pauli(3)*Pauli(2)))
    -
    -    Another variant is to use evaluate_pauli_product function to evaluate
    -    the product of Pauli matrices and other symbols (with commutative
    -    multiply rules)
    -
    -    See Also
    -    =======
    -    evaluate_pauli_product
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.physics.paulialgebra import Pauli
    -    >>> Pauli(1)
    -    sigma1
    -    >>> Pauli(1)*Pauli(2)
    -    I*sigma3
    -    >>> Pauli(1)*Pauli(1)
    -    1
    -    >>> Pauli(3)**4
    -    1
    -    >>> Pauli(1)*Pauli(2)*Pauli(3)
    -    I
    -
    -    >>> from sympy import I
    -    >>> I*(Pauli(2)*Pauli(3))
    -    -sigma1
    -
    -    >>> from sympy.physics.paulialgebra import evaluate_pauli_product
    -    >>> f = I*Pauli(2)*Pauli(3)
    -    >>> f
    -    I*sigma2*sigma3
    -    >>> evaluate_pauli_product(f)
    -    -sigma1
    -
    -    """
    -
    -    __slots__ = ["i"]
    -
    -    def __new__(cls, i):
    -        if not i in [1, 2, 3]:
    -            raise IndexError("Invalid Pauli index")
    -        obj = Symbol.__new__(cls, "sigma%d" % i, commutative=False)
    -        obj.i = i
    -        return obj
    -
    -    def __getnewargs__(self):
    -        return (self.i,)
    -
    -    # FIXME don't work for -I*Pauli(2)*Pauli(3)
    -    def __mul__(self, other):
    -        if isinstance(other, Pauli):
    -            j = self.i
    -            k = other.i
    -            return delta(j, k) \
    -                + I*epsilon(j, k, 1)*Pauli(1) \
    -                + I*epsilon(j, k, 2)*Pauli(2) \
    -                + I*epsilon(j, k, 3)*Pauli(3)
    -        return super(Pauli, self).__mul__(other)
    -
    -    def _eval_power(b, e):
    -        if e.is_Integer and e.is_positive:
    -            return super(Pauli, b).__pow__(int(e) % 2)
    -
    -
    -
    [docs]def evaluate_pauli_product(arg): - '''Help function to evaluate Pauli matrices product - with symbolic objects - - Parameters - ========== - - arg: symbolic expression that contains Paulimatrices - - Examples - ======== - - >>> from sympy.physics.paulialgebra import Pauli, evaluate_pauli_product - >>> from sympy import I - >>> evaluate_pauli_product(I*Pauli(1)*Pauli(2)) - -sigma3 - - >>> from sympy.abc import x,y - >>> evaluate_pauli_product(x**2*Pauli(2)*Pauli(1)) - -I*x**2*sigma3 - ''' - tmp = arg.as_coeff_mul() - sigma_product = 1 - com_product = 1 - for el in tmp[1]: - if isinstance(el, Pauli): - sigma_product *= el - else: - com_product *= el - return (tmp[0]*sigma_product*com_product)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/qho_1d.html b/dev-py3k/_modules/sympy/physics/qho_1d.html deleted file mode 100644 index d11158bbbea..00000000000 --- a/dev-py3k/_modules/sympy/physics/qho_1d.html +++ /dev/null @@ -1,183 +0,0 @@ - - - - - - - - - - sympy.physics.qho_1d — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.qho_1d

    -from sympy.core import S, pi, Rational
    -from sympy.functions import hermite, sqrt, exp, factorial
    -from sympy.physics.quantum.constants import hbar
    -
    -
    -
    [docs]def psi_n(n, x, m, omega): - """ - Returns the wavefunction psi_{n} for the One-dimensional harmonic oscillator. - - ``n`` - the "nodal" quantum number. Corresponds to the number of nodes in the - wavefunction. n >= 0 - ``x`` - x coordinate - ``m`` - mass of the particle - ``omega`` - angular frequency of the oscillator - - Examples - ======== - - >>> from sympy.physics.qho_1d import psi_n - >>> from sympy import var - >>> var("x m omega") - (x, m, omega) - >>> psi_n(0, x, m, omega) - (m*omega)**(1/4)*exp(-m*omega*x**2/(2*hbar))/(hbar**(1/4)*pi**(1/4)) - - """ - - # sympify arguments - n, x, m, omega = list(map(S, [n, x, m, omega])) - nu = m * omega / hbar - # normalization coefficient - C = (nu/pi)**(S(1)/4) * sqrt(1/(2**n*factorial(n))) - - return C * exp(-nu* x**2 /2) * hermite(n, sqrt(nu)*x) - -
    -
    [docs]def E_n(n, omega): - """ - Returns the Energy of the One-dimensional harmonic oscillator - - ``n`` - the "nodal" quantum number - ``omega`` - the harmonic oscillator angular frequency - - The unit of the returned value matches the unit of hw, since the energy is - calculated as: - - E_n = hbar * omega*(n + 1/2) - - Examples - ======== - - >>> from sympy.physics.qho_1d import E_n - >>> from sympy import var - >>> var("x omega") - (x, omega) - >>> E_n(x, omega) - hbar*omega*(x + 1/2) - """ - - return hbar * omega*(n + Rational(1, 2))
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/quantum/anticommutator.html b/dev-py3k/_modules/sympy/physics/quantum/anticommutator.html deleted file mode 100644 index b1618456c8d..00000000000 --- a/dev-py3k/_modules/sympy/physics/quantum/anticommutator.html +++ /dev/null @@ -1,260 +0,0 @@ - - - - - - - - - - sympy.physics.quantum.anticommutator — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.quantum.anticommutator

    -"""The anti-commutator: ``{A,B} = A*B + B*A``."""
    -
    -from sympy import S, Expr, Mul, Integer
    -from sympy.printing.pretty.stringpict import prettyForm
    -
    -from sympy.physics.quantum.operator import Operator
    -from sympy.physics.quantum.dagger import Dagger
    -
    -__all__ = [
    -    'AntiCommutator'
    -]
    -
    -#-----------------------------------------------------------------------------
    -# Anti-commutator
    -#-----------------------------------------------------------------------------
    -
    -
    -
    [docs]class AntiCommutator(Expr): - """The standard anticommutator, in an unevaluated state. - - Evaluating an anticommutator is defined [1]_ as: ``{A, B} = A*B + B*A``. - This class returns the anticommutator in an unevaluated form. To evaluate - the anticommutator, use the ``.doit()`` method. - - Cannonical ordering of an anticommutator is ``{A, B}`` for ``A < B``. The - arguments of the anticommutator are put into canonical order using - ``__cmp__``. If ``B < A``, then ``{A, B}`` is returned as ``{B, A}``. - - Parameters - ========== - - A : Expr - The first argument of the anticommutator {A,B}. - B : Expr - The second argument of the anticommutator {A,B}. - - Examples - ======== - - >>> from sympy import symbols - >>> from sympy.physics.quantum import AntiCommutator - >>> from sympy.physics.quantum import Operator, Dagger - >>> x, y = symbols('x,y') - >>> A = Operator('A') - >>> B = Operator('B') - - Create an anticommutator and use ``doit()`` to multiply them out. - - >>> ac = AntiCommutator(A,B); ac - {A,B} - >>> ac.doit() - A*B + B*A - - The commutator orders it arguments in canonical order: - - >>> ac = AntiCommutator(B,A); ac - {A,B} - - Commutative constants are factored out: - - >>> AntiCommutator(3*x*A,x*y*B) - 3*x**2*y*{A,B} - - Adjoint operations applied to the anticommutator are properly applied to - the arguments: - - >>> Dagger(AntiCommutator(A,B)) - {Dagger(A),Dagger(B)} - - References - ========== - - .. [1] http://en.wikipedia.org/wiki/Commutator - """ - is_commutative = False - - def __new__(cls, A, B): - r = cls.eval(A, B) - if r is not None: - return r - obj = Expr.__new__(cls, A, B) - return obj - - @classmethod - def eval(cls, a, b): - if not (a and b): - return S.Zero - if a == b: - return Integer(2)*a**2 - if a.is_commutative or b.is_commutative: - return Integer(2)*a*b - - # [xA,yB] -> xy*[A,B] - # from sympy.physics.qmul import QMul - ca, nca = a.args_cnc() - cb, ncb = b.args_cnc() - c_part = ca + cb - if c_part: - return Mul(Mul(*c_part), cls(Mul._from_args(nca), Mul._from_args(ncb))) - - # Canonical ordering of arguments - #The Commutator [A,B] is on canonical form if A < B. - if a.compare(b) == 1: - return cls(b, a) - -
    [docs] def doit(self, **hints): - """ Evaluate anticommutator """ - A = self.args[0] - B = self.args[1] - if isinstance(A, Operator) and isinstance(B, Operator): - try: - comm = A._eval_anticommutator(B, **hints) - except NotImplementedError: - try: - comm = B._eval_anticommutator(A, **hints) - except NotImplementedError: - comm = None - if comm is not None: - return comm.doit(**hints) - return (A*B + B*A).doit(**hints) -
    - def _eval_adjoint(self): - return AntiCommutator(Dagger(self.args[0]), Dagger(self.args[1])) - - def _sympyrepr(self, printer, *args): - return "%s(%s,%s)" % ( - self.__class__.__name__, printer._print( - self.args[0]), printer._print(self.args[1]) - ) - - def _sympystr(self, printer, *args): - return "{%s,%s}" % (self.args[0], self.args[1]) - - def _pretty(self, printer, *args): - pform = printer._print(self.args[0], *args) - pform = prettyForm(*pform.right((prettyForm(',')))) - pform = prettyForm(*pform.right((printer._print(self.args[1], *args)))) - pform = prettyForm(*pform.parens(left='{', right='}')) - return pform - - def _latex(self, printer, *args): - return "\\left\\{%s,%s\\right\\}" % tuple([ - printer._print(arg, *args) for arg in self.args])
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/quantum/cartesian.html b/dev-py3k/_modules/sympy/physics/quantum/cartesian.html deleted file mode 100644 index 51e79da60b6..00000000000 --- a/dev-py3k/_modules/sympy/physics/quantum/cartesian.html +++ /dev/null @@ -1,454 +0,0 @@ - - - - - - - - - - sympy.physics.quantum.cartesian — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.quantum.cartesian

    -"""Operators and states for 1D cartesian position and momentum.
    -
    -TODO:
    -
    -* Add 3D classes to mappings in operatorset.py
    -
    -"""
    -
    -from sympy import DiracDelta, exp, I, Interval, pi, S, sqrt
    -
    -from sympy.physics.quantum.constants import hbar
    -from sympy.physics.quantum.hilbert import L2
    -from sympy.physics.quantum.operator import DifferentialOperator, HermitianOperator
    -from sympy.physics.quantum.state import Ket, Bra, State
    -
    -__all__ = [
    -    'XOp',
    -    'YOp',
    -    'ZOp',
    -    'PxOp',
    -    'X',
    -    'Y',
    -    'Z',
    -    'Px',
    -    'XKet',
    -    'XBra',
    -    'PxKet',
    -    'PxBra',
    -    'PositionState3D',
    -    'PositionKet3D',
    -    'PositionBra3D'
    -]
    -
    -#-------------------------------------------------------------------------
    -# Position operators
    -#-------------------------------------------------------------------------
    -
    -
    -
    [docs]class XOp(HermitianOperator): - """1D cartesian position operator.""" - - @classmethod - def default_args(self): - return ("X",) - - @classmethod - def _eval_hilbert_space(self, args): - return L2(Interval(S.NegativeInfinity, S.Infinity)) - - def _eval_commutator_PxOp(self, other): - return I*hbar - - def _apply_operator_XKet(self, ket): - return ket.position*ket - - def _apply_operator_PositionKet3D(self, ket): - return ket.position_x*ket - - def _represent_PxKet(self, basis, **options): - index = options.pop("index", 1) - - states = basis._enumerate_state(2, start_index=index) - coord1 = states[0].momentum - coord2 = states[1].momentum - d = DifferentialOperator(coord1) - delta = DiracDelta(coord1 - coord2) - - return I*hbar*(d*delta) - -
    -
    [docs]class YOp(HermitianOperator): - """ Y cartesian coordinate operator (for 2D or 3D systems) """ - - @classmethod - def default_args(self): - return ("Y",) - - @classmethod - def _eval_hilbert_space(self, args): - return L2(Interval(S.NegativeInfinity, S.Infinity)) - - def _apply_operator_PositionKet3D(self, ket): - return ket.position_y*ket - -
    -
    [docs]class ZOp(HermitianOperator): - """ Z cartesian coordinate operator (for 3D systems) """ - - @classmethod - def default_args(self): - return ("Z",) - - @classmethod - def _eval_hilbert_space(self, args): - return L2(Interval(S.NegativeInfinity, S.Infinity)) - - def _apply_operator_PositionKet3D(self, ket): - return ket.position_z*ket - -#------------------------------------------------------------------------- -# Momentum operators -#------------------------------------------------------------------------- - -
    -
    [docs]class PxOp(HermitianOperator): - """1D cartesian momentum operator.""" - - @classmethod - def default_args(self): - return ("Px",) - - @classmethod - def _eval_hilbert_space(self, args): - return L2(Interval(S.NegativeInfinity, S.Infinity)) - - def _apply_operator_PxKet(self, ket): - return ket.momentum*ket - - def _represent_XKet(self, basis, **options): - index = options.pop("index", 1) - - states = basis._enumerate_state(2, start_index=index) - coord1 = states[0].position - coord2 = states[1].position - d = DifferentialOperator(coord1) - delta = DiracDelta(coord1 - coord2) - - return -I*hbar*(d*delta) -
    -X = XOp('X') -Y = YOp('Y') -Z = ZOp('Z') -Px = PxOp('Px') - -#------------------------------------------------------------------------- -# Position eigenstates -#------------------------------------------------------------------------- - - -
    [docs]class XKet(Ket): - """1D cartesian position eigenket.""" - - @classmethod - def _operators_to_state(self, op, **options): - return self.__new__(self, *_lowercase_labels(op), **options) - - def _state_to_operators(self, op_class, **options): - return op_class.__new__(op_class, - *_uppercase_labels(self), **options) - - @classmethod - def default_args(self): - return ("x",) - - @classmethod - def dual_class(self): - return XBra - - @property -
    [docs] def position(self): - """The position of the state.""" - return self.label[0] -
    - def _enumerate_state(self, num_states, **options): - return _enumerate_continuous_1D(self, num_states, **options) - - def _eval_innerproduct_XBra(self, bra, **hints): - return DiracDelta(self.position - bra.position) - - def _eval_innerproduct_PxBra(self, bra, **hints): - return exp(-I*self.position*bra.momentum/hbar)/sqrt(2*pi*hbar) - -
    -
    [docs]class XBra(Bra): - """1D cartesian position eigenbra.""" - - @classmethod - def default_args(self): - return ("x",) - - @classmethod - def dual_class(self): - return XKet - - @property -
    [docs] def position(self): - """The position of the state.""" - return self.label[0] - -
    -
    [docs]class PositionState3D(State): - """ Base class for 3D cartesian position eigenstates """ - - @classmethod - def _operators_to_state(self, op, **options): - return self.__new__(self, *_lowercase_labels(op), **options) - - def _state_to_operators(self, op_class, **options): - return op_class.__new__(op_class, - *_uppercase_labels(self), **options) - - @classmethod - def default_args(self): - return ("x", "y", "z") - - @property -
    [docs] def position_x(self): - """ The x coordinate of the state """ - return self.label[0] -
    - @property -
    [docs] def position_y(self): - """ The y coordinate of the state """ - return self.label[1] -
    - @property -
    [docs] def position_z(self): - """ The z coordinate of the state """ - return self.label[2] - -
    -
    [docs]class PositionKet3D(Ket, PositionState3D): - """ 3D cartesian position eigenket """ - - def _eval_innerproduct_PositionBra3D(self, bra, **options): - x_diff = self.position_x - bra.position_x - y_diff = self.position_y - bra.position_y - z_diff = self.position_z - bra.position_z - - return DiracDelta(x_diff)*DiracDelta(y_diff)*DiracDelta(z_diff) - - @classmethod - def dual_class(self): - return PositionBra3D - -
    -
    [docs]class PositionBra3D(Bra, PositionState3D): - """ 3D cartesian position eigenbra """ - - @classmethod - def dual_class(self): - return PositionKet3D - -#------------------------------------------------------------------------- -# Momentum eigenstates -#------------------------------------------------------------------------- - -
    -
    [docs]class PxKet(Ket): - """1D cartesian momentum eigenket.""" - - @classmethod - def _operators_to_state(self, op, **options): - return self.__new__(self, *_lowercase_labels(op), **options) - - def _state_to_operators(self, op_class, **options): - return op_class.__new__(op_class, - *_uppercase_labels(self), **options) - - @classmethod - def default_args(self): - return ("px",) - - @classmethod - def dual_class(self): - return PxBra - - @property -
    [docs] def momentum(self): - """The momentum of the state.""" - return self.label[0] -
    - def _enumerate_state(self, *args, **options): - return _enumerate_continuous_1D(self, *args, **options) - - def _eval_innerproduct_XBra(self, bra, **hints): - return exp(I*self.momentum*bra.position/hbar)/sqrt(2*pi*hbar) - - def _eval_innerproduct_PxBra(self, bra, **hints): - return DiracDelta(self.momentum - bra.momentum) - -
    -
    [docs]class PxBra(Bra): - """1D cartesian momentum eigenbra.""" - - @classmethod - def default_args(self): - return ("px",) - - @classmethod - def dual_class(self): - return PxKet - - @property -
    [docs] def momentum(self): - """The momentum of the state.""" - return self.label[0] - -#------------------------------------------------------------------------- -# Global helper functions -#------------------------------------------------------------------------- - -
    -def _enumerate_continuous_1D(*args, **options): - state = args[0] - num_states = args[1] - state_class = state.__class__ - index_list = options.pop('index_list', []) - - if len(index_list) == 0: - start_index = options.pop('start_index', 1) - index_list = list(range(start_index, start_index + num_states)) - - enum_states = [0 for i in range(len(index_list))] - - for i, ind in enumerate(index_list): - label = state.args[0] - enum_states[i] = state_class(str(label) + "_" + str(ind), **options) - - return enum_states - - -def _lowercase_labels(ops): - if not isinstance(ops, set): - ops = [ops] - - return [str(arg.label[0]).lower() for arg in ops] - - -def _uppercase_labels(ops): - if not isinstance(ops, set): - ops = [ops] - - new_args = [str(arg.label[0])[0].upper() + - str(arg.label[0])[1:] for arg in ops] - - return new_args -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/quantum/cg.html b/dev-py3k/_modules/sympy/physics/quantum/cg.html deleted file mode 100644 index 9e380194194..00000000000 --- a/dev-py3k/_modules/sympy/physics/quantum/cg.html +++ /dev/null @@ -1,836 +0,0 @@ - - - - - - - - - - sympy.physics.quantum.cg — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.quantum.cg

    -#TODO:
    -# -Implement Clebsch-Gordan symmetries
    -# -Improve simplification method
    -# -Implement new simpifications
    -"""Clebsch-Gordon Coefficients."""
    -
    -from sympy import (Add, expand, Eq, Expr, Mul, Piecewise, Pow, sqrt, Sum,
    -                   symbols, sympify, Wild)
    -from sympy.printing.pretty.stringpict import prettyForm, stringPict
    -
    -from sympy.functions.special.tensor_functions import KroneckerDelta
    -from sympy.physics.wigner import clebsch_gordan, wigner_3j, wigner_6j, wigner_9j
    -
    -__all__ = [
    -    'CG',
    -    'Wigner3j',
    -    'Wigner6j',
    -    'Wigner9j',
    -    'cg_simp'
    -]
    -
    -#-----------------------------------------------------------------------------
    -# CG Coefficients
    -#-----------------------------------------------------------------------------
    -
    -
    -
    [docs]class Wigner3j(Expr): - """Class for the Wigner-3j symbols - - Wigner 3j-symbols are coefficients determined by the coupling of - two angular momenta. When created, they are expressed as symbolic - quantities that, for numerical parameters, can be evaluated using the - ``.doit()`` method [1]_. - - Parameters - ========== - - j1, m1, j2, m2, j3, m3 : Number, Symbol - Terms determining the angular momentum of coupled angular momentum - systems. - - Examples - ======== - - Declare a Wigner-3j coefficient and calcualte its value - - >>> from sympy.physics.quantum.cg import Wigner3j - >>> w3j = Wigner3j(6,0,4,0,2,0) - >>> w3j - Wigner3j(6, 0, 4, 0, 2, 0) - >>> w3j.doit() - sqrt(715)/143 - - See Also - ======== - - CG: Clebsch-Gordan coefficients - - References - ========== - - .. [1] Varshalovich, D A, Quantum Theory of Angular Momentum. 1988. - """ - - is_commutative = True - - def __new__(cls, j1, m1, j2, m2, j3, m3): - args = list(map(sympify, (j1, m1, j2, m2, j3, m3))) - return Expr.__new__(cls, *args) - - @property - def j1(self): - return self.args[0] - - @property - def m1(self): - return self.args[1] - - @property - def j2(self): - return self.args[2] - - @property - def m2(self): - return self.args[3] - - @property - def j3(self): - return self.args[4] - - @property - def m3(self): - return self.args[5] - - @property - def is_symbolic(self): - return not all([arg.is_number for arg in self.args]) - - # This is modified from the _print_Matrix method - def _pretty(self, printer, *args): - m = ((printer._print(self.j1), printer._print(self.m1)), - (printer._print(self.j2), printer._print(self.m2)), - (printer._print(self.j3), printer._print(self.m3))) - hsep = 2 - vsep = 1 - maxw = [-1] * 3 - for j in range(3): - maxw[j] = max([ m[j][i].width() for i in range(2) ]) - D = None - for i in range(2): - D_row = None - for j in range(3): - s = m[j][i] - wdelta = maxw[j] - s.width() - wleft = wdelta //2 - wright = wdelta - wleft - - s = prettyForm(*s.right(' '*wright)) - s = prettyForm(*s.left(' '*wleft)) - - if D_row is None: - D_row = s - continue - D_row = prettyForm(*D_row.right(' '*hsep)) - D_row = prettyForm(*D_row.right(s)) - if D is None: - D = D_row - continue - for _ in range(vsep): - D = prettyForm(*D.below(' ')) - D = prettyForm(*D.below(D_row)) - D = prettyForm(*D.parens()) - return D - - def _latex(self, printer, *args): - label = list(map(printer._print, (self.j1, self.j2, self.j3, - self.m1, self.m2, self.m3))) - return r'\left(\begin{array}{ccc} %s & %s & %s \\ %s & %s & %s \end{array}\right)' % \ - tuple(label) - - def doit(self, **hints): - if self.is_symbolic: - raise ValueError("Coefficients must be numerical") - return wigner_3j(self.j1, self.j2, self.j3, self.m1, self.m2, self.m3) - -
    -
    [docs]class CG(Wigner3j): - """Class for Clebsch-Gordan coefficient - - Clebsch-Gordan coefficients describe the angular momentum coupling between - two systems. The coefficients give the expansion of a coupled total angular - momentum state and an uncoupled tensor product state. The Clebsch-Gordan - coefficients are defined as [1]_: - - .. math :: - C^{j_1,m_1}_{j_2,m_2,j_3,m_3} = \langle j_1,m_1;j_2,m_2 | j_3,m_3\\rangle - - Parameters - ========== - - j1, m1, j2, m2, j3, m3 : Number, Symbol - Terms determining the angular momentum of coupled angular momentum - systems. - - Examples - ======== - - Define a Clebsch-Gordan coefficient and evaluate its value - - >>> from sympy.physics.quantum.cg import CG - >>> from sympy import S - >>> cg = CG(S(3)/2, S(3)/2, S(1)/2, -S(1)/2, 1, 1) - >>> cg - CG(3/2, 3/2, 1/2, -1/2, 1, 1) - >>> cg.doit() - sqrt(3)/2 - - See Also - ======== - - Wigner3j: Wigner-3j symbols - - References - ========== - - .. [1] Varshalovich, D A, Quantum Theory of Angular Momentum. 1988. - """ - - def doit(self, **hints): - if self.is_symbolic: - raise ValueError("Coefficients must be numerical") - return clebsch_gordan(self.j1, self.j2, self.j3, self.m1, self.m2, self.m3) - - def _pretty(self, printer, *args): - bot = printer._print_seq( - (self.j1, self.m1, self.j2, self.m2), delimiter=',') - top = printer._print_seq((self.j3, self.m3), delimiter=',') - - pad = max(top.width(), bot.width()) - bot = prettyForm(*bot.left(' ')) - top = prettyForm(*top.left(' ')) - - if not pad == bot.width(): - bot = prettyForm(*bot.right(' ' * (pad - bot.width()))) - if not pad == top.width(): - top = prettyForm(*top.right(' ' * (pad - top.width()))) - s = stringPict('C' + ' '*pad) - s = prettyForm(*s.below(bot)) - s = prettyForm(*s.above(top)) - return s - - def _latex(self, printer, *args): - label = list(map(printer._print, (self.j3, self.m3, self.j1, - self.m1, self.j2, self.m2))) - return r'C^{%s,%s}_{%s,%s,%s,%s}' % tuple(label) - -
    -
    [docs]class Wigner6j(Expr): - """Class for the Wigner-6j symbols - - See Also - ======== - - Wigner3j: Wigner-3j symbols - - """ - def __new__(cls, j1, j2, j12, j3, j, j23): - args = list(map(sympify, (j1, j2, j12, j3, j, j23))) - return Expr.__new__(cls, *args) - - @property - def j1(self): - return self.args[0] - - @property - def j2(self): - return self.args[1] - - @property - def j12(self): - return self.args[2] - - @property - def j3(self): - return self.args[3] - - @property - def j(self): - return self.args[4] - - @property - def j23(self): - return self.args[5] - - @property - def is_symbolic(self): - return not all([arg.is_number for arg in self.args]) - - # This is modified from the _print_Matrix method - def _pretty(self, printer, *args): - m = ((printer._print(self.j1), printer._print(self.j3)), - (printer._print(self.j2), printer._print(self.j)), - (printer._print(self.j12), printer._print(self.j23))) - hsep = 2 - vsep = 1 - maxw = [-1] * 3 - for j in range(3): - maxw[j] = max([ m[j][i].width() for i in range(2) ]) - D = None - for i in range(2): - D_row = None - for j in range(3): - s = m[j][i] - wdelta = maxw[j] - s.width() - wleft = wdelta //2 - wright = wdelta - wleft - - s = prettyForm(*s.right(' '*wright)) - s = prettyForm(*s.left(' '*wleft)) - - if D_row is None: - D_row = s - continue - D_row = prettyForm(*D_row.right(' '*hsep)) - D_row = prettyForm(*D_row.right(s)) - if D is None: - D = D_row - continue - for _ in range(vsep): - D = prettyForm(*D.below(' ')) - D = prettyForm(*D.below(D_row)) - D = prettyForm(*D.parens(left='{', right='}')) - return D - - def _latex(self, printer, *args): - label = list(map(printer._print, (self.j1, self.j2, self.j12, - self.j3, self.j, self.j23))) - return r'\left\{\begin{array}{ccc} %s & %s & %s \\ %s & %s & %s \end{array}\right\}' % \ - tuple(label) - - def doit(self, **hints): - if self.is_symbolic: - raise ValueError("Coefficients must be numerical") - return wigner_6j(self.j1, self.j2, self.j12, self.j3, self.j, self.j3) - -
    -
    [docs]class Wigner9j(Expr): - """Class for the Wigner-9j symbols - - See Also - ======== - - Wigner3j: Wigner-3j symbols - - """ - def __new__(cls, j1, j2, j12, j3, j4, j34, j13, j24, j): - args = list(map(sympify, (j1, j2, j12, j3, j4, j34, j13, j24, j))) - return Expr.__new__(cls, *args) - - @property - def j1(self): - return self.args[0] - - @property - def j2(self): - return self.args[1] - - @property - def j12(self): - return self.args[2] - - @property - def j3(self): - return self.args[3] - - @property - def j4(self): - return self.args[4] - - @property - def j34(self): - return self.args[5] - - @property - def j13(self): - return self.args[6] - - @property - def j24(self): - return self.args[7] - - @property - def j(self): - return self.args[8] - - @property - def is_symbolic(self): - return not all([arg.is_number for arg in self.args]) - - # This is modified from the _print_Matrix method - def _pretty(self, printer, *args): - m = ( - (printer._print( - self.j1), printer._print(self.j3), printer._print(self.j13)), - (printer._print( - self.j2), printer._print(self.j4), printer._print(self.j24)), - (printer._print(self.j12), printer._print(self.j34), printer._print(self.j))) - hsep = 2 - vsep = 1 - maxw = [-1] * 3 - for j in range(3): - maxw[j] = max([ m[j][i].width() for i in range(3) ]) - D = None - for i in range(3): - D_row = None - for j in range(3): - s = m[j][i] - wdelta = maxw[j] - s.width() - wleft = wdelta //2 - wright = wdelta - wleft - - s = prettyForm(*s.right(' '*wright)) - s = prettyForm(*s.left(' '*wleft)) - - if D_row is None: - D_row = s - continue - D_row = prettyForm(*D_row.right(' '*hsep)) - D_row = prettyForm(*D_row.right(s)) - if D is None: - D = D_row - continue - for _ in range(vsep): - D = prettyForm(*D.below(' ')) - D = prettyForm(*D.below(D_row)) - D = prettyForm(*D.parens(left='{', right='}')) - return D - - def _latex(self, printer, *args): - label = list(map(printer._print, (self.j1, self.j2, self.j12, self.j3, - self.j4, self.j34, self.j13, self.j24, self.j))) - return r'\left\{\begin{array}{ccc} %s & %s & %s \\ %s & %s & %s \\ %s & %s & %s \end{array}\right\}' % \ - tuple(label) - - def doit(self, **hints): - if self.is_symbolic: - raise ValueError("Coefficients must be numerical") - return wigner_9j(self.j1, self.j2, self.j12, self.j3, self.j4, self.j34, self.j13, self.j24, self.j) - -
    -
    [docs]def cg_simp(e): - """Simplify and combine CG coefficients - - This function uses various symmetry and properties of sums and - products of Clebsch-Gordan coefficients to simplify statements - involving these terms [1]_. - - Examples - ======== - - Simplify the sum over CG(a,alpha,0,0,a,alpha) for all alpha to - 2*a+1 - - >>> from sympy.physics.quantum.cg import CG, cg_simp - >>> a = CG(1,1,0,0,1,1) - >>> b = CG(1,0,0,0,1,0) - >>> c = CG(1,-1,0,0,1,-1) - >>> cg_simp(a+b+c) - 3 - - See Also - ======== - - CG: Clebsh-Gordan coefficients - - References - ========== - - .. [1] Varshalovich, D A, Quantum Theory of Angular Momentum. 1988. - """ - if isinstance(e, Add): - return _cg_simp_add(e) - elif isinstance(e, Sum): - return _cg_simp_sum(e) - elif isinstance(e, Mul): - return Mul(*[cg_simp(arg) for arg in e.args]) - elif isinstance(e, Pow): - return Pow(cg_simp(e.base), e.exp) - else: - return e - -
    -def _cg_simp_add(e): - #TODO: Improve simplification method - """Takes a sum of terms involving Clebsch-Gordan coefficients and - simplifies the terms. - - First, we create two lists, cg_part, which is all the terms involving CG - coefficients, and other_part, which is all other terms. The cg_part list - is then passed to the simplification methods, which return the new cg_part - and any additional terms that are added to other_part - """ - cg_part = [] - other_part = [] - - e = expand(e) - for arg in e.args: - if arg.has(CG): - if isinstance(arg, Sum): - other_part.append(_cg_simp_sum(arg)) - elif isinstance(arg, Mul): - terms = 1 - for term in arg.args: - if isinstance(term, Sum): - terms *= _cg_simp_sum(term) - else: - terms *= term - if terms.has(CG): - cg_part.append(terms) - else: - other_part.append(terms) - else: - cg_part.append(arg) - else: - other_part.append(arg) - - cg_part, other = _check_varsh_871_1(cg_part) - other_part.append(other) - cg_part, other = _check_varsh_871_2(cg_part) - other_part.append(other) - cg_part, other = _check_varsh_872_9(cg_part) - other_part.append(other) - return Add(*cg_part) + Add(*other_part) - - -def _check_varsh_871_1(term_list): - # Sum( CG(a,alpha,b,0,a,alpha), (alpha, -a, a)) == KroneckerDelta(b,0) - a, alpha, b, lt = list(map(Wild, ('a', 'alpha', 'b', 'lt'))) - expr = lt*CG(a, alpha, b, 0, a, alpha) - simp = (2*a + 1)*KroneckerDelta(b, 0) - sign = lt/abs(lt) - build_expr = 2*a + 1 - index_expr = a + alpha - return _check_cg_simp(expr, simp, sign, lt, term_list, (a, alpha, b, lt), (a, b), build_expr, index_expr) - - -def _check_varsh_871_2(term_list): - # Sum((-1)**(a-alpha)*CG(a,alpha,a,-alpha,c,0),(alpha,-a,a)) - a, alpha, c, lt = list(map(Wild, ('a', 'alpha', 'c', 'lt'))) - expr = lt*CG(a, alpha, a, -alpha, c, 0) - simp = sqrt(2*a + 1)*KroneckerDelta(c, 0) - sign = (-1)**(a - alpha)*lt/abs(lt) - build_expr = 2*a + 1 - index_expr = a + alpha - return _check_cg_simp(expr, simp, sign, lt, term_list, (a, alpha, c, lt), (a, c), build_expr, index_expr) - - -def _check_varsh_872_9(term_list): - # Sum( CG(a,alpha,b,beta,c,gamma)*CG(a,alpha',b,beta',c,gamma), (gamma, -c, c), (c, abs(a-b), a+b)) - a, alpha, alphap, b, beta, betap, c, gamma, lt = list(map(Wild, ( - 'a', 'alpha', 'alphap', 'b', 'beta', 'betap', 'c', 'gamma', 'lt'))) - # Case alpha==alphap, beta==betap - - # For numerical alpha,beta - expr = lt*CG(a, alpha, b, beta, c, gamma)**2 - simp = 1 - sign = lt/abs(lt) - x = abs(a - b) - y = abs(alpha + beta) - build_expr = a + b + 1 - Piecewise((x, x > y), (0, Eq(x, y)), (y, y > x)) - index_expr = a + b - c - term_list, other1 = _check_cg_simp(expr, simp, sign, lt, term_list, (a, alpha, b, beta, c, gamma, lt), (a, alpha, b, beta), build_expr, index_expr) - - # For symbolic alpha,beta - x = abs(a - b) - y = a + b - build_expr = (y + 1 - x)*(x + y + 1) - index_expr = (c - x)*(x + c) + c + gamma - term_list, other2 = _check_cg_simp(expr, simp, sign, lt, term_list, (a, alpha, b, beta, c, gamma, lt), (a, alpha, b, beta), build_expr, index_expr) - - # Case alpha!=alphap or beta!=betap - # Note: this only works with leading term of 1, pattern matching is unable to match when there is a Wild leading term - # For numerical alpha,alphap,beta,betap - expr = CG(a, alpha, b, beta, c, gamma)*CG(a, alphap, b, betap, c, gamma) - simp = KroneckerDelta(alpha, alphap)*KroneckerDelta(beta, betap) - sign = sympify(1) - x = abs(a - b) - y = abs(alpha + beta) - build_expr = a + b + 1 - Piecewise((x, x > y), (0, Eq(x, y)), (y, y > x)) - index_expr = a + b - c - term_list, other3 = _check_cg_simp(expr, simp, sign, sympify(1), term_list, (a, alpha, alphap, b, beta, betap, c, gamma), (a, alpha, alphap, b, beta, betap), build_expr, index_expr) - - # For symbolic alpha,alphap,beta,betap - x = abs(a - b) - y = a + b - build_expr = (y + 1 - x)*(x + y + 1) - index_expr = (c - x)*(x + c) + c + gamma - term_list, other4 = _check_cg_simp(expr, simp, sign, sympify(1), term_list, (a, alpha, alphap, b, beta, betap, c, gamma), (a, alpha, alphap, b, beta, betap), build_expr, index_expr) - - return term_list, other1 + other2 + other4 - - -def _check_cg_simp(expr, simp, sign, lt, term_list, variables, dep_variables, build_index_expr, index_expr): - """ Checks for simplifications that can be made, returning a tuple of the - simplified list of terms and any terms generated by simplification. - - Parameters - ========== - - expr: expression - The expression with Wild terms that will be matched to the terms in - the sum - - simp: expression - The expression with Wild terms that is substituted in place of the CG - terms in the case of simplification - - sign: expression - The expression with Wild terms denoting the sign that is on expr that - must match - - lt: expression - The expression with Wild terms that gives the leading term of the - matched expr - - term_list: list - A list of all of the terms is the sum to be simplified - - variables: list - A list of all the variables that appears in expr - - dep_variables: list - A list of the variables that must match for all the terms in the sum, - i.e. the dependant variables - - build_index_expr: expression - Expression with Wild terms giving the number of elements in cg_index - - index_expr: expression - Expression with Wild terms giving the index terms have when storing - them to cg_index - - """ - other_part = 0 - i = 0 - while i < len(term_list): - sub_1 = _check_cg(term_list[i], expr, len(variables)) - if sub_1 is None: - i += 1 - continue - if not sympify(build_index_expr.subs(sub_1)).is_number: - i += 1 - continue - sub_dep = [(x, sub_1[x]) for x in dep_variables] - cg_index = [None] * build_index_expr.subs(sub_1) - for j in range(i, len(term_list)): - sub_2 = _check_cg(term_list[j], expr.subs(sub_dep), len(variables) - len(dep_variables), sign=(sign.subs(sub_1), sign.subs(sub_dep))) - if sub_2 is None: - continue - if not sympify(index_expr.subs(sub_dep).subs(sub_2)).is_number: - continue - cg_index[index_expr.subs(sub_dep).subs(sub_2)] = j, expr.subs(lt, 1).subs(sub_dep).subs(sub_2), lt.subs(sub_2), sign.subs(sub_dep).subs(sub_2) - if all(i is not None for i in cg_index): - min_lt = min(*[ abs(term[2]) for term in cg_index ]) - indicies = [ term[0] for term in cg_index] - indicies.sort() - indicies.reverse() - [ term_list.pop(i) for i in indicies ] - for term in cg_index: - if abs(term[2]) > min_lt: - term_list.append( (term[2] - min_lt*term[3]) * term[1] ) - other_part += min_lt * (sign*simp).subs(sub_1) - else: - i += 1 - return term_list, other_part - - -def _check_cg(cg_term, expr, length, sign=None): - """Checks whether a term matches the given expression""" - # TODO: Check for symmetries - matches = cg_term.match(expr) - if matches is None: - return - if sign is not None: - if not isinstance(sign, tuple): - raise TypeError('sign must be a tuple') - if not sign[0] == (sign[1]).subs(matches): - return - if len(matches) == length: - return matches - - -def _cg_simp_sum(e): - e = _check_varsh_sum_871_1(e) - e = _check_varsh_sum_871_2(e) - e = _check_varsh_sum_872_4(e) - return e - - -def _check_varsh_sum_871_1(e): - a = Wild('a') - alpha = symbols('alpha') - b = Wild('b') - match = e.match(Sum(CG(a, alpha, b, 0, a, alpha), (alpha, -a, a))) - if match is not None and len(match) == 2: - return ((2*a + 1)*KroneckerDelta(b, 0)).subs(match) - return e - - -def _check_varsh_sum_871_2(e): - a = Wild('a') - alpha = symbols('alpha') - c = Wild('c') - match = e.match( - Sum((-1)**(a - alpha)*CG(a, alpha, a, -alpha, c, 0), (alpha, -a, a))) - if match is not None and len(match) == 2: - return (sqrt(2*a + 1)*KroneckerDelta(c, 0)).subs(match) - return e - - -def _check_varsh_sum_872_4(e): - a = Wild('a') - alpha = Wild('alpha') - b = Wild('b') - beta = Wild('beta') - c = Wild('c') - cp = Wild('cp') - gamma = Wild('gamma') - gammap = Wild('gammap') - match1 = e.match(Sum(CG(a, alpha, b, beta, c, gamma)*CG( - a, alpha, b, beta, cp, gammap), (alpha, -a, a), (beta, -b, b))) - if match1 is not None and len(match1) == 8: - return (KroneckerDelta(c, cp)*KroneckerDelta(gamma, gammap)).subs(match1) - match2 = e.match(Sum( - CG(a, alpha, b, beta, c, gamma)**2, (alpha, -a, a), (beta, -b, b))) - if match2 is not None and len(match2) == 6: - return 1 - return e - - -def _cg_list(term): - if isinstance(term, CG): - return (term,), 1, 1 - cg = [] - coeff = 1 - if not (isinstance(term, Mul) or isinstance(term, Pow)): - raise NotImplementedError('term must be CG, Add, Mul or Pow') - if isinstance(term, Pow) and sympify(term.exp).is_number: - if sympify(term.exp).is_number: - [ cg.append(term.base) for _ in range(term.exp) ] - else: - return (term,), 1, 1 - if isinstance(term, Mul): - for arg in term.args: - if isinstance(arg, CG): - cg.append(arg) - else: - coeff *= arg - return cg, coeff, coeff/abs(coeff) -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/quantum/commutator.html b/dev-py3k/_modules/sympy/physics/quantum/commutator.html deleted file mode 100644 index b4fe07d5204..00000000000 --- a/dev-py3k/_modules/sympy/physics/quantum/commutator.html +++ /dev/null @@ -1,327 +0,0 @@ - - - - - - - - - - sympy.physics.quantum.commutator — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.quantum.commutator

    -"""The commutator: [A,B] = A*B - B*A."""
    -
    -from sympy import S, Expr, Mul, Add
    -from sympy.printing.pretty.stringpict import prettyForm
    -
    -from sympy.physics.quantum.dagger import Dagger
    -from sympy.physics.quantum.operator import Operator
    -
    -
    -__all__ = [
    -    'Commutator'
    -]
    -
    -#-----------------------------------------------------------------------------
    -# Commutator
    -#-----------------------------------------------------------------------------
    -
    -
    -
    [docs]class Commutator(Expr): - """The standard commutator, in an unevaluated state. - - Evaluating a commutator is defined [1]_ as: ``[A, B] = A*B - B*A``. This - class returns the commutator in an unevaluated form. To evaluate the - commutator, use the ``.doit()`` method. - - Cannonical ordering of a commutator is ``[A, B]`` for ``A < B``. The - arguments of the commutator are put into canonical order using ``__cmp__``. - If ``B < A``, then ``[B, A]`` is returned as ``-[A, B]``. - - Parameters - ========== - - A : Expr - The first argument of the commutator [A,B]. - B : Expr - The second argument of the commutator [A,B]. - - Examples - ======== - - >>> from sympy.physics.quantum import Commutator, Dagger, Operator - >>> from sympy.abc import x, y - >>> A = Operator('A') - >>> B = Operator('B') - >>> C = Operator('C') - - Create a commutator and use ``.doit()`` to evaluate it: - - >>> comm = Commutator(A, B) - >>> comm - [A,B] - >>> comm.doit() - A*B - B*A - - The commutator orders it arguments in canonical order: - - >>> comm = Commutator(B, A); comm - -[A,B] - - Commutative constants are factored out: - - >>> Commutator(3*x*A, x*y*B) - 3*x**2*y*[A,B] - - Using ``.expand(commutator=True)``, the standard commutator expansion rules - can be applied: - - >>> Commutator(A+B, C).expand(commutator=True) - [A,C] + [B,C] - >>> Commutator(A, B+C).expand(commutator=True) - [A,B] + [A,C] - >>> Commutator(A*B, C).expand(commutator=True) - [A,C]*B + A*[B,C] - >>> Commutator(A, B*C).expand(commutator=True) - [A,B]*C + B*[A,C] - - Adjoint operations applied to the commutator are properly applied to the - arguments: - - >>> Dagger(Commutator(A, B)) - -[Dagger(A),Dagger(B)] - - References - ========== - - .. [1] http://en.wikipedia.org/wiki/Commutator - """ - is_commutative = False - - def __new__(cls, A, B): - r = cls.eval(A, B) - if r is not None: - return r - obj = Expr.__new__(cls, A, B) - return obj - - @classmethod - def eval(cls, a, b): - if not (a and b): - return S.Zero - if a == b: - return S.Zero - if a.is_commutative or b.is_commutative: - return S.Zero - - # [xA,yB] -> xy*[A,B] - # from sympy.physics.qmul import QMul - ca, nca = a.args_cnc() - cb, ncb = b.args_cnc() - c_part = ca + cb - if c_part: - return Mul(Mul(*c_part), cls(Mul._from_args(nca), Mul._from_args(ncb))) - - # Canonical ordering of arguments - # The Commutator [A, B] is in canonical form if A < B. - if a.compare(b) == 1: - return S.NegativeOne*cls(b, a) - - def _eval_expand_commutator(self, **hints): - A = self.args[0] - B = self.args[1] - - if isinstance(A, Add): - # [A + B, C] -> [A, C] + [B, C] - sargs = [] - for term in A.args: - comm = Commutator(term, B) - if isinstance(comm, Commutator): - comm = comm._eval_expand_commutator() - sargs.append(comm) - return Add(*sargs) - elif isinstance(B, Add): - # [A, B + C] -> [A, B] + [A, C] - sargs = [] - for term in B.args: - comm = Commutator(A, term) - if isinstance(comm, Commutator): - comm = comm._eval_expand_commutator() - sargs.append(comm) - return Add(*sargs) - elif isinstance(A, Mul): - # [A*B, C] -> A*[B, C] + [A, C]*B - a = A.args[0] - b = Mul(*A.args[1:]) - c = B - comm1 = Commutator(b, c) - comm2 = Commutator(a, c) - if isinstance(comm1, Commutator): - comm1 = comm1._eval_expand_commutator() - if isinstance(comm2, Commutator): - comm2 = comm2._eval_expand_commutator() - first = Mul(a, comm1) - second = Mul(comm2, b) - return Add(first, second) - elif isinstance(B, Mul): - # [A, B*C] -> [A, B]*C + B*[A, C] - a = A - b = B.args[0] - c = Mul(*B.args[1:]) - comm1 = Commutator(a, b) - comm2 = Commutator(a, c) - if isinstance(comm1, Commutator): - comm1 = comm1._eval_expand_commutator() - if isinstance(comm2, Commutator): - comm2 = comm2._eval_expand_commutator() - first = Mul(comm1, c) - second = Mul(b, comm2) - return Add(first, second) - - # No changes, so return self - return self - -
    [docs] def doit(self, **hints): - """ Evaluate commutator """ - A = self.args[0] - B = self.args[1] - if isinstance(A, Operator) and isinstance(B, Operator): - try: - comm = A._eval_commutator(B, **hints) - except NotImplementedError: - try: - comm = -1*B._eval_commutator(A, **hints) - except NotImplementedError: - comm = None - if comm is not None: - return comm.doit(**hints) - return (A*B - B*A).doit(**hints) -
    - def _eval_adjoint(self): - return Commutator(Dagger(self.args[1]), Dagger(self.args[0])) - - def _sympyrepr(self, printer, *args): - return "%s(%s,%s)" % ( - self.__class__.__name__, printer._print( - self.args[0]), printer._print(self.args[1]) - ) - - def _sympystr(self, printer, *args): - return "[%s,%s]" % (self.args[0], self.args[1]) - - def _pretty(self, printer, *args): - pform = printer._print(self.args[0], *args) - pform = prettyForm(*pform.right((prettyForm(',')))) - pform = prettyForm(*pform.right((printer._print(self.args[1], *args)))) - pform = prettyForm(*pform.parens(left='[', right=']')) - return pform - - def _latex(self, printer, *args): - return "\\left[%s,%s\\right]" % tuple([ - printer._print(arg, *args) for arg in self.args])
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/quantum/dagger.html b/dev-py3k/_modules/sympy/physics/quantum/dagger.html deleted file mode 100644 index 91d1b4a24e8..00000000000 --- a/dev-py3k/_modules/sympy/physics/quantum/dagger.html +++ /dev/null @@ -1,202 +0,0 @@ - - - - - - - - - - sympy.physics.quantum.dagger — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.quantum.dagger

    -"""Hermitian conjugation."""
    -
    -from sympy.core import Expr
    -from sympy.functions.elementary.complexes import adjoint
    -
    -__all__ = [
    -    'Dagger'
    -]
    -
    -
    -
    [docs]class Dagger(adjoint): - """General Hermitian conjugate operation. - - Take the Hermetian conjugate of an argument [1]_. For matrices this - operation is equivalent to transpose and complex conjugate [2]_. - - Parameters - ========== - - arg : Expr - The sympy expression that we want to take the dagger of. - - Examples - ======== - - Daggering various quantum objects: - - >>> from sympy.physics.quantum.dagger import Dagger - >>> from sympy.physics.quantum.state import Ket, Bra - >>> from sympy.physics.quantum.operator import Operator - >>> Dagger(Ket('psi')) - <psi| - >>> Dagger(Bra('phi')) - |phi> - >>> Dagger(Operator('A')) - Dagger(A) - - Inner and outer products:: - - >>> from sympy.physics.quantum import InnerProduct, OuterProduct - >>> Dagger(InnerProduct(Bra('a'), Ket('b'))) - <b|a> - >>> Dagger(OuterProduct(Ket('a'), Bra('b'))) - |b><a| - - Powers, sums and products:: - - >>> A = Operator('A') - >>> B = Operator('B') - >>> Dagger(A*B) - Dagger(B)*Dagger(A) - >>> Dagger(A+B) - Dagger(A) + Dagger(B) - >>> Dagger(A**2) - Dagger(A)**2 - - Dagger also seamlessly handles complex numbers and matrices:: - - >>> from sympy import Matrix, I - >>> m = Matrix([[1,I],[2,I]]) - >>> m - [1, I] - [2, I] - >>> Dagger(m) - [ 1, 2] - [-I, -I] - - References - ========== - - .. [1] http://en.wikipedia.org/wiki/Hermitian_adjoint - .. [2] http://en.wikipedia.org/wiki/Hermitian_transpose - """ - - def __new__(cls, arg): - if hasattr(arg, 'adjoint'): - obj = arg.adjoint() - elif hasattr(arg, 'conjugate') and hasattr(arg, 'transpose'): - obj = arg.conjugate().transpose() - if obj is not None: - return obj - return Expr.__new__(cls, arg) -
    -adjoint.__name__ = "Dagger" -adjoint._sympyrepr = lambda a, b: "Dagger(%s)" % b._print(a.args[0]) -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/quantum/gate.html b/dev-py3k/_modules/sympy/physics/quantum/gate.html deleted file mode 100644 index fcec094975a..00000000000 --- a/dev-py3k/_modules/sympy/physics/quantum/gate.html +++ /dev/null @@ -1,1365 +0,0 @@ - - - - - - - - - - sympy.physics.quantum.gate — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.quantum.gate

    -"""An implementation of gates that act on qubits.
    -
    -Gates are unitary operators that act on the space of qubits.
    -
    -Medium Term Todo:
    -
    -* Optimize Gate._apply_operators_Qubit to remove the creation of many
    -  intermediate Qubit objects.
    -* Add commutation relationships to all operators and use this in gate_sort.
    -* Fix gate_sort and gate_simp.
    -* Get multi-target UGates plotting properly.
    -* Get UGate to work with either sympy/numpy matrices and output either
    -  format. This should also use the matrix slots.
    -"""
    -
    -from itertools import chain
    -import random
    -
    -from sympy import Add, I, Integer, Matrix, Mul, Pow, sqrt, Tuple
    -from sympy.core.numbers import Number
    -from sympy.core.compatibility import is_sequence
    -from sympy.printing.pretty.stringpict import prettyForm, stringPict
    -
    -from sympy.physics.quantum.anticommutator import AntiCommutator
    -from sympy.physics.quantum.commutator import Commutator
    -from sympy.physics.quantum.qexpr import QuantumError
    -from sympy.physics.quantum.hilbert import ComplexSpace
    -from sympy.physics.quantum.operator import (UnitaryOperator, Operator,
    -                                            HermitianOperator)
    -from sympy.physics.quantum.matrixutils import matrix_tensor_product, matrix_eye
    -from sympy.physics.quantum.matrixcache import matrix_cache
    -from sympy.physics.quantum.dagger import Dagger
    -
    -from sympy.matrices.matrices import MatrixBase
    -
    -__all__ = [
    -    'Gate',
    -    'CGate',
    -    'UGate',
    -    'OneQubitGate',
    -    'TwoQubitGate',
    -    'IdentityGate',
    -    'HadamardGate',
    -    'XGate',
    -    'YGate',
    -    'ZGate',
    -    'TGate',
    -    'PhaseGate',
    -    'SwapGate',
    -    'CNotGate',
    -    # Aliased gate names
    -    'CNOT',
    -    'SWAP',
    -    'H',
    -    'X',
    -    'Y',
    -    'Z',
    -    'T',
    -    'S',
    -    'Phase',
    -    'normalized',
    -    'gate_sort',
    -    'gate_simp',
    -    'random_circuit',
    -]
    -
    -#-----------------------------------------------------------------------------
    -# Gate Super-Classes
    -#-----------------------------------------------------------------------------
    -
    -_normalized = True
    -
    -
    -
    [docs]def normalized(normalize): - """Should Hadamard gates be normalized by a 1/sqrt(2). - - This is a global setting that can be used to simplify the look of various - expressions, by leaving of the leading 1/sqrt(2) of the Hadamard gate. - - Parameters - ---------- - normalize : bool - Should the Hadamard gate include the 1/sqrt(2) normalization factor? - When True, the Hadamard gate will have the 1/sqrt(2). When False, the - Hadamard gate will not have this factor. - """ - global _normalized - _normalized = normalize - -
    -def _validate_targets_controls(tandc): - tandc = list(tandc) - # Check for integers - for bit in tandc: - if not bit.is_Integer and not bit.is_Symbol: - raise TypeError('Integer expected, got: %r' % tandc[bit]) - # Detect duplicates - if len(list(set(tandc))) != len(tandc): - raise QuantumError( - 'Target/control qubits in a gate cannot be duplicated' - ) - - -
    [docs]class Gate(UnitaryOperator): - """Non-controlled unitary gate operator that acts on qubits. - - This is a general abstract gate that needs to be subclassed to do anything - useful. - - Parameters - ---------- - label : tuple, int - A list of the target qubits (as ints) that the gate will apply to. - - Examples - -------- - - - """ - - _label_separator = ',' - - gate_name = 'G' - gate_name_latex = 'G' - - #------------------------------------------------------------------------- - # Initialization/creation - #------------------------------------------------------------------------- - - @classmethod - def _eval_args(cls, args): - args = Tuple(*UnitaryOperator._eval_args(args)) - _validate_targets_controls(args) - return args - - @classmethod - def _eval_hilbert_space(cls, args): - """This returns the smallest possible Hilbert space.""" - return ComplexSpace(2)**(max(args) + 1) - - #------------------------------------------------------------------------- - # Properties - #------------------------------------------------------------------------- - - @property -
    [docs] def nqubits(self): - """The total number of qubits this gate acts on. - - For controlled gate subclasses this includes both target and control - qubits, so that, for examples the CNOT gate acts on 2 qubits. - """ - return len(self.targets) -
    - @property -
    [docs] def min_qubits(self): - """The minimum number of qubits this gate needs to act on.""" - return max(self.targets) + 1 -
    - @property -
    [docs] def targets(self): - """A tuple of target qubits.""" - return self.label -
    - @property - def gate_name_plot(self): - return r'$%s$' % self.gate_name_latex - - #------------------------------------------------------------------------- - # Gate methods - #------------------------------------------------------------------------- - -
    [docs] def get_target_matrix(self, format='sympy'): - """The matrix rep. of the target part of the gate. - - Parameters - ---------- - format : str - The format string ('sympy','numpy', etc.) - """ - raise NotImplementedError( - 'get_target_matrix is not implemented in Gate.') - - #------------------------------------------------------------------------- - # Apply - #------------------------------------------------------------------------- -
    - def _apply_operator_IntQubit(self, qubits, **options): - """Redirect an apply from IntQubit to Qubit""" - return self._apply_operator_Qubit(qubits, **options) - - def _apply_operator_Qubit(self, qubits, **options): - """Apply this gate to a Qubit.""" - - # Check number of qubits this gate acts on. - if qubits.nqubits < self.min_qubits: - raise QuantumError( - 'Gate needs a minimum of %r qubits to act on, got: %r' % - (self.min_qubits, qubits.nqubits) - ) - - # If the controls are not met, just return - if isinstance(self, CGate): - if not self.eval_controls(qubits): - return qubits - - targets = self.targets - target_matrix = self.get_target_matrix(format='sympy') - - # Find which column of the target matrix this applies to. - column_index = 0 - n = 1 - for target in targets: - column_index += n*qubits[target] - n = n << 1 - column = target_matrix[:, int(column_index)] - - # Now apply each column element to the qubit. - result = 0 - for index in range(column.rows): - # TODO: This can be optimized to reduce the number of Qubit - # creations. We should simply manipulate the raw list of qubit - # values and then build the new Qubit object once. - # Make a copy of the incoming qubits. - new_qubit = qubits.__class__(*qubits.args) - # Flip the bits that need to be flipped. - for bit in range(len(targets)): - if new_qubit[targets[bit]] != (index >> bit) & 1: - new_qubit = new_qubit.flip(targets[bit]) - # The value in that row and column times the flipped-bit qubit - # is the result for that part. - result += column[index]*new_qubit - return result - - #------------------------------------------------------------------------- - # Represent - #------------------------------------------------------------------------- - - def _represent_default_basis(self, **options): - return self._represent_ZGate(None, **options) - - def _represent_ZGate(self, basis, **options): - format = options.get('format', 'sympy') - nqubits = options.get('nqubits', 0) - if nqubits == 0: - raise QuantumError( - 'The number of qubits must be given as nqubits.') - - # Make sure we have enough qubits for the gate. - if nqubits < self.min_qubits: - raise QuantumError( - 'The number of qubits %r is too small for the gate.' % nqubits - ) - - target_matrix = self.get_target_matrix(format) - targets = self.targets - if isinstance(self, CGate): - controls = self.controls - else: - controls = [] - m = represent_zbasis( - controls, targets, target_matrix, nqubits, format - ) - return m - - #------------------------------------------------------------------------- - # Print methods - #------------------------------------------------------------------------- - - def _sympystr(self, printer, *args): - label = self._print_label(printer, *args) - return '%s(%s)' % (self.gate_name, label) - - def _pretty(self, printer, *args): - a = stringPict(str(self.gate_name)) - b = self._print_label_pretty(printer, *args) - return self._print_subscript_pretty(a, b) - - def _latex(self, printer, *args): - label = self._print_label(printer, *args) - return '%s_{%s}' % (self.gate_name_latex, label) - - def plot_gate(self, axes, gate_idx, gate_grid, wire_grid): - raise NotImplementedError('plot_gate is not implemented.') - -
    -
    [docs]class CGate(Gate): - """A general unitary gate with control qubits. - - A general control gate applies a target gate to a set of targets if all - of the control qubits have a particular values (set by - ``CGate.control_value``). - - Parameters - ---------- - label : tuple - The label in this case has the form (controls, gate), where controls - is a tuple/list of control qubits (as ints) and gate is a ``Gate`` - instance that is the target operator. - - Examples - -------- - - """ - - gate_name = 'C' - gate_name_latex = 'C' - - # The values this class controls for. - control_value = Integer(1) - - #------------------------------------------------------------------------- - # Initialization - #------------------------------------------------------------------------- - - @classmethod - def _eval_args(cls, args): - # _eval_args has the right logic for the controls argument. - controls = args[0] - gate = args[1] - if not is_sequence(controls): - controls = (controls,) - controls = UnitaryOperator._eval_args(controls) - _validate_targets_controls(chain(controls, gate.targets)) - return (Tuple(*controls), gate) - - @classmethod - def _eval_hilbert_space(cls, args): - """This returns the smallest possible Hilbert space.""" - return ComplexSpace(2)**max(max(args[0]) + 1, args[1].min_qubits) - - #------------------------------------------------------------------------- - # Properties - #------------------------------------------------------------------------- - - @property -
    [docs] def nqubits(self): - """The total number of qubits this gate acts on. - - For controlled gate subclasses this includes both target and control - qubits, so that, for examples the CNOT gate acts on 2 qubits. - """ - return len(self.targets) + len(self.controls) -
    - @property -
    [docs] def min_qubits(self): - """The minimum number of qubits this gate needs to act on.""" - return max(max(self.controls), max(self.targets)) + 1 -
    - @property -
    [docs] def targets(self): - """A tuple of target qubits.""" - return self.gate.targets -
    - @property -
    [docs] def controls(self): - """A tuple of control qubits.""" - return tuple(self.label[0]) -
    - @property -
    [docs] def gate(self): - """The non-controlled gate that will be applied to the targets.""" - return self.label[1] - - #------------------------------------------------------------------------- - # Gate methods - #------------------------------------------------------------------------- -
    - def get_target_matrix(self, format='sympy'): - return self.gate.get_target_matrix(format) - -
    [docs] def eval_controls(self, qubit): - """Return True/False to indicate if the controls are satisfied.""" - return all(qubit[bit] == self.control_value for bit in self.controls) -
    -
    [docs] def decompose(self, **options): - """Decompose the controlled gate into CNOT and single qubits gates.""" - if len(self.controls) == 1: - c = self.controls[0] - t = self.gate.targets[0] - if isinstance(self.gate, YGate): - g1 = PhaseGate(t) - g2 = CNotGate(c, t) - g3 = PhaseGate(t) - g4 = ZGate(t) - return g1*g2*g3*g4 - if isinstance(self.gate, ZGate): - g1 = HadamardGate(t) - g2 = CNotGate(c, t) - g3 = HadamardGate(t) - return g1*g2*g3 - else: - return self - - #------------------------------------------------------------------------- - # Print methods - #------------------------------------------------------------------------- -
    - def _print_label(self, printer, *args): - controls = self._print_sequence(self.controls, ',', printer, *args) - gate = printer._print(self.gate, *args) - return '(%s),%s' % (controls, gate) - - def _pretty(self, printer, *args): - controls = self._print_sequence_pretty( - self.controls, ',', printer, *args) - gate = printer._print(self.gate) - gate_name = stringPict(str(self.gate_name)) - first = self._print_subscript_pretty(gate_name, controls) - gate = self._print_parens_pretty(gate) - final = prettyForm(*first.right((gate))) - return final - - def _latex(self, printer, *args): - controls = self._print_sequence(self.controls, ',', printer, *args) - gate = printer._print(self.gate, *args) - return r'%s_{%s}{\left(%s\right)}' % \ - (self.gate_name_latex, controls, gate) - - def plot_gate(self, circ_plot, gate_idx): - min_wire = int(min(chain(self.controls, self.targets))) - max_wire = int(max(chain(self.controls, self.targets))) - circ_plot.control_line(gate_idx, min_wire, max_wire) - for c in self.controls: - circ_plot.control_point(gate_idx, int(c)) - self.gate.plot_gate(circ_plot, gate_idx) - - #------------------------------------------------------------------------- - # Miscellaneous - #------------------------------------------------------------------------- - - def _eval_dagger(self): - if isinstance(self.gate, HermitianOperator): - return self - else: - return Gate._eval_dagger(self) - - def _eval_inverse(self): - if isinstance(self.gate, HermitianOperator): - return self - else: - return Gate._eval_inverse(self) - - def _eval_power(self, exp): - if isinstance(self.gate, HermitianOperator): - if exp == -1: - return Gate._eval_power(self, exp) - elif abs(exp) % 2 == 0: - return self*(Gate._eval_inverse(self)) - else: - return self - else: - return Gate._eval_power(self, exp) - -
    -
    [docs]class UGate(Gate): - """General gate specified by a set of targets and a target matrix. - - Parameters - ---------- - label : tuple - A tuple of the form (targets, U), where targets is a tuple of the - target qubits and U is a unitary matrix with dimension of - len(targets). - """ - gate_name = 'U' - gate_name_latex = 'U' - - #------------------------------------------------------------------------- - # Initialization - #------------------------------------------------------------------------- - - @classmethod - def _eval_args(cls, args): - targets = args[0] - if not is_sequence(targets): - targets = (targets,) - targets = Gate._eval_args(targets) - _validate_targets_controls(targets) - mat = args[1] - if not isinstance(mat, MatrixBase): - raise TypeError('Matrix expected, got: %r' % mat) - dim = 2**len(targets) - if not all(dim == shape for shape in mat.shape): - raise IndexError( - 'Number of targets must match the matrix size: %r %r' % - (targets, mat) - ) - return (targets, mat) - - @classmethod - def _eval_hilbert_space(cls, args): - """This returns the smallest possible Hilbert space.""" - return ComplexSpace(2)**(max(args[0]) + 1) - - #------------------------------------------------------------------------- - # Properties - #------------------------------------------------------------------------- - - @property -
    [docs] def targets(self): - """A tuple of target qubits.""" - return tuple(self.label[0]) - - #------------------------------------------------------------------------- - # Gate methods - #------------------------------------------------------------------------- -
    -
    [docs] def get_target_matrix(self, format='sympy'): - """The matrix rep. of the target part of the gate. - - Parameters - ---------- - format : str - The format string ('sympy','numpy', etc.) - """ - return self.label[1] - - #------------------------------------------------------------------------- - # Print methods - #-------------------------------------------------------------------------
    - def _pretty(self, printer, *args): - targets = self._print_sequence_pretty( - self.targets, ',', printer, *args) - gate_name = stringPict(str(self.gate_name)) - return self._print_subscript_pretty(gate_name, targets) - - def _latex(self, printer, *args): - targets = self._print_sequence(self.targets, ',', printer, *args) - return r'%s_{%s}' % (self.gate_name_latex, targets) - - def plot_gate(self, circ_plot, gate_idx): - circ_plot.one_qubit_box( - self.gate_name_plot, - gate_idx, int(self.targets[0]) - ) - -
    -
    [docs]class OneQubitGate(Gate): - """A single qubit unitary gate base class.""" - - nqubits = Integer(1) - - def plot_gate(self, circ_plot, gate_idx): - circ_plot.one_qubit_box( - self.gate_name_plot, - gate_idx, int(self.targets[0]) - ) - - def _eval_commutator(self, other, **hints): - if isinstance(other, OneQubitGate): - if self.targets != other.targets or self.__class__ == other.__class__: - return Integer(0) - return Operator._eval_commutator(self, other, **hints) - - def _eval_anticommutator(self, other, **hints): - if isinstance(other, OneQubitGate): - if self.targets != other.targets or self.__class__ == other.__class__: - return Integer(2)*self*other - return Operator._eval_anticommutator(self, other, **hints) - -
    -
    [docs]class TwoQubitGate(Gate): - """A two qubit unitary gate base class.""" - - nqubits = Integer(2) - - -#----------------------------------------------------------------------------- -# Single Qubit Gates -#----------------------------------------------------------------------------- - -
    -
    [docs]class IdentityGate(OneQubitGate): - """The single qubit identity gate. - - Parameters - ---------- - target : int - The target qubit this gate will apply to. - - Examples - -------- - - """ - gate_name = '1' - gate_name_latex = '1' - - def get_target_matrix(self, format='sympy'): - return matrix_cache.get_matrix('eye2', format) - - def _eval_commutator(self, other, **hints): - return Integer(0) - - def _eval_anticommutator(self, other, **hints): - return Integer(2)*other - -
    -
    [docs]class HadamardGate(HermitianOperator, OneQubitGate): - """The single qubit Hadamard gate. - - Parameters - ---------- - target : int - The target qubit this gate will apply to. - - Examples - -------- - - >>> from sympy import sqrt - >>> from sympy.physics.quantum.qubit import Qubit - >>> from sympy.physics.quantum.gate import HadamardGate - >>> from sympy.physics.quantum.qapply import qapply - >>> qapply(HadamardGate(0)*Qubit('1')) - sqrt(2)*|0>/2 - sqrt(2)*|1>/2 - >>> # Hadamard on bell state, applied on 2 qubits. - >>> psi = 1/sqrt(2)*(Qubit('00')+Qubit('11')) - >>> qapply(HadamardGate(0)*HadamardGate(1)*psi) - sqrt(2)*|00>/2 + sqrt(2)*|11>/2 - - """ - gate_name = 'H' - gate_name_latex = 'H' - - def get_target_matrix(self, format='sympy'): - if _normalized: - return matrix_cache.get_matrix('H', format) - else: - return matrix_cache.get_matrix('Hsqrt2', format) - - def _eval_commutator_XGate(self, other, **hints): - return I*sqrt(2)*YGate(self.targets[0]) - - def _eval_commutator_YGate(self, other, **hints): - return I*sqrt(2)*(ZGate(self.targets[0]) - XGate(self.targets[0])) - - def _eval_commutator_ZGate(self, other, **hints): - return -I*sqrt(2)*YGate(self.targets[0]) - - def _eval_anticommutator_XGate(self, other, **hints): - return sqrt(2)*IdentityGate(self.targets[0]) - - def _eval_anticommutator_YGate(self, other, **hints): - return Integer(0) - - def _eval_anticommutator_ZGate(self, other, **hints): - return sqrt(2)*IdentityGate(self.targets[0]) - -
    -
    [docs]class XGate(HermitianOperator, OneQubitGate): - """The single qubit X, or NOT, gate. - - Parameters - ---------- - target : int - The target qubit this gate will apply to. - - Examples - -------- - - """ - gate_name = 'X' - gate_name_latex = 'X' - - def get_target_matrix(self, format='sympy'): - return matrix_cache.get_matrix('X', format) - - def plot_gate(self, circ_plot, gate_idx): - circ_plot.not_point( - gate_idx, int(self.label[0]) - ) - - def _eval_commutator_YGate(self, other, **hints): - return Integer(2)*I*ZGate(self.targets[0]) - - def _eval_anticommutator_XGate(self, other, **hints): - return Integer(2)*IdentityGate(self.targets[0]) - - def _eval_anticommutator_YGate(self, other, **hints): - return Integer(0) - - def _eval_anticommutator_ZGate(self, other, **hints): - return Integer(0) - -
    -
    [docs]class YGate(HermitianOperator, OneQubitGate): - """The single qubit Y gate. - - Parameters - ---------- - target : int - The target qubit this gate will apply to. - - Examples - -------- - - """ - gate_name = 'Y' - gate_name_latex = 'Y' - - def get_target_matrix(self, format='sympy'): - return matrix_cache.get_matrix('Y', format) - - def _eval_commutator_ZGate(self, other, **hints): - return Integer(2)*I*XGate(self.targets[0]) - - def _eval_anticommutator_YGate(self, other, **hints): - return Integer(2)*IdentityGate(self.targets[0]) - - def _eval_anticommutator_ZGate(self, other, **hints): - return Integer(0) - -
    -
    [docs]class ZGate(HermitianOperator, OneQubitGate): - """The single qubit Z gate. - - Parameters - ---------- - target : int - The target qubit this gate will apply to. - - Examples - -------- - - """ - gate_name = 'Z' - gate_name_latex = 'Z' - - def get_target_matrix(self, format='sympy'): - return matrix_cache.get_matrix('Z', format) - - def _eval_commutator_XGate(self, other, **hints): - return Integer(2)*I*YGate(self.targets[0]) - - def _eval_anticommutator_YGate(self, other, **hints): - return Integer(0) - -
    -
    [docs]class PhaseGate(OneQubitGate): - """The single qubit phase, or S, gate. - - This gate rotates the phase of the state by pi/2 if the state is ``|1>`` and - does nothing if the state is ``|0>``. - - Parameters - ---------- - target : int - The target qubit this gate will apply to. - - Examples - -------- - - """ - gate_name = 'S' - gate_name_latex = 'S' - - def get_target_matrix(self, format='sympy'): - return matrix_cache.get_matrix('S', format) - - def _eval_commutator_ZGate(self, other, **hints): - return Integer(0) - - def _eval_commutator_TGate(self, other, **hints): - return Integer(0) - -
    -
    [docs]class TGate(OneQubitGate): - """The single qubit pi/8 gate. - - This gate rotates the phase of the state by pi/4 if the state is ``|1>`` and - does nothing if the state is ``|0>``. - - Parameters - ---------- - target : int - The target qubit this gate will apply to. - - Examples - -------- - - """ - gate_name = 'T' - gate_name_latex = 'T' - - def get_target_matrix(self, format='sympy'): - return matrix_cache.get_matrix('T', format) - - def _eval_commutator_ZGate(self, other, **hints): - return Integer(0) - - def _eval_commutator_PhaseGate(self, other, **hints): - return Integer(0) - - -# Aliases for gate names.
    -H = HadamardGate -X = XGate -Y = YGate -Z = ZGate -T = TGate -Phase = S = PhaseGate - - -#----------------------------------------------------------------------------- -# 2 Qubit Gates -#----------------------------------------------------------------------------- - - -
    [docs]class CNotGate(HermitianOperator, CGate, TwoQubitGate): - """Two qubit controlled-NOT. - - This gate performs the NOT or X gate on the target qubit if the control - qubits all have the value 1. - - Parameters - ---------- - label : tuple - A tuple of the form (control, target). - - Examples - -------- - - >>> from sympy.physics.quantum.gate import CNOT - >>> from sympy.physics.quantum.qapply import qapply - >>> from sympy.physics.quantum.qubit import Qubit - >>> c = CNOT(1,0) - >>> qapply(c*Qubit('10')) # note that qubits are indexed from right to left - |11> - - """ - gate_name = 'CNOT' - gate_name_latex = 'CNOT' - - #------------------------------------------------------------------------- - # Initialization - #------------------------------------------------------------------------- - - @classmethod - def _eval_args(cls, args): - args = Gate._eval_args(args) - return args - - @classmethod - def _eval_hilbert_space(cls, args): - """This returns the smallest possible Hilbert space.""" - return ComplexSpace(2)**(max(args) + 1) - - #------------------------------------------------------------------------- - # Properties - #------------------------------------------------------------------------- - - @property -
    [docs] def min_qubits(self): - """The minimum number of qubits this gate needs to act on.""" - return max(self.label) + 1 -
    - @property -
    [docs] def targets(self): - """A tuple of target qubits.""" - return (self.label[1],) -
    - @property -
    [docs] def controls(self): - """A tuple of control qubits.""" - return (self.label[0],) -
    - @property -
    [docs] def gate(self): - """The non-controlled gate that will be applied to the targets.""" - return XGate(self.label[1]) - - #------------------------------------------------------------------------- - # Properties - #------------------------------------------------------------------------- - - # The default printing of Gate works better than those of CGate, so we - # go around the overridden methods in CGate. -
    - def _print_label(self, printer, *args): - return Gate._print_label(self, printer, *args) - - def _pretty(self, printer, *args): - return Gate._pretty(self, printer, *args) - - def _latex(self, printer, *args): - return Gate._latex(self, printer, *args) - - #------------------------------------------------------------------------- - # Commutator/AntiCommutator - #------------------------------------------------------------------------- - - def _eval_commutator_ZGate(self, other, **hints): - """[CNOT(i, j), Z(i)] == 0.""" - if self.controls[0] == other.targets[0]: - return Integer(0) - else: - raise NotImplementedError('Commutator not implemented: %r' % other) - - def _eval_commutator_TGate(self, other, **hints): - """[CNOT(i, j), T(i)] == 0.""" - return self._eval_commutator_ZGate(other, **hints) - - def _eval_commutator_PhaseGate(self, other, **hints): - """[CNOT(i, j), S(i)] == 0.""" - return self._eval_commutator_ZGate(other, **hints) - - def _eval_commutator_XGate(self, other, **hints): - """[CNOT(i, j), X(j)] == 0.""" - if self.targets[0] == other.targets[0]: - return Integer(0) - else: - raise NotImplementedError('Commutator not implemented: %r' % other) - - def _eval_commutator_CNotGate(self, other, **hints): - """[CNOT(i, j), CNOT(i,k)] == 0.""" - if self.controls[0] == other.controls[0]: - return Integer(0) - else: - raise NotImplementedError('Commutator not implemented: %r' % other) - -
    -
    [docs]class SwapGate(TwoQubitGate): - """Two qubit SWAP gate. - - This gate swap the values of the two qubits. - - Parameters - ---------- - label : tuple - A tuple of the form (target1, target2). - - Examples - -------- - - """ - gate_name = 'SWAP' - gate_name_latex = 'SWAP' - - def get_target_matrix(self, format='sympy'): - return matrix_cache.get_matrix('SWAP', format) - -
    [docs] def decompose(self, **options): - """Decompose the SWAP gate into CNOT gates.""" - i, j = self.targets[0], self.targets[1] - g1 = CNotGate(i, j) - g2 = CNotGate(j, i) - return g1*g2*g1 -
    - def plot_gate(self, circ_plot, gate_idx): - min_wire = int(min(self.targets)) - max_wire = int(max(self.targets)) - circ_plot.control_line(gate_idx, min_wire, max_wire) - circ_plot.swap_point(gate_idx, min_wire) - circ_plot.swap_point(gate_idx, max_wire) - - def _represent_ZGate(self, basis, **options): - """Represent the SWAP gate in the computational basis. - - The following representation is used to compute this: - - SWAP = |1><1|x|1><1| + |0><0|x|0><0| + |1><0|x|0><1| + |0><1|x|1><0| - """ - format = options.get('format', 'sympy') - targets = [int(t) for t in self.targets] - min_target = min(targets) - max_target = max(targets) - nqubits = options.get('nqubits', self.min_qubits) - - op01 = matrix_cache.get_matrix('op01', format) - op10 = matrix_cache.get_matrix('op10', format) - op11 = matrix_cache.get_matrix('op11', format) - op00 = matrix_cache.get_matrix('op00', format) - eye2 = matrix_cache.get_matrix('eye2', format) - - result = None - for i, j in ((op01, op10), (op10, op01), (op00, op00), (op11, op11)): - product = nqubits*[eye2] - product[nqubits - min_target - 1] = i - product[nqubits - max_target - 1] = j - new_result = matrix_tensor_product(*product) - if result is None: - result = new_result - else: - result = result + new_result - - return result - - -# Aliases for gate names.
    -CNOT = CNotGate -SWAP = SwapGate - -#----------------------------------------------------------------------------- -# Represent -#----------------------------------------------------------------------------- - - -def represent_zbasis(controls, targets, target_matrix, nqubits, format='sympy'): - """Represent a gate with controls, targets and target_matrix. - - This function does the low-level work of representing gates as matrices - in the standard computational basis (ZGate). Currently, we support two - main cases: - - 1. One target qubit and no control qubits. - 2. One target qubits and multiple control qubits. - - For the base of multiple controls, we use the following expression [1]: - - 1_{2**n} + (|1><1|)^{(n-1)} x (target-matrix - 1_{2}) - - Parameters - ---------- - controls : list, tuple - A sequence of control qubits. - targets : list, tuple - A sequence of target qubits. - target_matrix : sympy.Matrix, numpy.matrix, scipy.sparse - The matrix form of the transformation to be performed on the target - qubits. The format of this matrix must match that passed into - the `format` argument. - nqubits : int - The total number of qubits used for the representation. - format : str - The format of the final matrix ('sympy', 'numpy', 'scipy.sparse'). - - Examples - -------- - - References - ---------- - [1] http://www.johnlapeyre.com/qinf/qinf_html/node6.html. - """ - controls = [int(x) for x in controls] - targets = [int(x) for x in targets] - nqubits = int(nqubits) - - # This checks for the format as well. - op11 = matrix_cache.get_matrix('op11', format) - eye2 = matrix_cache.get_matrix('eye2', format) - - # Plain single qubit case - if len(controls) == 0 and len(targets) == 1: - product = [] - bit = targets[0] - # Fill product with [I1,Gate,I2] such that the unitaries, - # I, cause the gate to be applied to the correct Qubit - if bit != nqubits - 1: - product.append(matrix_eye(2**(nqubits - bit - 1), format=format)) - product.append(target_matrix) - if bit != 0: - product.append(matrix_eye(2**bit, format=format)) - return matrix_tensor_product(*product) - - # Single target, multiple controls. - elif len(targets) == 1 and len(controls) >= 1: - target = targets[0] - - # Build the non-trivial part. - product2 = [] - for i in range(nqubits): - product2.append(matrix_eye(2, format=format)) - for control in controls: - product2[nqubits - 1 - control] = op11 - product2[nqubits - 1 - target] = target_matrix - eye2 - - return matrix_eye(2**nqubits, format=format) + \ - matrix_tensor_product(*product2) - - # Multi-target, multi-control is not yet implemented. - else: - raise NotImplementedError( - 'The representation of multi-target, multi-control gates ' - 'is not implemented.' - ) - - -#----------------------------------------------------------------------------- -# Gate manipulation functions. -#----------------------------------------------------------------------------- - - -
    [docs]def gate_simp(circuit): - """Simplifies gates symbolically - - It first sorts gates using gate_sort. It then applies basic - simplification rules to the circuit, e.g., XGate**2 = Identity - """ - - # Bubble sort out gates that commute. - circuit = gate_sort(circuit) - - # Do simplifications by subing a simplification into the first element - # which can be simplified. We recursively call gate_simp with new circuit - # as input more simplifications exist. - if isinstance(circuit, Add): - return sum(gate_simp(t) for t in circuit.args) - elif isinstance(circuit, Mul): - circuit_args = circuit.args - elif isinstance(circuit, Pow): - b, e = circuit.as_base_exp() - circuit_args = (gate_simp(b)**e,) - else: - return circuit - - # Iterate through each element in circuit, simplify if possible. - for i in range(len(circuit_args)): - # H,X,Y or Z squared is 1. - # T**2 = S, S**2 = Z - if isinstance(circuit_args[i], Pow): - if isinstance(circuit_args[i].base, - (HadamardGate, XGate, YGate, ZGate)) \ - and isinstance(circuit_args[i].exp, Number): - # Build a new circuit taking replacing the - # H,X,Y,Z squared with one. - newargs = (circuit_args[:i] + - (circuit_args[i].base**(circuit_args[i].exp % 2),) + - circuit_args[i + 1:]) - # Recursively simplify the new circuit. - circuit = gate_simp(Mul(*newargs)) - break - elif isinstance(circuit_args[i].base, PhaseGate): - # Build a new circuit taking old circuit but splicing - # in simplification. - newargs = circuit_args[:i] - # Replace PhaseGate**2 with ZGate. - newargs = newargs + (ZGate(circuit_args[i].base.args[0])** - (Integer(circuit_args[i].exp/2)), circuit_args[i].base** - (circuit_args[i].exp % 2)) - # Append the last elements. - newargs = newargs + circuit_args[i + 1:] - # Recursively simplify the new circuit. - circuit = gate_simp(Mul(*newargs)) - break - elif isinstance(circuit_args[i].base, TGate): - # Build a new circuit taking all the old elements. - newargs = circuit_args[:i] - - # Put an Phasegate in place of any TGate**2. - newargs = newargs + (PhaseGate(circuit_args[i].base.args[0])** - Integer(circuit_args[i].exp/2), circuit_args[i].base** - (circuit_args[i].exp % 2)) - - # Append the last elements. - newargs = newargs + circuit_args[i + 1:] - # Recursively simplify the new circuit. - circuit = gate_simp(Mul(*newargs)) - break - return circuit - -
    -
    [docs]def gate_sort(circuit): - """Sorts the gates while keeping track of commutation relations - - This function uses a bubble sort to rearrange the order of gate - application. Keeps track of Quantum computations special commutation - relations (e.g. things that apply to the same Qubit do not commute with - each other) - - circuit is the Mul of gates that are to be sorted. - """ - # Make sure we have an Add or Mul. - if isinstance(circuit, Add): - return sum(gate_sort(t) for t in circuit.args) - if isinstance(circuit, Pow): - return gate_sort(circuit.base)**circuit.exp - elif isinstance(circuit, Gate): - return circuit - if not isinstance(circuit, Mul): - return circuit - - changes = True - while changes: - changes = False - circ_array = circuit.args - for i in range(len(circ_array) - 1): - # Go through each element and switch ones that are in wrong order - if isinstance(circ_array[i], (Gate, Pow)) and \ - isinstance(circ_array[i + 1], (Gate, Pow)): - # If we have a Pow object, look at only the base - first_base, first_exp = circ_array[i].as_base_exp() - second_base, second_exp = circ_array[i + 1].as_base_exp() - - # Use sympy's hash based sorting. This is not mathematical - # sorting, but is rather based on comparing hashes of objects. - # See Basic.compare for details. - if first_base.compare(second_base) > 0: - if Commutator(first_base, second_base).doit() == 0: - new_args = (circuit.args[:i] + (circuit.args[i + 1],) + - (circuit.args[i],) + circuit.args[i + 2:]) - circuit = Mul(*new_args) - circ_array = circuit.args - changes = True - break - if AntiCommutator(first_base, second_base).doit() == 0: - new_args = (circuit.args[:i] + (circuit.args[i + 1],) + - (circuit.args[i],) + circuit.args[i + 2:]) - sign = Integer(-1)**(first_exp*second_exp) - circuit = sign*Mul(*new_args) - circ_array = circuit.args - changes = True - break - return circuit - - -#----------------------------------------------------------------------------- -# Utility functions -#----------------------------------------------------------------------------- - -
    -
    [docs]def random_circuit(ngates, nqubits, gate_space=(X, Y, Z, S, T, H, CNOT, SWAP)): - """Return a random circuit of ngates and nqubits. - - This uses an equally weighted sample of (X, Y, Z, S, T, H, CNOT, SWAP) - gates. - - Parameters - ---------- - ngates : int - The number of gates in the circuit. - nqubits : int - The number of qubits in the circuit. - gate_space : tuple - A tuple of the gate classes that will be used in the circuit. - Repeating gate classes multiple times in this tuple will increase - the frequency they appear in the random circuit. - """ - qubit_space = list(range(nqubits)) - result = [] - for i in range(ngates): - g = random.choice(gate_space) - if g == CNotGate or g == SwapGate: - qubits = random.sample(qubit_space, 2) - g = g(*qubits) - else: - qubit = random.choice(qubit_space) - g = g(qubit) - result.append(g) - return Mul(*result) - -
    -def zx_basis_transform(self, format='sympy'): - """Transformation matrix from Z to X basis.""" - return matrix_cache.get_matrix('ZX', format) - - -def zy_basis_transform(self, format='sympy'): - """Transformation matrix from Z to Y basis.""" - return matrix_cache.get_matrix('ZY', format) -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/quantum/grover.html b/dev-py3k/_modules/sympy/physics/quantum/grover.html deleted file mode 100644 index 667c0499fb5..00000000000 --- a/dev-py3k/_modules/sympy/physics/quantum/grover.html +++ /dev/null @@ -1,434 +0,0 @@ - - - - - - - - - - sympy.physics.quantum.grover — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.quantum.grover

    -"""Grover's algorithm and helper functions.
    -
    -Todo:
    -
    -* W gate construction (or perhaps -W gate based on Mermin's book)
    -* Generalize the algorithm for an unknown function that returns 1 on multiple
    -  qubit states, not just one.
    -* Implement _represent_ZGate in OracleGate
    -"""
    -
    -from sympy import floor, pi, sqrt, sympify
    -from sympy.core.compatibility import callable
    -from sympy.physics.quantum.qapply import qapply
    -from sympy.physics.quantum.qexpr import QuantumError
    -from sympy.physics.quantum.hilbert import ComplexSpace
    -from sympy.physics.quantum.operator import UnitaryOperator
    -from sympy.physics.quantum.gate import Gate
    -from sympy.physics.quantum.qubit import IntQubit
    -import collections
    -
    -__all__ = [
    -    'OracleGate',
    -    'WGate',
    -    'superposition_basis',
    -    'grover_iteration',
    -    'apply_grover'
    -]
    -
    -
    -
    [docs]def superposition_basis(nqubits): - """Creates an equal superposition of the computational basis. - - Parameters - ========== - - nqubits : int - The number of qubits. - - Returns - ======= - - state : Qubit - An equal superposition of the computational basis with nqubits. - - Examples - ======== - - Create an equal superposition of 2 qubits:: - - >>> from sympy.physics.quantum.grover import superposition_basis - >>> superposition_basis(2) - |0>/2 + |1>/2 + |2>/2 + |3>/2 - """ - - amp = 1/sqrt(2**nqubits) - return sum([amp*IntQubit(n, nqubits) for n in range(2**nqubits)]) - -
    -
    [docs]class OracleGate(Gate): - """A black box gate. - - The gate marks the desired qubits of an unknown function by flipping - the sign of the qubits. The unknown function returns true when it - finds its desired qubits and false otherwise. - - Parameters - ========== - - qubits : int - Number of qubits. - - oracle : callable - A callable function that returns a boolean on a computational basis. - - Examples - ======== - - Apply an Oracle gate that flips the sign of ``|2>`` on different qubits:: - - >>> from sympy.physics.quantum.qubit import IntQubit - >>> from sympy.physics.quantum.qapply import qapply - >>> from sympy.physics.quantum.grover import OracleGate - >>> f = lambda qubits: qubits == IntQubit(2) - >>> v = OracleGate(2, f) - >>> qapply(v*IntQubit(2)) - -|2> - >>> qapply(v*IntQubit(3)) - |3> - """ - - gate_name = 'V' - gate_name_latex = 'V' - - #------------------------------------------------------------------------- - # Initialization/creation - #------------------------------------------------------------------------- - - @classmethod - def _eval_args(cls, args): - # TODO: args[1] is not a subclass of Basic - if len(args) != 2: - raise QuantumError( - 'Insufficient/excessive arguments to Oracle. Please ' + - 'supply the number of qubits and an unknown function.' - ) - sub_args = (args[0],) - sub_args = UnitaryOperator._eval_args(sub_args) - if not sub_args[0].is_Integer: - raise TypeError('Integer expected, got: %r' % sub_args[0]) - - if not isinstance(args[1], collections.Callable): - raise TypeError('Callable expected, got: %r' % args[1]) - return (sub_args[0], args[1]) - - @classmethod - def _eval_hilbert_space(cls, args): - """This returns the smallest possible Hilbert space.""" - return ComplexSpace(2)**args[0] - - #------------------------------------------------------------------------- - # Properties - #------------------------------------------------------------------------- - - @property -
    [docs] def search_function(self): - """The unknown function that helps find the sought after qubits.""" - return self.label[1] -
    - @property -
    [docs] def targets(self): - """A tuple of target qubits.""" - return sympify(tuple(range(self.args[0]))) - - #------------------------------------------------------------------------- - # Apply - #------------------------------------------------------------------------- -
    - def _apply_operator_Qubit(self, qubits, **options): - """Apply this operator to a Qubit subclass. - - Parameters - ========== - - qubits : Qubit - The qubit subclass to apply this operator to. - - Returns - ======= - - state : Expr - The resulting quantum state. - """ - if qubits.nqubits != self.nqubits: - raise QuantumError( - 'OracleGate operates on %r qubits, got: %r' - % (self.nqubits, qubits.nqubits) - ) - # If function returns 1 on qubits - # return the negative of the qubits (flip the sign) - if self.search_function(qubits): - return -qubits - else: - return qubits - - #------------------------------------------------------------------------- - # Represent - #------------------------------------------------------------------------- - - def _represent_ZGate(self, basis, **options): - raise NotImplementedError( - "Represent for the Oracle has not been implemented yet" - ) - -
    -
    [docs]class WGate(Gate): - """General n qubit W Gate in Grover's algorithm. - - The gate performs the operation ``2|phi><phi| - 1`` on some qubits. - ``|phi> = (tensor product of n Hadamards)*(|0> with n qubits)`` - - Parameters - ========== - - nqubits : int - The number of qubits to operate on - - """ - - gate_name = 'W' - gate_name_latex = 'W' - - @classmethod - def _eval_args(cls, args): - if len(args) != 1: - raise QuantumError( - 'Insufficient/excessive arguments to W gate. Please ' + - 'supply the number of qubits to operate on.' - ) - args = UnitaryOperator._eval_args(args) - if not args[0].is_Integer: - raise TypeError('Integer expected, got: %r' % args[0]) - return args - - #------------------------------------------------------------------------- - # Properties - #------------------------------------------------------------------------- - - @property - def targets(self): - return sympify(tuple(reversed(list(range(self.args[0]))))) - - #------------------------------------------------------------------------- - # Apply - #------------------------------------------------------------------------- - - def _apply_operator_Qubit(self, qubits, **options): - """ - qubits: a set of qubits (Qubit) - Returns: quantum object (quantum expression - QExpr) - """ - if qubits.nqubits != self.nqubits: - raise QuantumError( - 'WGate operates on %r qubits, got: %r' - % (self.nqubits, qubits.nqubits) - ) - - # See 'Quantum Computer Science' by David Mermin p.92 -> W|a> result - # Return (2/(sqrt(2^n)))|phi> - |a> where |a> is the current basis - # state and phi is the superposition of basis states (see function - # create_computational_basis above) - basis_states = superposition_basis(self.nqubits) - change_to_basis = (2/sqrt(2**self.nqubits))*basis_states - return change_to_basis - qubits - -
    -
    [docs]def grover_iteration(qstate, oracle): - """Applies one application of the Oracle and W Gate, WV. - - Parameters - ========== - - qstate : Qubit - A superposition of qubits. - oracle : OracleGate - The black box operator that flips the sign of the desired basis qubits. - - Returns - ======= - - Qubit : The qubits after applying the Oracle and W gate. - - Examples - ======== - - Perform one iteration of grover's algorithm to see a phase change:: - - >>> from sympy.physics.quantum.qapply import qapply - >>> from sympy.physics.quantum.qubit import IntQubit - >>> from sympy.physics.quantum.grover import OracleGate - >>> from sympy.physics.quantum.grover import superposition_basis - >>> from sympy.physics.quantum.grover import grover_iteration - >>> numqubits = 2 - >>> basis_states = superposition_basis(numqubits) - >>> f = lambda qubits: qubits == IntQubit(2) - >>> v = OracleGate(numqubits, f) - >>> qapply(grover_iteration(basis_states, v)) - |2> - - """ - wgate = WGate(oracle.nqubits) - return wgate*oracle*qstate - -
    -
    [docs]def apply_grover(oracle, nqubits, iterations=None): - """Applies grover's algorithm. - - Parameters - ========== - - oracle : callable - The unknown callable function that returns true when applied to the - desired qubits and false otherwise. - - Returns - ======= - - state : Expr - The resulting state after Grover's algorithm has been iterated. - - Examples - ======== - - Apply grover's algorithm to an even superposition of 2 qubits:: - - >>> from sympy.physics.quantum.qapply import qapply - >>> from sympy.physics.quantum.qubit import IntQubit - >>> from sympy.physics.quantum.grover import apply_grover - >>> f = lambda qubits: qubits == IntQubit(2) - >>> qapply(apply_grover(f, 2)) - |2> - - """ - if nqubits <= 0: - raise QuantumError( - 'Grover\'s algorithm needs nqubits > 0, received %r qubits' - % nqubits - ) - if iterations is None: - iterations = floor(sqrt(2**nqubits)*(pi/4)) - - v = OracleGate(nqubits, oracle) - iterated = superposition_basis(nqubits) - for iter in range(iterations): - iterated = grover_iteration(iterated, v) - iterated = qapply(iterated) - - return iterated
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/quantum/hilbert.html b/dev-py3k/_modules/sympy/physics/quantum/hilbert.html deleted file mode 100644 index 90c02819db3..00000000000 --- a/dev-py3k/_modules/sympy/physics/quantum/hilbert.html +++ /dev/null @@ -1,769 +0,0 @@ - - - - - - - - - - sympy.physics.quantum.hilbert — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.quantum.hilbert

    -"""Hilbert spaces for quantum mechanics.
    -
    -Authors:
    -* Brian Granger
    -* Matt Curry
    -"""
    -
    -from sympy import Basic, Interval, oo, sympify
    -from sympy.printing.pretty.stringpict import prettyForm
    -
    -from sympy.physics.quantum.qexpr import QuantumError
    -
    -from sympy.core.compatibility import reduce
    -from functools import reduce
    -
    -__all__ = [
    -    'HilbertSpaceError',
    -    'HilbertSpace',
    -    'ComplexSpace',
    -    'L2',
    -    'FockSpace'
    -]
    -
    -#-----------------------------------------------------------------------------
    -# Main objects
    -#-----------------------------------------------------------------------------
    -
    -
    -class HilbertSpaceError(QuantumError):
    -    pass
    -
    -#-----------------------------------------------------------------------------
    -# Main objects
    -#-----------------------------------------------------------------------------
    -
    -
    -
    [docs]class HilbertSpace(Basic): - """An abstract Hilbert space for quantum mechanics. - - In short, a Hilbert space is an abstract vector space that is complete - with inner products defined [1]_. - - Examples - ======== - - >>> from sympy.physics.quantum.hilbert import HilbertSpace - >>> hs = HilbertSpace() - >>> hs - H - - References - ========== - - .. [1] http://en.wikipedia.org/wiki/Hilbert_space - """ - - def __new__(cls): - obj = Basic.__new__(cls) - return obj - - @property -
    [docs] def dimension(self): - """Return the Hilbert dimension of the space.""" - raise NotImplementedError('This Hilbert space has no dimension.') -
    - def __add__(self, other): - return DirectSumHilbertSpace(self, other) - - def __radd__(self, other): - return DirectSumHilbertSpace(other, self) - - def __mul__(self, other): - return TensorProductHilbertSpace(self, other) - - def __rmul__(self, other): - return TensorProductHilbertSpace(other, self) - - def __pow__(self, other, mod=None): - if mod is not None: - raise ValueError('The third argument to __pow__ is not supported \ - for Hilbert spaces.') - return TensorPowerHilbertSpace(self, other) - - def __contains__(self, other): - """Is the operator or state in this Hilbert space. - - This is checked by comparing the classes of the Hilbert spaces, not - the instances. This is to allow Hilbert Spaces with symbolic - dimensions. - """ - if other.hilbert_space.__class__ == self.__class__: - return True - else: - return False - - def _sympystr(self, printer, *args): - return 'H' - - def _pretty(self, printer, *args): - # u = u'\u2108' # script - u = '\u0048' - return prettyForm(u) - - def _latex(self, printer, *args): - return r'\mathcal{H}' - -
    -
    [docs]class ComplexSpace(HilbertSpace): - """Finite dimensional Hilbert space of complex vectors. - - The elements of this Hilbert space are n-dimensional complex valued - vectors with the usual inner product that takes the complex conjugate - of the vector on the right. - - A classic example of this type of Hilbert space is spin-1/2, which is - ``ComplexSpace(2)``. Generalizing to spin-s, the space is - ``ComplexSpace(2*s+1)``. Quantum computing with N qubits is done with the - direct product space ``ComplexSpace(2)**N``. - - Examples - ======== - - >>> from sympy import symbols - >>> from sympy.physics.quantum.hilbert import ComplexSpace - >>> c1 = ComplexSpace(2) - >>> c1 - C(2) - >>> c1.dimension - 2 - - >>> n = symbols('n') - >>> c2 = ComplexSpace(n) - >>> c2 - C(n) - >>> c2.dimension - n - - """ - - def __new__(cls, dimension): - dimension = sympify(dimension) - r = cls.eval(dimension) - if isinstance(r, Basic): - return r - obj = Basic.__new__(cls, dimension) - return obj - - @classmethod - def eval(cls, dimension): - if len(dimension.atoms()) == 1: - if not (dimension.is_Integer and dimension > 0 or dimension is oo - or dimension.is_Symbol): - raise TypeError('The dimension of a ComplexSpace can only' - 'be a positive integer, oo, or a Symbol: %r' - % dimension) - else: - for dim in dimension.atoms(): - if not (dim.is_Integer or dim is oo or dim.is_Symbol): - raise TypeError('The dimension of a ComplexSpace can only' - ' contain integers, oo, or a Symbol: %r' - % dim) - - @property - def dimension(self): - return self.args[0] - - def _sympyrepr(self, printer, *args): - return "%s(%s)" % (self.__class__.__name__, - printer._print(self.dimension, *args)) - - def _sympystr(self, printer, *args): - return "C(%s)" % printer._print(self.dimension, *args) - - def _pretty(self, printer, *args): - # u = u'\u2102' # script - u = '\u0043' - pform_exp = printer._print(self.dimension, *args) - pform_base = prettyForm(u) - return pform_base**pform_exp - - def _latex(self, printer, *args): - return r'\mathcal{C}^{%s}' % printer._print(self.dimension, *args) - -
    -
    [docs]class L2(HilbertSpace): - """The Hilbert space of square integrable functions on an interval. - - An L2 object takes in a single sympy Interval argument which represents - the interval its functions (vectors) are defined on. - - Examples - ======== - - >>> from sympy import Interval, oo - >>> from sympy.physics.quantum.hilbert import L2 - >>> hs = L2(Interval(0,oo)) - >>> hs - L2([0, oo)) - >>> hs.dimension - oo - >>> hs.interval - [0, oo) - - """ - - def __new__(cls, interval): - if not isinstance(interval, Interval): - raise TypeError('L2 interval must be an Interval instance: %r' - % interval) - obj = Basic.__new__(cls, interval) - return obj - - @property - def dimension(self): - return oo - - @property - def interval(self): - return self.args[0] - - def _sympyrepr(self, printer, *args): - return "L2(%s)" % printer._print(self.interval, *args) - - def _sympystr(self, printer, *args): - return "L2(%s)" % printer._print(self.interval, *args) - - def _pretty(self, printer, *args): - pform_exp = prettyForm("2") - pform_base = prettyForm("L") - return pform_base**pform_exp - - def _latex(self, printer, *args): - interval = printer._print(self.interval, *args) - return r'{\mathcal{L}^2}\left( %s \right)' % interval - -
    -
    [docs]class FockSpace(HilbertSpace): - """The Hilbert space for second quantization. - - Technically, this Hilbert space is a infinite direct sum of direct - products of single particle Hilbert spaces [1]_. This is a mess, so we have - a class to represent it directly. - - Examples - ======== - - >>> from sympy.physics.quantum.hilbert import FockSpace - >>> hs = FockSpace() - >>> hs - F - >>> hs.dimension - oo - - References - ========== - - .. [1] http://en.wikipedia.org/wiki/Fock_space - """ - - def __new__(cls): - obj = Basic.__new__(cls) - return obj - - @property - def dimension(self): - return oo - - def _sympyrepr(self, printer, *args): - return "FockSpace()" - - def _sympystr(self, printer, *args): - return "F" - - def _pretty(self, printer, *args): - # u = u'\u2131' # script - u = '\u0046' - return prettyForm(u) - - def _latex(self, printer, *args): - return r'\mathcal{F}' - -
    -class TensorProductHilbertSpace(HilbertSpace): - """A tensor product of Hilbert spaces [1]_. - - The tensor product between Hilbert spaces is represented by the - operator ``*`` Products of the same Hilbert space will be combined into - tensor powers. - - A ``TensorProductHilbertSpace`` object takes in an arbitrary number of - ``HilbertSpace`` objects as its arguments. In addition, multiplication of - ``HilbertSpace`` objects will automatically return this tensor product - object. - - Examples - ======== - - >>> from sympy.physics.quantum.hilbert import ComplexSpace, FockSpace - >>> from sympy import symbols - - >>> c = ComplexSpace(2) - >>> f = FockSpace() - >>> hs = c*f - >>> hs - C(2)*F - >>> hs.dimension - oo - >>> hs.spaces - (C(2), F) - - >>> c1 = ComplexSpace(2) - >>> n = symbols('n') - >>> c2 = ComplexSpace(n) - >>> hs = c1*c2 - >>> hs - C(2)*C(n) - >>> hs.dimension - 2*n - - References - ========== - - .. [1] http://en.wikipedia.org/wiki/Hilbert_space#Tensor_products - """ - - def __new__(cls, *args): - r = cls.eval(args) - if isinstance(r, Basic): - return r - obj = Basic.__new__(cls, *args) - return obj - - @classmethod - def eval(cls, args): - """Evaluates the direct product.""" - new_args = [] - recall = False - #flatten arguments - for arg in args: - if isinstance(arg, TensorProductHilbertSpace): - new_args.extend(arg.args) - recall = True - elif isinstance(arg, (HilbertSpace, TensorPowerHilbertSpace)): - new_args.append(arg) - else: - raise TypeError('Hilbert spaces can only be multiplied by \ - other Hilbert spaces: %r' % arg) - #combine like arguments into direct powers - comb_args = [] - prev_arg = None - for new_arg in new_args: - if prev_arg is not None: - if isinstance(new_arg, TensorPowerHilbertSpace) and \ - isinstance(prev_arg, TensorPowerHilbertSpace) and \ - new_arg.base == prev_arg.base: - prev_arg = new_arg.base**(new_arg.exp + prev_arg.exp) - elif isinstance(new_arg, TensorPowerHilbertSpace) and \ - new_arg.base == prev_arg: - prev_arg = prev_arg**(new_arg.exp + 1) - elif isinstance(prev_arg, TensorPowerHilbertSpace) and \ - new_arg == prev_arg.base: - prev_arg = new_arg**(prev_arg.exp + 1) - elif new_arg == prev_arg: - prev_arg = new_arg**2 - else: - comb_args.append(prev_arg) - prev_arg = new_arg - elif prev_arg is None: - prev_arg = new_arg - comb_args.append(prev_arg) - if recall: - return TensorProductHilbertSpace(*comb_args) - elif len(comb_args) == 1: - return TensorPowerHilbertSpace(comb_args[0].base, comb_args[0].exp) - else: - return None - - @property - def dimension(self): - arg_list = [arg.dimension for arg in self.args] - if oo in arg_list: - return oo - else: - return reduce(lambda x, y: x*y, arg_list) - - @property - def spaces(self): - """A tuple of the Hilbert spaces in this tensor product.""" - return self.args - - def _spaces_printer(self, printer, *args): - spaces_strs = [] - for arg in self.args: - s = printer._print(arg, *args) - if isinstance(arg, DirectSumHilbertSpace): - s = '(%s)' % s - spaces_strs.append(s) - return spaces_strs - - def _sympyrepr(self, printer, *args): - spaces_reprs = self._spaces_printer(printer, *args) - return "TensorProductHilbertSpace(%s)" % ','.join(spaces_reprs) - - def _sympystr(self, printer, *args): - spaces_strs = self._spaces_printer(printer, *args) - return '*'.join(spaces_strs) - - def _pretty(self, printer, *args): - length = len(self.args) - pform = printer._print('', *args) - for i in range(length): - next_pform = printer._print(self.args[i], *args) - if isinstance(self.args[i], (DirectSumHilbertSpace, - TensorProductHilbertSpace)): - next_pform = prettyForm( - *next_pform.parens(left='(', right=')') - ) - pform = prettyForm(*pform.right(next_pform)) - if i != length - 1: - if printer._use_unicode: - pform = prettyForm(*pform.right(' ' + '\u2a02' + ' ')) - else: - pform = prettyForm(*pform.right(' x ')) - return pform - - def _latex(self, printer, *args): - length = len(self.args) - s = '' - for i in range(length): - arg_s = printer._print(self.args[i], *args) - if isinstance(self.args[i], (DirectSumHilbertSpace, - TensorProductHilbertSpace)): - arg_s = r'\left(%s\right)' % arg_s - s = s + arg_s - if i != length - 1: - s = s + r'\otimes ' - return s - - -class DirectSumHilbertSpace(HilbertSpace): - """A direct sum of Hilbert spaces [1]_. - - This class uses the ``+`` operator to represent direct sums between - different Hilbert spaces. - - A ``DirectSumHilbertSpace`` object takes in an arbitrary number of - ``HilbertSpace`` objects as its arguments. Also, addition of - ``HilbertSpace`` objects will automatically return a direct sum object. - - Examples - ======== - - >>> from sympy.physics.quantum.hilbert import ComplexSpace, FockSpace - >>> from sympy import symbols - - >>> c = ComplexSpace(2) - >>> f = FockSpace() - >>> hs = c+f - >>> hs - C(2)+F - >>> hs.dimension - oo - >>> list(hs.spaces) - [C(2), F] - - References - ========== - - .. [1] http://en.wikipedia.org/wiki/Hilbert_space#Direct_sums - """ - def __new__(cls, *args): - r = cls.eval(args) - if isinstance(r, Basic): - return r - obj = Basic.__new__(cls, *args) - return obj - - @classmethod - def eval(cls, args): - """Evaluates the direct product.""" - new_args = [] - recall = False - #flatten arguments - for arg in args: - if isinstance(arg, DirectSumHilbertSpace): - new_args.extend(arg.args) - recall = True - elif isinstance(arg, HilbertSpace): - new_args.append(arg) - else: - raise TypeError('Hilbert spaces can only be summed with other \ - Hilbert spaces: %r' % arg) - if recall: - return DirectSumHilbertSpace(*new_args) - else: - return None - - @property - def dimension(self): - arg_list = [arg.dimension for arg in self.args] - if oo in arg_list: - return oo - else: - return reduce(lambda x, y: x + y, arg_list) - - @property - def spaces(self): - """A tuple of the Hilbert spaces in this direct sum.""" - return self.args - - def _sympyrepr(self, printer, *args): - spaces_reprs = [printer._print(arg, *args) for arg in self.args] - return "DirectSumHilbertSpace(%s)" % ','.join(spaces_reprs) - - def _sympystr(self, printer, *args): - spaces_strs = [printer._print(arg, *args) for arg in self.args] - return '+'.join(spaces_strs) - - def _pretty(self, printer, *args): - length = len(self.args) - pform = printer._print('', *args) - for i in range(length): - next_pform = printer._print(self.args[i], *args) - if isinstance(self.args[i], (DirectSumHilbertSpace, - TensorProductHilbertSpace)): - next_pform = prettyForm( - *next_pform.parens(left='(', right=')') - ) - pform = prettyForm(*pform.right(next_pform)) - if i != length - 1: - if printer._use_unicode: - pform = prettyForm(*pform.right(' ' + '\u2295' + ' ')) - else: - pform = prettyForm(*pform.right(' + ')) - return pform - - def _latex(self, printer, *args): - length = len(self.args) - s = '' - for i in range(length): - arg_s = printer._print(self.args[i], *args) - if isinstance(self.args[i], (DirectSumHilbertSpace, - TensorProductHilbertSpace)): - arg_s = r'\left(%s\right)' % arg_s - s = s + arg_s - if i != length - 1: - s = s + r'\oplus ' - return s - - -class TensorPowerHilbertSpace(HilbertSpace): - """An exponentiated Hilbert space [1]_. - - Tensor powers (repeated tensor products) are represented by the - operator ``**`` Identical Hilbert spaces that are multiplied together - will be automatically combined into a single tensor power object. - - Any Hilbert space, product, or sum may be raised to a tensor power. The - ``TensorPowerHilbertSpace`` takes two arguments: the Hilbert space; and the - tensor power (number). - - Examples - ======== - - >>> from sympy.physics.quantum.hilbert import ComplexSpace, FockSpace - >>> from sympy import symbols - - >>> n = symbols('n') - >>> c = ComplexSpace(2) - >>> hs = c**n - >>> hs - C(2)**n - >>> hs.dimension - 2**n - - >>> c = ComplexSpace(2) - >>> c*c - C(2)**2 - >>> f = FockSpace() - >>> c*f*f - C(2)*F**2 - - References - ========== - - .. [1] http://en.wikipedia.org/wiki/Hilbert_space#Tensor_products - """ - - def __new__(cls, *args): - r = cls.eval(args) - if isinstance(r, Basic): - return r - return Basic.__new__(cls, *r) - - @classmethod - def eval(cls, args): - new_args = args[0], sympify(args[1]) - exp = new_args[1] - #simplify hs**1 -> hs - if exp == 1: - return args[0] - #simplify hs**0 -> 1 - if exp == 0: - return sympify(1) - #check (and allow) for hs**(x+42+y...) case - if len(exp.atoms()) == 1: - if not (exp.is_Integer and exp >= 0 or exp.is_Symbol): - raise ValueError('Hilbert spaces can only be raised to \ - positive integers or Symbols: %r' % exp) - else: - for power in exp.atoms(): - if not (power.is_Integer or power.is_Symbol): - raise ValueError('Tensor powers can only contain integers \ - or Symbols: %r' % power) - return new_args - - @property - def base(self): - return self.args[0] - - @property - def exp(self): - return self.args[1] - - @property - def dimension(self): - if self.base.dimension == oo: - return oo - else: - return self.base.dimension**self.exp - - def _sympyrepr(self, printer, *args): - return "TensorPowerHilbertSpace(%s,%s)" % (printer._print(self.base, - *args), printer._print(self.exp, *args)) - - def _sympystr(self, printer, *args): - return "%s**%s" % (printer._print(self.base, *args), - printer._print(self.exp, *args)) - - def _pretty(self, printer, *args): - pform_exp = printer._print(self.exp, *args) - if printer._use_unicode: - pform_exp = prettyForm(*pform_exp.left(prettyForm('\u2a02'))) - else: - pform_exp = prettyForm(*pform_exp.left(prettyForm('x'))) - pform_base = printer._print(self.base, *args) - return pform_base**pform_exp - - def _latex(self, printer, *args): - base = printer._print(self.base, *args) - exp = printer._print(self.exp, *args) - return r'{%s}^{\otimes %s}' % (base, exp) -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/quantum/innerproduct.html b/dev-py3k/_modules/sympy/physics/quantum/innerproduct.html deleted file mode 100644 index e9de636d6bd..00000000000 --- a/dev-py3k/_modules/sympy/physics/quantum/innerproduct.html +++ /dev/null @@ -1,254 +0,0 @@ - - - - - - - - - - sympy.physics.quantum.innerproduct — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.quantum.innerproduct

    -"""Symbolic inner product."""
    -
    -
    -from sympy import Expr, conjugate
    -from sympy.printing.pretty.stringpict import prettyForm
    -from sympy.physics.quantum.dagger import Dagger
    -from sympy.physics.quantum.state import KetBase, BraBase
    -
    -__all__ = [
    -    'InnerProduct'
    -]
    -
    -
    -# InnerProduct is not an QExpr because it is really just a regular commutative
    -# number. We have gone back and forth about this, but we gain a lot by having
    -# it subclass Expr. The main challenges were getting Dagger to work
    -# (we use _eval_conjugate) and represent (we can use atoms and subs). Having
    -# it be an Expr, mean that there are no commutative QExpr subclasses,
    -# which simplifies the design of everything.
    -
    -
    [docs]class InnerProduct(Expr): - """An unevaluated inner product between a Bra and a Ket [1]. - - Parameters - ========== - - bra : BraBase or subclass - The bra on the left side of the inner product. - ket : KetBase or subclass - The ket on the right side of the inner product. - - Examples - ======== - - Create an InnerProduct and check its properties: - - >>> from sympy.physics.quantum import Bra, Ket, InnerProduct - >>> b = Bra('b') - >>> k = Ket('k') - >>> ip = b*k - >>> ip - <b|k> - >>> ip.bra - <b| - >>> ip.ket - |k> - - In simple products of kets and bras inner products will be automatically - identified and created:: - - >>> b*k - <b|k> - - But in more complex expressions, there is ambiguity in whether inner or - outer products should be created:: - - >>> k*b*k*b - |k><b|*|k>*<b| - - A user can force the creation of a inner products in a complex expression - by using parentheses to group the bra and ket:: - - >>> k*(b*k)*b - <b|k>*|k>*<b| - - Notice how the inner product <b|k> moved to the left of the expression - because inner products are commutative complex numbers. - - References - ========== - - .. [1] http://en.wikipedia.org/wiki/Inner_product - """ - is_complex = True - - def __new__(cls, bra, ket): - if not isinstance(ket, KetBase): - raise TypeError('KetBase subclass expected, got: %r' % ket) - if not isinstance(bra, BraBase): - raise TypeError('BraBase subclass expected, got: %r' % ket) - obj = Expr.__new__(cls, bra, ket) - return obj - - @property - def bra(self): - return self.args[0] - - @property - def ket(self): - return self.args[1] - - def _eval_conjugate(self): - return InnerProduct(Dagger(self.ket), Dagger(self.bra)) - - def _sympyrepr(self, printer, *args): - return '%s(%s,%s)' % (self.__class__.__name__, - printer._print(self.bra, *args), printer._print(self.ket, *args)) - - def _sympystr(self, printer, *args): - sbra = str(self.bra) - sket = str(self.ket) - return '%s|%s' % (sbra[:-1], sket[1:]) - - def _pretty(self, printer, *args): - # Print state contents - bra = self.bra._print_contents_pretty(printer, *args) - ket = self.ket._print_contents_pretty(printer, *args) - # Print brackets - height = max(bra.height(), ket.height()) - use_unicode = printer._use_unicode - lbracket, _ = self.bra._pretty_brackets(height, use_unicode) - cbracket, rbracket = self.ket._pretty_brackets(height, use_unicode) - # Build innerproduct - pform = prettyForm(*bra.left(lbracket)) - pform = prettyForm(*pform.right(cbracket)) - pform = prettyForm(*pform.right(ket)) - pform = prettyForm(*pform.right(rbracket)) - return pform - - def _latex(self, printer, *args): - bra_label = self.bra._print_contents_latex(printer, *args) - ket = printer._print(self.ket, *args) - return r'\left\langle %s \right. %s' % (bra_label, ket) - - def doit(self, **hints): - try: - r = self.ket._eval_innerproduct(self.bra, **hints) - except NotImplementedError: - try: - r = conjugate( - self.bra.dual._eval_innerproduct(self.ket.dual, **hints) - ) - except NotImplementedError: - r = None - if r is not None: - return r - return self
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/quantum/operator.html b/dev-py3k/_modules/sympy/physics/quantum/operator.html deleted file mode 100644 index c3d03326bf1..00000000000 --- a/dev-py3k/_modules/sympy/physics/quantum/operator.html +++ /dev/null @@ -1,637 +0,0 @@ - - - - - - - - - - sympy.physics.quantum.operator — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.quantum.operator

    -"""Quantum mechanical operators.
    -
    -TODO:
    -
    -* Fix early 0 in apply_operators.
    -* Debug and test apply_operators.
    -* Get cse working with classes in this file.
    -* Doctests and documentation of special methods for InnerProduct, Commutator,
    -  AntiCommutator, represent, apply_operators.
    -"""
    -
    -from sympy import Derivative, Expr
    -from sympy.printing.pretty.stringpict import prettyForm
    -from sympy.physics.quantum.dagger import Dagger
    -from sympy.physics.quantum.qexpr import QExpr, dispatch_method
    -
    -__all__ = [
    -    'Operator',
    -    'HermitianOperator',
    -    'UnitaryOperator',
    -    'OuterProduct',
    -    'DifferentialOperator'
    -]
    -
    -#-----------------------------------------------------------------------------
    -# Operators and outer products
    -#-----------------------------------------------------------------------------
    -
    -
    -
    [docs]class Operator(QExpr): - """Base class for non-commuting quantum operators. - - An operator maps between quantum states [1]_. In quantum mechanics, - observables (including, but not limited to, measured physical values) are - represented as Hermitian operators [2]_. - - Parameters - ========== - - args : tuple - The list of numbers or parameters that uniquely specify the - operator. For time-dependent operators, this will include the time. - - Examples - ======== - - Create an operator and examine its attributes:: - - >>> from sympy.physics.quantum import Operator - >>> from sympy import symbols, I - >>> A = Operator('A') - >>> A - A - >>> A.hilbert_space - H - >>> A.label - (A,) - >>> A.is_commutative - False - - Create another operator and do some arithmetic operations:: - - >>> B = Operator('B') - >>> C = 2*A*A + I*B - >>> C - 2*A**2 + I*B - - Operators don't commute:: - - >>> A.is_commutative - False - >>> B.is_commutative - False - >>> A*B == B*A - False - - Polymonials of operators respect the commutation properties:: - - >>> e = (A+B)**3 - >>> e.expand() - A*B*A + A*B**2 + A**2*B + A**3 + B*A*B + B*A**2 + B**2*A + B**3 - - Operator inverses are handle symbolically:: - - >>> A.inv() - A**(-1) - >>> A*A.inv() - 1 - - References - ========== - - .. [1] http://en.wikipedia.org/wiki/Operator_%28physics%29 - .. [2] http://en.wikipedia.org/wiki/Observable - """ - - @classmethod - def default_args(self): - return ("O",) - #------------------------------------------------------------------------- - # Printing - #------------------------------------------------------------------------- - - _label_separator = ',' - - def _print_operator_name(self, printer, *args): - return printer._print(self.__class__.__name__, *args) - - _print_operator_name_latex = _print_operator_name - - def _print_operator_name_pretty(self, printer, *args): - return prettyForm(self.__class__.__name__) - - def _print_contents(self, printer, *args): - if len(self.label) == 1: - return self._print_label(printer, *args) - else: - return '%s(%s)' % ( - self._print_operator_name(printer, *args), - self._print_label(printer, *args) - ) - - def _print_contents_pretty(self, printer, *args): - if len(self.label) == 1: - return self._print_label_pretty(printer, *args) - else: - pform = self._print_operator_name_pretty(printer, *args) - label_pform = self._print_label_pretty(printer, *args) - label_pform = prettyForm( - *label_pform.parens(left='(', right=')') - ) - pform = prettyForm(*pform.right((label_pform))) - return pform - - def _print_contents_latex(self, printer, *args): - if len(self.label) == 1: - return self._print_label_latex(printer, *args) - else: - return r'%s\left(%s\right)' % ( - self._print_operator_name_latex(printer, *args), - self._print_label_latex(printer, *args) - ) - - #------------------------------------------------------------------------- - # _eval_* methods - #------------------------------------------------------------------------- - - def _eval_commutator(self, other, **options): - """Evaluate [self, other] if known, return None if not known.""" - return dispatch_method(self, '_eval_commutator', other, **options) - - def _eval_anticommutator(self, other, **options): - """Evaluate [self, other] if known.""" - return dispatch_method(self, '_eval_anticommutator', other, **options) - - #------------------------------------------------------------------------- - # Operator application - #------------------------------------------------------------------------- - - def _apply_operator(self, ket, **options): - return dispatch_method(self, '_apply_operator', ket, **options) - - def matrix_element(self, *args): - raise NotImplementedError('matrix_elements is not defined') - - #------------------------------------------------------------------------- - # Printing - #------------------------------------------------------------------------- - - def inverse(self): - return self._eval_inverse() - - inv = inverse - - def _eval_inverse(self): - # TODO: make non-commutative Exprs print powers using A**-1, not 1/A. - return self**(-1) - -
    -
    [docs]class HermitianOperator(Operator): - """A Hermitian operator that satisfies H == Dagger(H). - - Parameters - ========== - - args : tuple - The list of numbers or parameters that uniquely specify the - operator. For time-dependent operators, this will include the time. - - Examples - ======== - - >>> from sympy.physics.quantum import Dagger, HermitianOperator - >>> H = HermitianOperator('H') - >>> Dagger(H) - H - """ - - is_hermitian = True - - def _eval_inverse(self): - if isinstance(self, UnitaryOperator): - return self - else: - return Operator._eval_inverse(self) - - def _eval_power(self, exp): - if isinstance(self, UnitaryOperator): - if exp == -1: - return Operator._eval_power(self, exp) - elif abs(exp) % 2 == 0: - return self*(Operator._eval_inverse(self)) - else: - return self - else: - return Operator._eval_power(self, exp) - -
    -
    [docs]class UnitaryOperator(Operator): - """A unitary operator that satisfies U*Dagger(U) == 1. - - Parameters - ========== - - args : tuple - The list of numbers or parameters that uniquely specify the - operator. For time-dependent operators, this will include the time. - - Examples - ======== - - >>> from sympy.physics.quantum import Dagger, UnitaryOperator - >>> U = UnitaryOperator('U') - >>> U*Dagger(U) - 1 - """ - - def _eval_adjoint(self): - return self._eval_inverse() - -
    -
    [docs]class OuterProduct(Operator): - """An unevaluated outer product between a ket and bra. - - This constructs an outer product between any subclass of ``KetBase`` and - ``BraBase`` as ``|a><b|``. An ``OuterProduct`` inherits from Operator as they act as - operators in quantum expressions. For reference see [1]_. - - Parameters - ========== - - ket : KetBase - The ket on the left side of the outer product. - bar : BraBase - The bra on the right side of the outer product. - - Examples - ======== - - Create a simple outer product by hand and take its dagger:: - - >>> from sympy.physics.quantum import Ket, Bra, OuterProduct, Dagger - >>> from sympy.physics.quantum import Operator - - >>> k = Ket('k') - >>> b = Bra('b') - >>> op = OuterProduct(k, b) - >>> op - |k><b| - >>> op.hilbert_space - H - >>> op.ket - |k> - >>> op.bra - <b| - >>> Dagger(op) - |b><k| - - In simple products of kets and bras outer products will be automatically - identified and created:: - - >>> k*b - |k><b| - - But in more complex expressions, outer products are not automatically - created:: - - >>> A = Operator('A') - >>> A*k*b - A*|k>*<b| - - A user can force the creation of an outer product in a complex expression - by using parentheses to group the ket and bra:: - - >>> A*(k*b) - A*|k><b| - - References - ========== - - .. [1] http://en.wikipedia.org/wiki/Outer_product - """ - is_commutative = False - - def __new__(cls, *args, **old_assumptions): - from sympy.physics.quantum.state import KetBase, BraBase - ket = args[0] - bra = args[1] - if not isinstance(ket, KetBase): - raise TypeError('KetBase subclass expected, got: %r' % ket) - if not isinstance(bra, BraBase): - raise TypeError('BraBase subclass expected, got: %r' % ket) - if not ket.dual_class() == bra.__class__: - raise TypeError( - 'ket and bra are not dual classes: %r, %r' % - (ket.__class__, bra.__class__) - ) - # TODO: make sure the hilbert spaces of the bra and ket are compatible - obj = Expr.__new__(cls, *args, **old_assumptions) - obj.hilbert_space = ket.hilbert_space - return obj - - @property -
    [docs] def ket(self): - """Return the ket on the left side of the outer product.""" - return self.args[0] -
    - @property -
    [docs] def bra(self): - """Return the bra on the right side of the outer product.""" - return self.args[1] -
    - def _eval_adjoint(self): - return OuterProduct(Dagger(self.bra), Dagger(self.ket)) - - def _sympystr(self, printer, *args): - return str(self.ket) + str(self.bra) - - def _sympyrepr(self, printer, *args): - return '%s(%s,%s)' % (self.__class__.__name__, - printer._print(self.ket, *args), printer._print(self.bra, *args)) - - def _pretty(self, printer, *args): - pform = self.ket._pretty(printer, *args) - return prettyForm(*pform.right(self.bra._pretty(printer, *args))) - - def _latex(self, printer, *args): - k = printer._print(self.ket, *args) - b = printer._print(self.bra, *args) - return k + b - - def _represent(self, **options): - k = self.ket._represent(**options) - b = self.bra._represent(**options) - return k*b - - def _eval_trace(self, **kwargs): - # TODO if operands are tensorproducts this may be will be handled - # differently. - - return self.ket._eval_trace(self.bra, **kwargs) - -
    -
    [docs]class DifferentialOperator(Operator): - """An operator for representing the differential operator, i.e. d/dx - - It is initialized by passing two arguments. The first is an arbitrary - expression that involves a function, such as ``Derivative(f(x), x)``. The - second is the function (e.g. ``f(x)``) which we are to replace with the - ``Wavefunction`` that this ``DifferentialOperator`` is applied to. - - Parameters - ========== - - expr : Expr - The arbitrary expression which the appropriate Wavefunction is to be - substituted into - - func : Expr - A function (e.g. f(x)) which is to be replaced with the appropriate - Wavefunction when this DifferentialOperator is applied - - Examples - ======== - - You can define a completely arbitrary expression and specify where the - Wavefunction is to be substituted - - >>> from sympy import Derivative, Function, Symbol - >>> from sympy.physics.quantum.operator import DifferentialOperator - >>> from sympy.physics.quantum.state import Wavefunction - >>> from sympy.physics.quantum.qapply import qapply - >>> f = Function('f') - >>> x = Symbol('x') - >>> d = DifferentialOperator(1/x*Derivative(f(x), x), f(x)) - >>> w = Wavefunction(x**2, x) - >>> d.function - f(x) - >>> d.variables - (x,) - >>> qapply(d*w) - Wavefunction(2, x) - - """ - - @property -
    [docs] def variables(self): - """ - Returns the variables with which the function in the specified - arbitrary expression is evaluated - - Examples - ======== - - >>> from sympy.physics.quantum.operator import DifferentialOperator - >>> from sympy import Symbol, Function, Derivative - >>> x = Symbol('x') - >>> f = Function('f') - >>> d = DifferentialOperator(1/x*Derivative(f(x), x), f(x)) - >>> d.variables - (x,) - >>> y = Symbol('y') - >>> d = DifferentialOperator(Derivative(f(x, y), x) + - ... Derivative(f(x, y), y), f(x, y)) - >>> d.variables - (x, y) - """ - - return self.args[-1].args -
    - @property -
    [docs] def function(self): - """ - Returns the function which is to be replaced with the Wavefunction - - Examples - ======== - - >>> from sympy.physics.quantum.operator import DifferentialOperator - >>> from sympy import Function, Symbol, Derivative - >>> x = Symbol('x') - >>> f = Function('f') - >>> d = DifferentialOperator(Derivative(f(x), x), f(x)) - >>> d.function - f(x) - >>> y = Symbol('y') - >>> d = DifferentialOperator(Derivative(f(x, y), x) + - ... Derivative(f(x, y), y), f(x, y)) - >>> d.function - f(x, y) - """ - - return self.args[-1] -
    - @property -
    [docs] def expr(self): - """ - Returns the arbitary expression which is to have the Wavefunction - substituted into it - - Examples - ======== - - >>> from sympy.physics.quantum.operator import DifferentialOperator - >>> from sympy import Function, Symbol, Derivative - >>> x = Symbol('x') - >>> f = Function('f') - >>> d = DifferentialOperator(Derivative(f(x), x), f(x)) - >>> d.expr - Derivative(f(x), x) - >>> y = Symbol('y') - >>> d = DifferentialOperator(Derivative(f(x, y), x) + - ... Derivative(f(x, y), y), f(x, y)) - >>> d.expr - Derivative(f(x, y), x) + Derivative(f(x, y), y) - """ - - return self.args[0] -
    - @property -
    [docs] def free_symbols(self): - """ - Return the free symbols of the expression. - """ - - return self.expr.free_symbols -
    - def _apply_operator_Wavefunction(self, func): - from sympy.physics.quantum.state import Wavefunction - var = self.variables - wf_vars = func.args[1:] - - f = self.function - new_expr = self.expr.subs(f, func(*var)) - new_expr = new_expr.doit() - - return Wavefunction(new_expr, *wf_vars) - - def _eval_derivative(self, symbol): - new_expr = Derivative(self.expr, symbol) - return DifferentialOperator(new_expr, self.args[-1]) - - #------------------------------------------------------------------------- - # Printing - #------------------------------------------------------------------------- - - def _print(self, printer, *args): - return '%s(%s)' % ( - self._print_operator_name(printer, *args), - self._print_label(printer, *args) - ) - - def _print_pretty(self, printer, *args): - pform = self._print_operator_name_pretty(printer, *args) - label_pform = self._print_label_pretty(printer, *args) - label_pform = prettyForm( - *label_pform.parens(left='(', right=')') - ) - pform = prettyForm(*pform.right((label_pform))) - return pform
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/quantum/operatorset.html b/dev-py3k/_modules/sympy/physics/quantum/operatorset.html deleted file mode 100644 index cb6a27f9d48..00000000000 --- a/dev-py3k/_modules/sympy/physics/quantum/operatorset.html +++ /dev/null @@ -1,396 +0,0 @@ - - - - - - - - - - sympy.physics.quantum.operatorset — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.quantum.operatorset

    -""" A module for mapping operators to their corresponding eigenstates
    -and vice versa
    -
    -It contains a global dictionary with eigenstate-operator pairings.
    -If a new state-operator pair is created, this dictionary should be
    -updated as well.
    -
    -It also contains functions operators_to_state and state_to_operators
    -for mapping between the two. These can handle both classes and
    -instances of operators and states. See the individual function
    -descriptions for details.
    -
    -TODO List:
    -- Update the dictionary with a complete list of state-operator pairs
    -"""
    -
    -
    -from sympy.physics.quantum.cartesian import (XOp, YOp, ZOp, XKet, PxOp, PxKet,
    -                                             PositionKet3D)
    -from sympy.physics.quantum.operator import Operator
    -from sympy.physics.quantum.state import StateBase, BraBase, Ket
    -from sympy.physics.quantum.spin import (JxOp, JyOp, JzOp, J2Op, JxKet, JyKet,
    -                                        JzKet)
    -
    -__all__ = [
    -    'operators_to_state',
    -    'state_to_operators'
    -]
    -
    -#state_mapping stores the mappings between states and their associated
    -#operators or tuples of operators. This should be updated when new
    -#classes are written! Entries are of the form PxKet : PxOp or
    -#something like 3DKet : (ROp, ThetaOp, PhiOp)
    -
    -#frozenset is used so that the reverse mapping can be made
    -#(regular sets are not hashable because they are mutable
    -state_mapping = { JxKet: frozenset((J2Op, JxOp)),
    -                  JyKet: frozenset((J2Op, JyOp)),
    -                  JzKet: frozenset((J2Op, JzOp)),
    -                  Ket: Operator,
    -                  PositionKet3D: frozenset((XOp, YOp, ZOp)),
    -                  PxKet: PxOp,
    -                  XKet: XOp }
    -
    -op_mapping = dict((v, k) for k, v in state_mapping.items())
    -
    -
    -
    [docs]def operators_to_state(operators, **options): - """ Returns the eigenstate of the given operator or set of operators - - A global function for mapping operator classes to their associated - states. It takes either an Operator or a set of operators and - returns the state associated with these. - - This function can handle both instances of a given operator or - just the class itself (i.e. both XOp() and XOp) - - There are multiple use cases to consider: - - 1) A class or set of classes is passed: First, we try to - instantiate default instances for these operators. If this fails, - then the class is simply returned. If we succeed in instantiating - default instances, then we try to call state._operators_to_state - on the operator instances. If this fails, the class is returned. - Otherwise, the instance returned by _operators_to_state is returned. - - 2) An instance or set of instances is passed: In this case, - state._operators_to_state is called on the instances passed. If - this fails, a state class is returned. If the method returns an - instance, that instance is returned. - - In both cases, if the operator class or set does not exist in the - state_mapping dictionary, None is returned. - - Parameters - ========== - - arg: Operator or set - The class or instance of the operator or set of operators - to be mapped to a state - - Examples - ======== - - >>> from sympy.physics.quantum.cartesian import XOp, PxOp - >>> from sympy.physics.quantum.operatorset import operators_to_state - >>> from sympy.physics.quantum.operator import Operator - >>> operators_to_state(XOp) - |x> - >>> operators_to_state(XOp()) - |x> - >>> operators_to_state(PxOp) - |px> - >>> operators_to_state(PxOp()) - |px> - >>> operators_to_state(Operator) - |psi> - >>> operators_to_state(Operator()) - |psi> - """ - - if not (isinstance(operators, Operator) - or isinstance(operators, set) or issubclass(operators, Operator)): - raise NotImplementedError("Argument is not an Operator or a set!") - - if isinstance(operators, set): - for s in operators: - if not (isinstance(s, Operator) - or issubclass(s, Operator)): - raise NotImplementedError("Set is not all Operators!") - - #ops = tuple(operators) - ops = frozenset(operators) - - if ops in op_mapping: # ops is a list of classes in this case - #Try to get an object from default instances of the - #operators...if this fails, return the class - try: - op_instances = [op() for op in ops] - ret = _get_state(op_mapping[ops], set(op_instances), **options) - except NotImplementedError: - ret = op_mapping[ops] - - return ret - else: - tmp = [type(o) for o in ops] - classes = frozenset(tmp) - - if classes in op_mapping: - ret = _get_state(op_mapping[classes], ops, **options) - else: - ret = None - - return ret - else: - if operators in op_mapping: - try: - op_instance = operators() - ret = _get_state(op_mapping[operators], op_instance, **options) - except NotImplementedError: - ret = op_mapping[operators] - - return ret - elif type(operators) in op_mapping: - return _get_state(op_mapping[type(operators)], operators, **options) - else: - return None - -
    -
    [docs]def state_to_operators(state, **options): - """ Returns the operator or set of operators corresponding to the - given eigenstate - - A global function for mapping state classes to their associated - operators or sets of operators. It takes either a state class - or instance. - - This function can handle both instances of a given state or just - the class itself (i.e. both XKet() and XKet) - - There are multiple use cases to consider: - - 1) A state class is passed: In this case, we first try - instantiating a default instance of the class. If this succeeds, - then we try to call state._state_to_operators on that instance. - If the creation of the default instance or if the calling of - _state_to_operators fails, then either an operator class or set of - operator classes is returned. Otherwise, the appropriate - operator instances are returned. - - 2) A state instance is returned: Here, state._state_to_operators - is called for the instance. If this fails, then a class or set of - operator classes is returned. Otherwise, the instances are returned. - - In either case, if the state's class does not exist in - state_mapping, None is returned. - - Parameters - ========== - - arg: StateBase class or instance (or subclasses) - The class or instance of the state to be mapped to an - operator or set of operators - - Examples - ======== - - >>> from sympy.physics.quantum.cartesian import XKet, PxKet, XBra, PxBra - >>> from sympy.physics.quantum.operatorset import state_to_operators - >>> from sympy.physics.quantum.state import Ket, Bra - >>> state_to_operators(XKet) - X - >>> state_to_operators(XKet()) - X - >>> state_to_operators(PxKet) - Px - >>> state_to_operators(PxKet()) - Px - >>> state_to_operators(PxBra) - Px - >>> state_to_operators(XBra) - X - >>> state_to_operators(Ket) - O - >>> state_to_operators(Bra) - O - """ - - if not (isinstance(state, StateBase) or issubclass(state, StateBase)): - raise NotImplementedError("Argument is not a state!") - - if state in state_mapping: # state is a class - state_inst = _make_default(state) - try: - ret = _get_ops(state_inst, - _make_set(state_mapping[state]), **options) - except (NotImplementedError, TypeError): - ret = state_mapping[state] - elif type(state) in state_mapping: - ret = _get_ops(state, - _make_set(state_mapping[type(state)]), **options) - elif isinstance(state, BraBase) and state.dual_class() in state_mapping: - ret = _get_ops(state, - _make_set(state_mapping[state.dual_class()])) - elif issubclass(state, BraBase) and state.dual_class() in state_mapping: - state_inst = _make_default(state) - try: - ret = _get_ops(state_inst, - _make_set(state_mapping[state.dual_class()])) - except (NotImplementedError, TypeError): - ret = state_mapping[state.dual_class()] - else: - ret = None - - return _make_set(ret) - -
    -def _make_default(expr): - try: - ret = expr() - except Exception: - ret = expr - - return ret - - -def _get_state(state_class, ops, **options): - # Try to get a state instance from the operator INSTANCES. - # If this fails, get the class - try: - ret = state_class._operators_to_state(ops, **options) - except NotImplementedError: - ret = _make_default(state_class) - - return ret - - -def _get_ops(state_inst, op_classes, **options): - # Try to get operator instances from the state INSTANCE. - # If this fails, just return the classes - try: - ret = state_inst._state_to_operators(op_classes, **options) - except NotImplementedError: - if isinstance(op_classes, (set, tuple, frozenset)): - ret = tuple([_make_default(x) for x in op_classes]) - else: - ret = _make_default(op_classes) - - if isinstance(ret, set) and len(ret) == 1: - return ret[0] - - return ret - - -def _make_set(ops): - if isinstance(ops, (tuple, list, frozenset)): - return set(ops) - else: - return ops -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/quantum/piab.html b/dev-py3k/_modules/sympy/physics/quantum/piab.html deleted file mode 100644 index a1ee41face0..00000000000 --- a/dev-py3k/_modules/sympy/physics/quantum/piab.html +++ /dev/null @@ -1,184 +0,0 @@ - - - - - - - - - - sympy.physics.quantum.piab — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.quantum.piab

    -"""1D quantum particle in a box."""
    -
    -from sympy import Symbol, pi, sqrt, sin, Interval, S
    -
    -from sympy.physics.quantum.operator import HermitianOperator
    -from sympy.physics.quantum.state import Ket, Bra
    -from sympy.physics.quantum.constants import hbar
    -from sympy.functions.special.tensor_functions import KroneckerDelta
    -from sympy.physics.quantum.hilbert import L2
    -
    -m = Symbol('m')
    -L = Symbol('L')
    -
    -
    -__all__ = [
    -    'PIABHamiltonian',
    -    'PIABKet',
    -    'PIABBra'
    -]
    -
    -
    -
    [docs]class PIABHamiltonian(HermitianOperator): - """Particle in a box Hamiltonian operator.""" - - @classmethod - def _eval_hilbert_space(cls, label): - return L2(Interval(S.NegativeInfinity, S.Infinity)) - - def _apply_operator_PIABKet(self, ket, **options): - n = ket.label[0] - return (n**2*pi**2*hbar**2)/(2*m*L**2)*ket - -
    -
    [docs]class PIABKet(Ket): - """Particle in a box eigenket.""" - - @classmethod - def _eval_hilbert_space(cls, args): - return L2(Interval(S.NegativeInfinity, S.Infinity)) - - @classmethod - def dual_class(self): - return PIABBra - - def _represent_default_basis(self, **options): - return self._represent_XOp(None, **options) - - def _represent_XOp(self, basis, **options): - x = Symbol('x') - n = Symbol('n') - subs_info = options.get('subs', {}) - return sqrt(2/L)*sin(n*pi*x/L).subs(subs_info) - - def _eval_innerproduct_PIABBra(self, bra): - return KroneckerDelta(bra.label[0], self.label[0]) - -
    -
    [docs]class PIABBra(Bra): - """Particle in a box eigenbra.""" - - @classmethod - def _eval_hilbert_space(cls, label): - return L2(Interval(S.NegativeInfinity, S.Infinity)) - - @classmethod - def dual_class(self): - return PIABKet
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/quantum/qapply.html b/dev-py3k/_modules/sympy/physics/quantum/qapply.html deleted file mode 100644 index ee680f13688..00000000000 --- a/dev-py3k/_modules/sympy/physics/quantum/qapply.html +++ /dev/null @@ -1,298 +0,0 @@ - - - - - - - - - - sympy.physics.quantum.qapply — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.quantum.qapply

    -"""Logic for applying operators to states.
    -
    -Todo:
    -* Sometimes the final result needs to be expanded, we should do this by hand.
    -"""
    -
    -
    -from sympy import Add, Mul, Pow, sympify, S
    -
    -from sympy.physics.quantum.anticommutator import AntiCommutator
    -from sympy.physics.quantum.commutator import Commutator
    -from sympy.physics.quantum.dagger import Dagger
    -from sympy.physics.quantum.innerproduct import InnerProduct
    -from sympy.physics.quantum.operator import OuterProduct, Operator
    -from sympy.physics.quantum.state import State, KetBase, BraBase, Wavefunction
    -from sympy.physics.quantum.tensorproduct import TensorProduct
    -
    -__all__ = [
    -    'qapply'
    -]
    -
    -
    -#-----------------------------------------------------------------------------
    -# Main code
    -#-----------------------------------------------------------------------------
    -
    -
    [docs]def qapply(e, **options): - """Apply operators to states in a quantum expression. - - Parameters - ========== - - e : Expr - The expression containing operators and states. This expression tree - will be walked to find operators acting on states symbolically. - options : dict - A dict of key/value pairs that determine how the operator actions - are carried out. - - The following options are valid: - - * ``dagger``: try to apply Dagger operators to the left - (default: False). - * ``ip_doit``: call ``.doit()`` in inner products when they are - encountered (default: True). - - Returns - ======= - - e : Expr - The original expression, but with the operators applied to states. - """ - from sympy.physics.quantum.density import Density - - dagger = options.get('dagger', False) - - if e == 0: - return S.Zero - - # This may be a bit aggressive but ensures that everything gets expanded - # to its simplest form before trying to apply operators. This includes - # things like (A+B+C)*|a> and A*(|a>+|b>) and all Commutators and - # TensorProducts. The only problem with this is that if we can't apply - # all the Operators, we have just expanded everything. - # TODO: don't expand the scalars in front of each Mul. - e = e.expand(commutator=True, tensorproduct=True) - - # If we just have a raw ket, return it. - if isinstance(e, KetBase): - return e - - # We have an Add(a, b, c, ...) and compute - # Add(qapply(a), qapply(b), ...) - elif isinstance(e, Add): - result = 0 - for arg in e.args: - result += qapply(arg, **options) - return result - - # For a Density operator call qapply on its state - elif isinstance(e, Density): - new_args = [(qapply(state, **options), prob) for (state, - prob) in e.args] - return Density(*new_args) - - # For a raw TensorProduct, call qapply on its args. - elif isinstance(e, TensorProduct): - return TensorProduct(*[qapply(t, **options) for t in e.args]) - - # For a Pow, call qapply on its base. - elif isinstance(e, Pow): - return qapply(e.base, **options)**e.exp - - # We have a Mul where there might be actual operators to apply to kets. - elif isinstance(e, Mul): - result = qapply_Mul(e, **options) - if result == e and dagger: - return Dagger(qapply_Mul(Dagger(e), **options)) - else: - return result - - # In all other cases (State, Operator, Pow, Commutator, InnerProduct, - # OuterProduct) we won't ever have operators to apply to kets. - else: - return e - -
    -def qapply_Mul(e, **options): - - ip_doit = options.get('ip_doit', True) - - args = list(e.args) - - # If we only have 0 or 1 args, we have nothing to do and return. - if len(args) <= 1 or not isinstance(e, Mul): - return e - rhs = args.pop() - lhs = args.pop() - - # Make sure we have two non-commutative objects before proceeding. - if (sympify(rhs).is_commutative and not isinstance(rhs, Wavefunction)) or \ - (sympify(lhs).is_commutative and not isinstance(lhs, Wavefunction)): - return e - - # For a Pow with an integer exponent, apply one of them and reduce the - # exponent by one. - if isinstance(lhs, Pow) and lhs.exp.is_Integer: - args.append(lhs.base**(lhs.exp - 1)) - lhs = lhs.base - - # Pull OuterProduct apart - if isinstance(lhs, OuterProduct): - args.append(lhs.ket) - lhs = lhs.bra - - # Call .doit() on Commutator/AntiCommutator. - if isinstance(lhs, (Commutator, AntiCommutator)): - comm = lhs.doit() - if isinstance(comm, Add): - return qapply( - e.func(*(args + [comm.args[0], rhs])) + - e.func(*(args + [comm.args[1], rhs])), - **options - ) - else: - return qapply(e.func(*args)*comm*rhs, **options) - - # Apply tensor products of operators to states - if isinstance(lhs, TensorProduct) and all([isinstance(arg, Operator) or arg == 1 for arg in lhs.args]) and \ - isinstance(rhs, TensorProduct) and all([isinstance(arg, State) or arg == 1 for arg in rhs.args]) and \ - len(lhs.args) == len(rhs.args): - result = TensorProduct(*[qapply(lhs.args[n]*rhs.args[n], **options) for n in range(len(lhs.args))]).expand(tensorproduct=True) - return qapply_Mul(e.func(*args), **options)*result - - # Now try to actually apply the operator and build an inner product. - try: - result = lhs._apply_operator(rhs, **options) - except (NotImplementedError, AttributeError): - try: - result = rhs._apply_operator(lhs, **options) - except (NotImplementedError, AttributeError): - if isinstance(lhs, BraBase) and isinstance(rhs, KetBase): - result = InnerProduct(lhs, rhs) - if ip_doit: - result = result.doit() - else: - result = None - - # TODO: I may need to expand before returning the final result. - if result == 0: - return S.Zero - elif result is None: - if len(args) == 0: - # We had two args to begin with so args=[]. - return e - else: - return qapply_Mul(e.func(*(args + [lhs])), **options)*rhs - elif isinstance(result, InnerProduct): - return result*qapply_Mul(e.func(*args), **options) - else: # result is a scalar times a Mul, Add or TensorProduct - return qapply(e.func(*args)*result, **options) -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/quantum/qft.html b/dev-py3k/_modules/sympy/physics/quantum/qft.html deleted file mode 100644 index 611ef29f9a7..00000000000 --- a/dev-py3k/_modules/sympy/physics/quantum/qft.html +++ /dev/null @@ -1,326 +0,0 @@ - - - - - - - - - - sympy.physics.quantum.qft — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.quantum.qft

    -"""An implementation of qubits and gates acting on them.
    -
    -Todo:
    -
    -* Update docstrings.
    -* Update tests.
    -* Implement apply using decompose.
    -* Implement represent using decompose or something smarter. For this to
    -  work we first have to implement represent for SWAP.
    -* Decide if we want upper index to be inclusive in the constructor.
    -* Fix the printing of Rk gates in plotting.
    -"""
    -
    -from sympy import Expr, Matrix, exp, I, pi, Integer, Symbol
    -from sympy.functions import sqrt
    -
    -from sympy.physics.quantum.qapply import qapply
    -from sympy.physics.quantum.qexpr import QuantumError, QExpr
    -from sympy.matrices import eye
    -from sympy.physics.quantum.tensorproduct import matrix_tensor_product
    -
    -from sympy.physics.quantum.gate import (
    -    Gate, HadamardGate, SwapGate, OneQubitGate, CGate, PhaseGate, TGate, ZGate
    -)
    -
    -__all__ = [
    -    'QFT',
    -    'IQFT',
    -    'RkGate',
    -    'Rk'
    -]
    -
    -#-----------------------------------------------------------------------------
    -# Fourier stuff
    -#-----------------------------------------------------------------------------
    -
    -
    -
    [docs]class RkGate(OneQubitGate): - """This is the R_k gate of the QTF.""" - gate_name = 'Rk' - gate_name_latex = 'R' - - def __new__(cls, *args): - if len(args) != 2: - raise QuantumError( - 'Rk gates only take two arguments, got: %r' % args - ) - # For small k, Rk gates simplify to other gates, using these - # substitutions give us familiar results for the QFT for small numbers - # of qubits. - target = args[0] - k = args[1] - if k == 1: - return ZGate(target) - elif k == 2: - return PhaseGate(target) - elif k == 3: - return TGate(target) - args = cls._eval_args(args) - inst = Expr.__new__(cls, *args) - inst.hilbert_space = cls._eval_hilbert_space(args) - return inst - - @classmethod - def _eval_args(cls, args): - # Fall back to this, because Gate._eval_args assumes that args is - # all targets and can't contain duplicates. - return QExpr._eval_args(args) - - @property - def k(self): - return self.label[1] - - @property - def targets(self): - return self.label[:1] - - @property - def gate_name_plot(self): - return r'$%s_%s$' % (self.gate_name_latex, str(self.k)) - - def get_target_matrix(self, format='sympy'): - if format == 'sympy': - return Matrix([[1, 0], [0, exp(Integer(2)*pi*I/(Integer(2)**self.k))]]) - raise NotImplementedError( - 'Invalid format for the R_k gate: %r' % format) - -
    -Rk = RkGate - - -class Fourier(Gate): - """Superclass of Quantum Fourier and Inverse Quantum Fourier Gates.""" - - @classmethod - def _eval_args(self, args): - if len(args) != 2: - raise QuantumError( - 'QFT/IQFT only takes two arguments, got: %r' % args - ) - if args[0] >= args[1]: - raise QuantumError("Start must be smaller than finish") - return Gate._eval_args(args) - - def _represent_default_basis(self, **options): - return self._represent_ZGate(None, **options) - - def _represent_ZGate(self, basis, **options): - """ - Represents the (I)QFT In the Z Basis - """ - nqubits = options.get('nqubits', 0) - if nqubits == 0: - raise QuantumError( - 'The number of qubits must be given as nqubits.') - if nqubits < self.min_qubits: - raise QuantumError( - 'The number of qubits %r is too small for the gate.' % nqubits - ) - size = self.size - omega = self.omega - - #Make a matrix that has the basic Fourier Transform Matrix - arrayFT = [[omega**( - i*j % size)/sqrt(size) for i in range(size)] for j in range(size)] - matrixFT = Matrix(arrayFT) - - #Embed the FT Matrix in a higher space, if necessary - if self.label[0] != 0: - matrixFT = matrix_tensor_product(eye(2**self.label[0]), matrixFT) - if self.min_qubits < nqubits: - matrixFT = matrix_tensor_product( - matrixFT, eye(2**(nqubits - self.min_qubits))) - - return matrixFT - - @property - def targets(self): - return list(range(self.label[0], self.label[1])) - - @property - def min_qubits(self): - return self.label[1] - - @property - def size(self): - """Size is the size of the QFT matrix""" - return 2**(self.label[1] - self.label[0]) - - @property - def omega(self): - return Symbol('omega') - - -
    [docs]class QFT(Fourier): - """The forward quantum Fourier transform.""" - - gate_name = 'QFT' - gate_name_latex = 'QFT' - -
    [docs] def decompose(self): - """Decomposes QFT into elementary gates.""" - start = self.label[0] - finish = self.label[1] - circuit = 1 - for level in reversed(list(range(start, finish))): - circuit = HadamardGate(level)*circuit - for i in range(level - start): - circuit = CGate(level - i - 1, RkGate(level, i + 2))*circuit - for i in range((finish - start)//2): - circuit = SwapGate(i + start, finish - i - 1)*circuit - return circuit -
    - def _apply_operator_Qubit(self, qubits, **options): - return qapply(self.decompose()*qubits) - - def _eval_inverse(self): - return IQFT(*self.args) - - @property - def omega(self): - return exp(2*pi*I/self.size) - -
    -
    [docs]class IQFT(Fourier): - """The inverse quantum Fourier transform.""" - - gate_name = 'IQFT' - gate_name_latex = '{QFT^{-1}}' - -
    [docs] def decompose(self): - """Decomposes IQFT into elementary gates.""" - start = self.args[0] - finish = self.args[1] - circuit = 1 - for i in range((finish - start)//2): - circuit = SwapGate(i + start, finish - i - 1)*circuit - for level in range(start, finish): - for i in reversed(list(range(level - start))): - circuit = CGate(level - i - 1, RkGate(level, -i - 2))*circuit - circuit = HadamardGate(level)*circuit - return circuit -
    - def _eval_inverse(self): - return QFT(*self.args) - - @property - def omega(self): - return exp(-2*pi*I/self.size)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/quantum/qubit.html b/dev-py3k/_modules/sympy/physics/quantum/qubit.html deleted file mode 100644 index 0430ea13d57..00000000000 --- a/dev-py3k/_modules/sympy/physics/quantum/qubit.html +++ /dev/null @@ -1,881 +0,0 @@ - - - - - - - - - - sympy.physics.quantum.qubit — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.quantum.qubit

    -"""Qubits for quantum computing.
    -
    -Todo:
    -* Finish implementing measurement logic. This should include POVM.
    -* Update docstrings.
    -* Update tests.
    -"""
    -
    -import math
    -
    -from sympy import Integer, log, Mul, Add, Pow, conjugate
    -from sympy.core.basic import sympify
    -from sympy.matrices import Matrix, zeros
    -from sympy.printing.pretty.stringpict import prettyForm
    -
    -
    -from sympy.physics.quantum.hilbert import ComplexSpace
    -from sympy.physics.quantum.state import Ket, Bra, State
    -
    -from sympy.physics.quantum.qexpr import QuantumError
    -from sympy.physics.quantum.represent import represent
    -from sympy.physics.quantum.matrixutils import (
    -    numpy_ndarray, scipy_sparse_matrix
    -)
    -from sympy.mpmath.libmp.libintmath import bitcount
    -
    -__all__ = [
    -    'Qubit',
    -    'QubitBra',
    -    'IntQubit',
    -    'IntQubitBra',
    -    'qubit_to_matrix',
    -    'matrix_to_qubit',
    -    'matrix_to_density',
    -    'measure_all',
    -    'measure_partial',
    -    'measure_partial_oneshot',
    -    'measure_all_oneshot'
    -]
    -
    -#-----------------------------------------------------------------------------
    -# Qubit Classes
    -#-----------------------------------------------------------------------------
    -
    -
    -class QubitState(State):
    -    """Base class for Qubit and QubitBra."""
    -
    -    #-------------------------------------------------------------------------
    -    # Initialization/creation
    -    #-------------------------------------------------------------------------
    -
    -    @classmethod
    -    def _eval_args(cls, args):
    -        # If we are passed a QubitState or subclass, we just take its qubit
    -        # values directly.
    -        if len(args) == 1 and isinstance(args[0], QubitState):
    -            return args[0].qubit_values
    -
    -        # Turn strings into tuple of strings
    -        if len(args) == 1 and isinstance(args[0], str):
    -            args = tuple(args[0])
    -
    -        args = sympify(args)
    -
    -        # Validate input (must have 0 or 1 input)
    -        for element in args:
    -            if not (element == 1 or element == 0):
    -                raise ValueError(
    -                    "Qubit values must be 0 or 1, got: %r" % element)
    -        return args
    -
    -    @classmethod
    -    def _eval_hilbert_space(cls, args):
    -        return ComplexSpace(2)**len(args)
    -
    -    #-------------------------------------------------------------------------
    -    # Properties
    -    #-------------------------------------------------------------------------
    -
    -    @property
    -    def dimension(self):
    -        """The number of Qubits in the state."""
    -        return len(self.qubit_values)
    -
    -    @property
    -    def nqubits(self):
    -        return self.dimension
    -
    -    @property
    -    def qubit_values(self):
    -        """Returns the values of the qubits as a tuple."""
    -        return self.label
    -
    -    #-------------------------------------------------------------------------
    -    # Special methods
    -    #-------------------------------------------------------------------------
    -
    -    def __len__(self):
    -        return self.dimension
    -
    -    def __getitem__(self, bit):
    -        return self.qubit_values[int(self.dimension - bit - 1)]
    -
    -    #-------------------------------------------------------------------------
    -    # Utility methods
    -    #-------------------------------------------------------------------------
    -
    -    def flip(self, *bits):
    -        """Flip the bit(s) given."""
    -        newargs = list(self.qubit_values)
    -        for i in bits:
    -            bit = int(self.dimension - i - 1)
    -            if newargs[bit] == 1:
    -                newargs[bit] = 0
    -            else:
    -                newargs[bit] = 1
    -        return self.__class__(*tuple(newargs))
    -
    -
    -
    [docs]class Qubit(QubitState, Ket): - """A multi-qubit ket in the computational (z) basis. - - We use the normal convention that the least significant qubit is on the - right, so ``|00001>`` has a 1 in the least significant qubit. - - Parameters - ========== - - values : list, str - The qubit values as a list of ints ([0,0,0,1,1,]) or a string ('011'). - - Examples - ======== - - Create a qubit in a couple of different ways and look at their attributes: - - >>> from sympy.physics.quantum.qubit import Qubit - >>> Qubit(0,0,0) - |000> - >>> q = Qubit('0101') - >>> q - |0101> - - >>> q.nqubits - 4 - >>> len(q) - 4 - >>> q.dimension - 4 - >>> q.qubit_values - (0, 1, 0, 1) - - We can flip the value of an individual qubit: - - >>> q.flip(1) - |0111> - - We can take the dagger of a Qubit to get a bra: - - >>> from sympy.physics.quantum.dagger import Dagger - >>> Dagger(q) - <0101| - >>> type(Dagger(q)) - <class 'sympy.physics.quantum.qubit.QubitBra'> - - Inner products work as expected: - - >>> ip = Dagger(q)*q - >>> ip - <0101|0101> - >>> ip.doit() - 1 - """ - - @classmethod - def dual_class(self): - return QubitBra - - def _eval_innerproduct_QubitBra(self, bra, **hints): - if self.label == bra.label: - return Integer(1) - else: - return Integer(0) - - def _represent_default_basis(self, **options): - return self._represent_ZGate(None, **options) - - def _represent_ZGate(self, basis, **options): - """Represent this qubits in the computational basis (ZGate). - """ - format = options.get('format', 'sympy') - n = 1 - definite_state = 0 - for it in reversed(self.qubit_values): - definite_state += n*it - n = n*2 - result = [0]*(2**self.dimension) - result[int(definite_state)] = 1 - if format == 'sympy': - return Matrix(result) - elif format == 'numpy': - import numpy as np - return np.matrix(result, dtype='complex').transpose() - elif format == 'scipy.sparse': - from scipy import sparse - return sparse.csr_matrix(result, dtype='complex').transpose() - - def _eval_trace(self, bra, **kwargs): - indices = kwargs.get('indices', []) - - #sort index list to begin trace from most-significant - #qubit - sorted_idx = list(indices) - if len(sorted_idx) == 0: - sorted_idx = list(range(0, self.nqubits)) - sorted_idx.sort() - - #trace out for each of index - new_mat = self*bra - for i in range(len(sorted_idx) - 1, -1, -1): - # start from tracing out from leftmost qubit - new_mat = self._reduced_density(new_mat, int(sorted_idx[i])) - - if (len(sorted_idx) == self.nqubits): - #in case full trace was requested - return new_mat[0] - else: - return matrix_to_density(new_mat) - - def _reduced_density(self, matrix, qubit, **options): - """Compute the reduced density matrix by tracing out one qubit. - The qubit argument should be of type python int, since it is used - in bit operations - """ - def find_index_that_is_projected(j, k, qubit): - bit_mask = 2**qubit - 1 - return ((j >> qubit) << (1 + qubit)) + (j & bit_mask) + (k << qubit) - - old_matrix = represent(matrix, **options) - old_size = old_matrix.cols - #we expect the old_size to be even - new_size = old_size//2 - new_matrix = Matrix().zeros(new_size) - - for i in range(new_size): - for j in range(new_size): - for k in range(2): - col = find_index_that_is_projected(j, k, qubit) - row = find_index_that_is_projected(i, k, qubit) - new_matrix[i, j] += old_matrix[row, col] - - return new_matrix - -
    -
    [docs]class QubitBra(QubitState, Bra): - """A multi-qubit bra in the computational (z) basis. - - We use the normal convention that the least significant qubit is on the - right, so ``|00001>`` has a 1 in the least significant qubit. - - Parameters - ========== - - values : list, str - The qubit values as a list of ints ([0,0,0,1,1,]) or a string ('011'). - - See also - ======== - - Qubit: Examples using qubits - - """ - @classmethod - def dual_class(self): - return Qubit - -
    -class IntQubitState(QubitState): - """A base class for qubits that work with binary representations.""" - - @classmethod - def _eval_args(cls, args): - # The case of a QubitState instance - if len(args) == 1 and isinstance(args[0], QubitState): - return QubitState._eval_args(args) - # For a single argument, we construct the binary representation of - # that integer with the minimal number of bits. - if len(args) == 1 and args[0] > 1: - #rvalues is the minimum number of bits needed to express the number - rvalues = reversed(range(bitcount(abs(args[0])))) - qubit_values = [(args[0] >> i) & 1 for i in rvalues] - return QubitState._eval_args(qubit_values) - # For two numbers, the second number is the number of bits - # on which it is expressed, so IntQubit(0,5) == |00000>. - elif len(args) == 2 and args[1] > 1: - need = bitcount(abs(args[0])) - if args[1] < need: - raise ValueError( - 'cannot represent %s with %s bits' % (args[0], args[1])) - qubit_values = [(args[0] >> i) & 1 for i in reversed(list(range(args[1])))] - return QubitState._eval_args(qubit_values) - else: - return QubitState._eval_args(args) - - def as_int(self): - """Return the numerical value of the qubit.""" - number = 0 - n = 1 - for i in reversed(self.qubit_values): - number += n*i - n = n << 1 - return number - - def _print_label(self, printer, *args): - return str(self.as_int()) - - def _print_label_pretty(self, printer, *args): - label = self._print_label(printer, *args) - return prettyForm(label) - - _print_label_repr = _print_label - _print_label_latex = _print_label - - -
    [docs]class IntQubit(IntQubitState, Qubit): - """A qubit ket that store integers as binary numbers in qubit values. - - The differences between this class and ``Qubit`` are: - - * The form of the constructor. - * The qubit values are printed as their corresponding integer, rather - than the raw qubit values. The internal storage format of the qubit - values in the same as ``Qubit``. - - Parameters - ========== - - values : int, tuple - If a single argument, the integer we want to represent in the qubit - values. This integer will be represented using the fewest possible - number of qubits. If a pair of integers, the first integer gives the - integer to represent in binary form and the second integer gives - the number of qubits to use. - - Examples - ======== - - Create a qubit for the integer 5: - - >>> from sympy.physics.quantum.qubit import IntQubit - >>> from sympy.physics.quantum.qubit import Qubit - >>> q = IntQubit(5) - >>> q - |5> - - We can also create an ``IntQubit`` by passing a ``Qubit`` instance. - - >>> q = IntQubit(Qubit('101')) - >>> q - |5> - >>> q.as_int() - 5 - >>> q.nqubits - 3 - >>> q.qubit_values - (1, 0, 1) - - We can go back to the regular qubit form. - - >>> Qubit(q) - |101> - """ - @classmethod - def dual_class(self): - return IntQubitBra - -
    -
    [docs]class IntQubitBra(IntQubitState, QubitBra): - """A qubit bra that store integers as binary numbers in qubit values.""" - - @classmethod - def dual_class(self): - return IntQubit - - -#----------------------------------------------------------------------------- -# Qubit <---> Matrix conversion functions -#----------------------------------------------------------------------------- - -
    -
    [docs]def matrix_to_qubit(matrix): - """Convert from the matrix repr. to a sum of Qubit objects. - - Parameters - ---------- - matrix : Matrix, numpy.matrix, scipy.sparse - The matrix to build the Qubit representation of. This works with - sympy matrices, numpy matrices and scipy.sparse sparse matrices. - - Examples - -------- - - Represent a state and then go back to its qubit form: - - >>> from sympy.physics.quantum.qubit import matrix_to_qubit, Qubit - >>> from sympy.physics.quantum.gate import Z - >>> from sympy.physics.quantum.represent import represent - >>> q = Qubit('01') - >>> matrix_to_qubit(represent(q)) - |01> - """ - # Determine the format based on the type of the input matrix - format = 'sympy' - if isinstance(matrix, numpy_ndarray): - format = 'numpy' - if isinstance(matrix, scipy_sparse_matrix): - format = 'scipy.sparse' - - # Make sure it is of correct dimensions for a Qubit-matrix representation. - # This logic should work with sympy, numpy or scipy.sparse matrices. - if matrix.shape[0] == 1: - mlistlen = matrix.shape[1] - nqubits = log(mlistlen, 2) - ket = False - cls = QubitBra - elif matrix.shape[1] == 1: - mlistlen = matrix.shape[0] - nqubits = log(mlistlen, 2) - ket = True - cls = Qubit - else: - raise QuantumError( - 'Matrix must be a row/column vector, got %r' % matrix - ) - if not isinstance(nqubits, Integer): - raise QuantumError('Matrix must be a row/column vector of size ' - '2**nqubits, got: %r' % matrix) - # Go through each item in matrix, if element is non-zero, make it into a - # Qubit item times the element. - result = 0 - for i in range(mlistlen): - if ket: - element = matrix[i, 0] - else: - element = matrix[0, i] - if format == 'numpy' or format == 'scipy.sparse': - element = complex(element) - if element != 0.0: - # Form Qubit array; 0 in bit-locations where i is 0, 1 in - # bit-locations where i is 1 - qubit_array = [int(i & (1 << x) != 0) for x in range(nqubits)] - qubit_array.reverse() - result = result + element*cls(*qubit_array) - - # If sympy simplified by pulling out a constant coefficient, undo that. - if isinstance(result, (Mul, Add, Pow)): - result = result.expand() - - return result - -
    -
    [docs]def matrix_to_density(mat): - """ - Works by finding the eigenvectors and eigenvalues of the matrix. - We know we can decompose rho by doing: - sum(EigenVal*|Eigenvect><Eigenvect|) - """ - from sympy.physics.quantum.density import Density - eigen = mat.eigenvects() - args = [[matrix_to_qubit(Matrix( - [vector, ])), x[0]] for x in eigen for vector in x[2] if x[0] != 0] - if (len(args) == 0): - return 0 - else: - return Density(*args) - -
    -
    [docs]def qubit_to_matrix(qubit, format='sympy'): - """Coverts an Add/Mul of Qubit objects into it's matrix representation - - This function is the inverse of ``matrix_to_qubit`` and is a shorthand - for ``represent(qubit)``. - """ - return represent(qubit, format=format) - - -#----------------------------------------------------------------------------- -# Measurement -#----------------------------------------------------------------------------- - -
    -
    [docs]def measure_all(qubit, format='sympy', normalize=True): - """Perform an ensemble measurement of all qubits. - - Parameters - ========== - - qubit : Qubit, Add - The qubit to measure. This can be any Qubit or a linear combination - of them. - format : str - The format of the intermediate matrices to use. Possible values are - ('sympy','numpy','scipy.sparse'). Currently only 'sympy' is - implemented. - - Returns - ======= - - result : list - A list that consists of primitive states and their probabilities. - - Examples - ======== - - >>> from sympy.physics.quantum.qubit import Qubit, measure_all - >>> from sympy.physics.quantum.gate import H, X, Y, Z - >>> from sympy.physics.quantum.qapply import qapply - - >>> c = H(0)*H(1)*Qubit('00') - >>> c - H(0)*H(1)*|00> - >>> q = qapply(c) - >>> measure_all(q) - [(|00>, 1/4), (|01>, 1/4), (|10>, 1/4), (|11>, 1/4)] - """ - m = qubit_to_matrix(qubit, format) - - if format == 'sympy': - results = [] - - if normalize: - m = m.normalized() - - size = max(m.shape) # Max of shape to account for bra or ket - nqubits = int(math.log(size)/math.log(2)) - for i in range(size): - if m[i] != 0.0: - results.append( - (Qubit(IntQubit(i, nqubits)), m[i]*conjugate(m[i])) - ) - return results - else: - raise NotImplementedError( - "This function can't handle non-sympy matrix formats yet" - ) - -
    -
    [docs]def measure_partial(qubit, bits, format='sympy', normalize=True): - """Perform a partial ensemble measure on the specifed qubits. - - Parameters - ========== - - qubits : Qubit - The qubit to measure. This can be any Qubit or a linear combination - of them. - bits : tuple - The qubits to measure. - format : str - The format of the intermediate matrices to use. Possible values are - ('sympy','numpy','scipy.sparse'). Currently only 'sympy' is - implemented. - - Returns - ======= - - result : list - A list that consists of primitive states and their probabilities. - - Examples - ======== - - >>> from sympy.physics.quantum.qubit import Qubit, measure_partial - >>> from sympy.physics.quantum.gate import H, X, Y, Z - >>> from sympy.physics.quantum.qapply import qapply - - >>> c = H(0)*H(1)*Qubit('00') - >>> c - H(0)*H(1)*|00> - >>> q = qapply(c) - >>> measure_partial(q, (0,)) - [(sqrt(2)*|00>/2 + sqrt(2)*|10>/2, 1/2), (sqrt(2)*|01>/2 + sqrt(2)*|11>/2, 1/2)] - """ - m = qubit_to_matrix(qubit, format) - - if isinstance(bits, (int, Integer)): - bits = (int(bits),) - - if format == 'sympy': - if normalize: - m = m.normalized() - - possible_outcomes = _get_possible_outcomes(m, bits) - - # Form output from function. - output = [] - for outcome in possible_outcomes: - # Calculate probability of finding the specified bits with - # given values. - prob_of_outcome = 0 - prob_of_outcome += (outcome.H*outcome)[0] - - # If the output has a chance, append it to output with found - # probability. - if prob_of_outcome != 0: - if normalize: - next_matrix = matrix_to_qubit(outcome.normalized()) - else: - next_matrix = matrix_to_qubit(outcome) - - output.append(( - next_matrix, - prob_of_outcome - )) - - return output - else: - raise NotImplementedError( - "This function can't handle non-sympy matrix formats yet" - ) - -
    -
    [docs]def measure_partial_oneshot(qubit, bits, format='sympy'): - """Perform a partial oneshot measurement on the specified qubits. - - A oneshot measurement is equivalent to performing a measurement on a - quantum system. This type of measurement does not return the probabilities - like an ensemble measurement does, but rather returns *one* of the - possible resulting states. The exact state that is returned is determined - by picking a state randomly according to the ensemble probabilities. - - Parameters - ---------- - qubits : Qubit - The qubit to measure. This can be any Qubit or a linear combination - of them. - bits : tuple - The qubits to measure. - format : str - The format of the intermediate matrices to use. Possible values are - ('sympy','numpy','scipy.sparse'). Currently only 'sympy' is - implemented. - - Returns - ------- - result : Qubit - The qubit that the system collapsed to upon measurement. - """ - import random - m = qubit_to_matrix(qubit, format) - - if format == 'sympy': - m = m.normalized() - possible_outcomes = _get_possible_outcomes(m, bits) - - # Form output from function - random_number = random.random() - total_prob = 0 - for outcome in possible_outcomes: - # Calculate probability of finding the specified bits - # with given values - total_prob += (outcome.H*outcome)[0] - if total_prob >= random_number: - return matrix_to_qubit(outcome.normalized()) - else: - raise NotImplementedError( - "This function can't handle non-sympy matrix formats yet" - ) - -
    -def _get_possible_outcomes(m, bits): - """Get the possible states that can be produced in a measurement. - - Parameters - ---------- - m : Matrix - The matrix representing the state of the system. - bits : tuple, list - Which bits will be measured. - - Returns - ------- - result : list - The list of possible states which can occur given this measurement. - These are un-normalized so we can derive the probability of finding - this state by taking the inner product with itself - """ - - # This is filled with loads of dirty binary tricks...You have been warned - - size = max(m.shape) # Max of shape to account for bra or ket - nqubits = int(math.log(size, 2) + .1) # Number of qubits possible - - # Make the output states and put in output_matrices, nothing in them now. - # Each state will represent a possible outcome of the measurement - # Thus, output_matrices[0] is the matrix which we get when all measured - # bits return 0. and output_matrices[1] is the matrix for only the 0th - # bit being true - output_matrices = [] - for i in range(1 << len(bits)): - output_matrices.append(zeros(2**nqubits, 1)) - - # Bitmasks will help sort how to determine possible outcomes. - # When the bit mask is and-ed with a matrix-index, - # it will determine which state that index belongs to - bit_masks = [] - for bit in bits: - bit_masks.append(1 << bit) - - # Make possible outcome states - for i in range(2**nqubits): - trueness = 0 # This tells us to which output_matrix this value belongs - # Find trueness - for j in range(len(bit_masks)): - if i & bit_masks[j]: - trueness += j + 1 - # Put the value in the correct output matrix - output_matrices[trueness][i] = m[i] - return output_matrices - - -
    [docs]def measure_all_oneshot(qubit, format='sympy'): - """Perform a oneshot ensemble measurement on all qubits. - - A oneshot measurement is equivalent to performing a measurement on a - quantum system. This type of measurement does not return the probabilities - like an ensemble measurement does, but rather returns *one* of the - possible resulting states. The exact state that is returned is determined - by picking a state randomly according to the ensemble probabilities. - - Parameters - ---------- - qubits : Qubit - The qubit to measure. This can be any Qubit or a linear combination - of them. - format : str - The format of the intermediate matrices to use. Possible values are - ('sympy','numpy','scipy.sparse'). Currently only 'sympy' is - implemented. - - Returns - ------- - result : Qubit - The qubit that the system collapsed to upon measurement. - """ - import random - m = qubit_to_matrix(qubit) - - if format == 'sympy': - m = m.normalized() - random_number = random.random() - total = 0 - result = 0 - for i in m: - total += i*i.conjugate() - if total > random_number: - break - result += 1 - return Qubit(IntQubit(result, int(math.log(max(m.shape), 2) + .1))) - else: - raise NotImplementedError( - "This function can't handle non-sympy matrix formats yet" - )
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/quantum/represent.html b/dev-py3k/_modules/sympy/physics/quantum/represent.html deleted file mode 100644 index 1a4ca9c5944..00000000000 --- a/dev-py3k/_modules/sympy/physics/quantum/represent.html +++ /dev/null @@ -1,669 +0,0 @@ - - - - - - - - - - sympy.physics.quantum.represent — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.quantum.represent

    -"""Logic for representing operators in state in various bases.
    -
    -TODO:
    -
    -* Get represent working with continuous hilbert spaces.
    -* Document default basis functionality.
    -"""
    -
    -from sympy import Add, Expr, I, integrate, Mul, Pow
    -from sympy.physics.quantum.dagger import Dagger
    -from sympy.physics.quantum.commutator import Commutator
    -from sympy.physics.quantum.anticommutator import AntiCommutator
    -from sympy.physics.quantum.innerproduct import InnerProduct
    -from sympy.physics.quantum.qexpr import QExpr
    -from sympy.physics.quantum.tensorproduct import TensorProduct
    -from sympy.physics.quantum.matrixutils import flatten_scalar
    -from sympy.physics.quantum.state import KetBase, BraBase, StateBase
    -from sympy.physics.quantum.operator import Operator, OuterProduct
    -from sympy.physics.quantum.qapply import qapply
    -from sympy.physics.quantum.operatorset import operators_to_state, state_to_operators
    -
    -__all__ = [
    -    'represent',
    -    'rep_innerproduct',
    -    'rep_expectation',
    -    'integrate_result',
    -    'get_basis',
    -    'enumerate_states'
    -]
    -
    -#-----------------------------------------------------------------------------
    -# Represent
    -#-----------------------------------------------------------------------------
    -
    -
    -def _sympy_to_scalar(e):
    -    """Convert from a sympy scalar to a Python scalar."""
    -    if isinstance(e, Expr):
    -        if e.is_Integer:
    -            return int(e)
    -        elif e.is_Float:
    -            return float(e)
    -        elif e.is_Rational:
    -            return float(e)
    -        elif e.is_Number or e.is_NumberSymbol or e == I:
    -            return complex(e)
    -    raise TypeError('Expected number, got: %r' % e)
    -
    -
    -
    [docs]def represent(expr, **options): - """Represent the quantum expression in the given basis. - - In quantum mechanics abstract states and operators can be represented in - various basis sets. Under this operation the follow transforms happen: - - * Ket -> column vector or function - * Bra -> row vector of function - * Operator -> matrix or differential operator - - This function is the top-level interface for this action. - - This function walks the sympy expression tree looking for ``QExpr`` - instances that have a ``_represent`` method. This method is then called - and the object is replaced by the representation returned by this method. - By default, the ``_represent`` method will dispatch to other methods - that handle the representation logic for a particular basis set. The - naming convention for these methods is the following:: - - def _represent_FooBasis(self, e, basis, **options) - - This function will have the logic for representing instances of its class - in the basis set having a class named ``FooBasis``. - - Parameters - ========== - - expr : Expr - The expression to represent. - basis : Operator, basis set - An object that contains the information about the basis set. If an - operator is used, the basis is assumed to be the orthonormal - eigenvectors of that operator. In general though, the basis argument - can be any object that contains the basis set information. - options : dict - Key/value pairs of options that are passed to the underlying method - that does finds the representation. These options can be used to - control how the representation is done. For example, this is where - the size of the basis set would be set. - - Returns - ======= - - e : Expr - The sympy expression of the represented quantum expression. - - Examples - ======== - - Here we subclass ``Operator`` and ``Ket`` to create the z-spin operator - and its spin 1/2 up eigenstate. By definining the ``_represent_SzOp`` - method, the ket can be represented in the z-spin basis. - - >>> from sympy.physics.quantum import Operator, represent, Ket - >>> from sympy import Matrix - - >>> class SzUpKet(Ket): - ... def _represent_SzOp(self, basis, **options): - ... return Matrix([1,0]) - ... - >>> class SzOp(Operator): - ... pass - ... - >>> sz = SzOp('Sz') - >>> up = SzUpKet('up') - >>> represent(up, basis=sz) - [1] - [0] - - Here we see an example of representations in a continuous - basis. We see that the result of representing various combinations - of cartesian position operators and kets give us continuous - expressions involving DiracDelta functions. - - >>> from sympy.physics.quantum.cartesian import XOp, XKet, XBra - >>> X = XOp() - >>> x = XKet() - >>> y = XBra('y') - >>> represent(X*x) - x*DiracDelta(x - x_2) - >>> represent(X*x*y) - x*DiracDelta(x - x_3)*DiracDelta(x_1 - y) - - """ - - format = options.get('format', 'sympy') - if isinstance(expr, QExpr) and not isinstance(expr, OuterProduct): - options['replace_none'] = False - temp_basis = get_basis(expr, **options) - if temp_basis is not None: - options['basis'] = temp_basis - try: - return expr._represent(**options) - except NotImplementedError as strerr: - #If no _represent_FOO method exists, map to the - #appropriate basis state and try - #the other methods of representation - options['replace_none'] = True - - if isinstance(expr, (KetBase, BraBase)): - try: - return rep_innerproduct(expr, **options) - except NotImplementedError: - raise NotImplementedError(strerr) - elif isinstance(expr, Operator): - try: - return rep_expectation(expr, **options) - except NotImplementedError: - raise NotImplementedError(strerr) - else: - raise NotImplementedError(strerr) - elif isinstance(expr, Add): - result = represent(expr.args[0], **options) - for args in expr.args[1:]: - # scipy.sparse doesn't support += so we use plain = here. - result = result + represent(args, **options) - return result - elif isinstance(expr, Pow): - base, exp = expr.as_base_exp() - if format == 'numpy' or format == 'scipy.sparse': - exp = _sympy_to_scalar(exp) - return represent(base, **options)**exp - elif isinstance(expr, TensorProduct): - new_args = [represent(arg, **options) for arg in expr.args] - return TensorProduct(*new_args) - elif isinstance(expr, Dagger): - return Dagger(represent(expr.args[0], **options)) - elif isinstance(expr, Commutator): - A = represent(expr.args[0], **options) - B = represent(expr.args[1], **options) - return A*B - B*A - elif isinstance(expr, AntiCommutator): - A = represent(expr.args[0], **options) - B = represent(expr.args[1], **options) - return A*B + B*A - elif isinstance(expr, InnerProduct): - return represent(Mul(expr.bra, expr.ket), **options) - elif not (isinstance(expr, Mul) or isinstance(expr, OuterProduct)): - # For numpy and scipy.sparse, we can only handle numerical prefactors. - if format == 'numpy' or format == 'scipy.sparse': - return _sympy_to_scalar(expr) - return expr - - if not (isinstance(expr, Mul) or isinstance(expr, OuterProduct)): - raise TypeError('Mul expected, got: %r' % expr) - - if "index" in options: - options["index"] += 1 - else: - options["index"] = 1 - - if not "unities" in options: - options["unities"] = [] - - result = represent(expr.args[-1], **options) - last_arg = expr.args[-1] - - for arg in reversed(expr.args[:-1]): - if isinstance(last_arg, Operator): - options["index"] += 1 - options["unities"].append(options["index"]) - elif isinstance(last_arg, BraBase) and isinstance(arg, KetBase): - options["index"] += 1 - elif isinstance(last_arg, KetBase) and isinstance(arg, Operator): - options["unities"].append(options["index"]) - elif isinstance(last_arg, KetBase) and isinstance(arg, BraBase): - options["unities"].append(options["index"]) - - result = represent(arg, **options)*result - last_arg = arg - - # All three matrix formats create 1 by 1 matrices when inner products of - # vectors are taken. In these cases, we simply return a scalar. - result = flatten_scalar(result) - - result = integrate_result(expr, result, **options) - - return result - -
    -
    [docs]def rep_innerproduct(expr, **options): - """ - Returns an innerproduct like representation (e.g. ``<x'|x>``) for the - given state. - - Attempts to calculate inner product with a bra from the specified - basis. Should only be passed an instance of KetBase or BraBase - - Parameters - ========== - - expr : KetBase or BraBase - The expression to be represented - - Examples - ======== - - >>> from sympy.physics.quantum.represent import rep_innerproduct - >>> from sympy.physics.quantum.cartesian import XOp, XKet, PxOp, PxKet - >>> rep_innerproduct(XKet()) - DiracDelta(x - x_1) - >>> rep_innerproduct(XKet(), basis=PxOp()) - sqrt(2)*exp(-I*px_1*x/hbar)/(2*sqrt(hbar)*sqrt(pi)) - >>> rep_innerproduct(PxKet(), basis=XOp()) - sqrt(2)*exp(I*px*x_1/hbar)/(2*sqrt(hbar)*sqrt(pi)) - - """ - - if not isinstance(expr, (KetBase, BraBase)): - raise TypeError("expr passed is not a Bra or Ket") - - basis = get_basis(expr, **options) - - if not isinstance(basis, StateBase): - raise NotImplementedError("Can't form this representation!") - - if not "index" in options: - options["index"] = 1 - - basis_kets = enumerate_states(basis, options["index"], 2) - - if isinstance(expr, BraBase): - bra = expr - ket = (basis_kets[1] if basis_kets[0].dual == expr else basis_kets[0]) - else: - bra = (basis_kets[1].dual if basis_kets[0] - == expr else basis_kets[0].dual) - ket = expr - - prod = InnerProduct(bra, ket) - result = prod.doit() - - format = options.get('format', 'sympy') - return expr._format_represent(result, format) - -
    -
    [docs]def rep_expectation(expr, **options): - """ - Returns an ``<x'|A|x>`` type representation for the given operator. - - Parameters - ========== - - expr : Operator - Operator to be represented in the specified basis - - Examples - ======== - - >>> from sympy.physics.quantum.cartesian import XOp, XKet, PxOp, PxKet - >>> from sympy.physics.quantum.represent import rep_expectation - >>> rep_expectation(XOp()) - x_1*DiracDelta(x_1 - x_2) - >>> rep_expectation(XOp(), basis=PxOp()) - <px_2|*X*|px_1> - >>> rep_expectation(XOp(), basis=PxKet()) - <px_2|*X*|px_1> - - """ - - if not "index" in options: - options["index"] = 1 - - if not isinstance(expr, Operator): - raise TypeError("The passed expression is not an operator") - - basis_state = get_basis(expr, **options) - - if basis_state is None or not isinstance(basis_state, StateBase): - raise NotImplementedError("Could not get basis kets for this operator") - - basis_kets = enumerate_states(basis_state, options["index"], 2) - - bra = basis_kets[1].dual - ket = basis_kets[0] - - return qapply(bra*expr*ket) - -
    -
    [docs]def integrate_result(orig_expr, result, **options): - """ - Returns the result of integrating over any unities ``(|x><x|)`` in - the given expression. Intended for integrating over the result of - representations in continuous bases. - - This function integrates over any unities that may have been - inserted into the quantum expression and returns the result. - It uses the interval of the Hilbert space of the basis state - passed to it in order to figure out the limits of integration. - The unities option must be - specified for this to work. - - Note: This is mostly used internally by represent(). Examples are - given merely to show the use cases. - - Parameters - ========== - - orig_expr : quantum expression - The original expression which was to be represented - - result: Expr - The resulting representation that we wish to integrate over - - Examples - ======== - - >>> from sympy import symbols, DiracDelta - >>> from sympy.physics.quantum.represent import integrate_result - >>> from sympy.physics.quantum.cartesian import XOp, XKet - >>> x_ket = XKet() - >>> X_op = XOp() - >>> x, x_1, x_2 = symbols('x, x_1, x_2') - >>> integrate_result(X_op*x_ket, x*DiracDelta(x-x_1)*DiracDelta(x_1-x_2)) - x*DiracDelta(x - x_1)*DiracDelta(x_1 - x_2) - >>> integrate_result(X_op*x_ket, x*DiracDelta(x-x_1)*DiracDelta(x_1-x_2), - ... unities=[1]) - x*DiracDelta(x - x_2) - - """ - if not isinstance(result, Expr): - return result - - options['replace_none'] = True - if not "basis" in options: - arg = orig_expr.args[-1] - options["basis"] = get_basis(arg, **options) - elif not isinstance(options["basis"], StateBase): - options["basis"] = get_basis(orig_expr, **options) - - basis = options.pop("basis", None) - - if basis is None: - return result - - unities = options.pop("unities", []) - - if len(unities) == 0: - return result - - kets = enumerate_states(basis, unities) - coords = [k.label[0] for k in kets] - - for coord in coords: - if coord in result.free_symbols: - #TODO: Add support for sets of operators - basis_op = state_to_operators(basis) - start = basis_op.hilbert_space.interval.start - end = basis_op.hilbert_space.interval.end - result = integrate(result, (coord, start, end)) - - return result - -
    -
    [docs]def get_basis(expr, **options): - """ - Returns a basis state instance corresponding to the basis specified in - options=s. If no basis is specified, the function tries to form a default - basis state of the given expression. - - There are three behaviors: - - 1. The basis specified in options is already an instance of StateBase. If - this is the case, it is simply returned. If the class is specified but - not an instance, a default instance is returned. - - 2. The basis specified is an operator or set of operators. If this - is the case, the operator_to_state mapping method is used. - - 3. No basis is specified. If expr is a state, then a default instance of - its class is returned. If expr is an operator, then it is mapped to the - corresponding state. If it is neither, then we cannot obtain the basis - state. - - If the basis cannot be mapped, then it is not changed. - - This will be called from within represent, and represent will - only pass QExpr's. - - TODO (?): Support for Muls and other types of expressions? - - Parameters - ========== - - expr : Operator or StateBase - Expression whose basis is sought - - Examples - ======== - - >>> from sympy.physics.quantum.represent import get_basis - >>> from sympy.physics.quantum.cartesian import XOp, XKet, PxOp, PxKet - >>> x = XKet() - >>> X = XOp() - >>> get_basis(x) - |x> - >>> get_basis(X) - |x> - >>> get_basis(x, basis=PxOp()) - |px> - >>> get_basis(x, basis=PxKet) - |px> - - """ - - basis = options.pop("basis", None) - replace_none = options.pop("replace_none", True) - - if basis is None and not replace_none: - return None - - if basis is None: - if isinstance(expr, KetBase): - return _make_default(expr.__class__) - elif isinstance(expr, BraBase): - return _make_default((expr.dual_class())) - elif isinstance(expr, Operator): - state_inst = operators_to_state(expr) - return (state_inst if state_inst is not None else None) - else: - return None - elif (isinstance(basis, Operator) or - (not isinstance(basis, StateBase) and issubclass(basis, Operator))): - state = operators_to_state(basis) - if state is None: - return None - elif isinstance(state, StateBase): - return state - else: - return _make_default(state) - elif isinstance(basis, StateBase): - return basis - elif issubclass(basis, StateBase): - return _make_default(basis) - else: - return None - -
    -def _make_default(expr): - try: - expr = expr() - except Exception: - return expr - - return expr - - -
    [docs]def enumerate_states(*args, **options): - """ - Returns instances of the given state with dummy indices appended - - Operates in two different modes: - - 1. Two arguments are passed to it. The first is the base state which is to - be indexed, and the second argument is a list of indices to append. - - 2. Three arguments are passed. The first is again the base state to be - indexed. The second is the start index for counting. The final argument - is the number of kets you wish to receive. - - Tries to call state._enumerate_state. If this fails, returns an empty list - - Parameters - ========== - - args : list - See list of operation modes above for explanation - - Examples - ======== - - >>> from sympy.physics.quantum.cartesian import XBra, XKet - >>> from sympy.physics.quantum.represent import enumerate_states - >>> test = XKet('foo') - >>> enumerate_states(test, 1, 3) - [|foo_1>, |foo_2>, |foo_3>] - >>> test2 = XBra('bar') - >>> enumerate_states(test2, [4, 5, 10]) - [<bar_4|, <bar_5|, <bar_10|] - - """ - - state = args[0] - - if not (len(args) == 2 or len(args) == 3): - raise NotImplementedError("Wrong number of arguments!") - - if not isinstance(state, StateBase): - raise TypeError("First argument is not a state!") - - if len(args) == 3: - num_states = args[2] - options['start_index'] = args[1] - else: - num_states = len(args[1]) - options['index_list'] = args[1] - - try: - ret = state._enumerate_state(num_states, **options) - except NotImplementedError: - ret = [] - - return ret
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/quantum/shor.html b/dev-py3k/_modules/sympy/physics/quantum/shor.html deleted file mode 100644 index 7a8baa51455..00000000000 --- a/dev-py3k/_modules/sympy/physics/quantum/shor.html +++ /dev/null @@ -1,320 +0,0 @@ - - - - - - - - - - sympy.physics.quantum.shor — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.quantum.shor

    -"""Shor's algorithm and helper functions.
    -
    -Todo:
    -
    -* Get the CMod gate working again using the new Gate API.
    -* Fix everything.
    -* Update docstrings and reformat.
    -* Remove print statements. We may want to think about a better API for this.
    -"""
    -import math
    -import random
    -
    -from sympy import Mul, S
    -from sympy import log, sqrt
    -from sympy.core.numbers import igcd
    -from sympy.utilities.iterables import variations
    -
    -from sympy.physics.quantum.gate import Gate
    -from sympy.physics.quantum.qubit import Qubit, measure_partial_oneshot
    -from sympy.physics.quantum.qapply import qapply
    -from sympy.physics.quantum.qft import QFT
    -from sympy.physics.quantum.qexpr import QuantumError
    -
    -
    -class OrderFindingException(QuantumError):
    -    pass
    -
    -
    -
    [docs]class CMod(Gate): - """A controlled mod gate. - - This is black box controlled Mod function for use by shor's algorithm. - TODO implement a decompose property that returns how to do this in terms - of elementary gates - """ - - @classmethod - def _eval_args(cls, args): - # t = args[0] - # a = args[1] - # N = args[2] - raise NotImplementedError('The CMod gate has not been completed.') - - @property -
    [docs] def t(self): - """Size of 1/2 input register. First 1/2 holds output.""" - return self.label[0] -
    - @property -
    [docs] def a(self): - """Base of the controlled mod function.""" - return self.label[1] -
    - @property -
    [docs] def N(self): - """N is the type of modular arithmetic we are doing.""" - return self.label[2] -
    - def _apply_operator_Qubit(self, qubits, **options): - """ - This directly calculates the controlled mod of the second half of - the register and puts it in the second - This will look pretty when we get Tensor Symbolically working - """ - n = 1 - k = 0 - # Determine the value stored in high memory. - for i in range(self.t): - k = k + n*qubits[self.t + i] - n = n*2 - - # The value to go in low memory will be out. - out = int(self.a**k % self.N) - - # Create array for new qbit-ket which will have high memory unaffected - outarray = list(qubits.args[0][:self.t]) - - # Place out in low memory - for i in reversed(list(range(self.t))): - outarray.append((out >> i) & 1) - - return Qubit(*outarray) - -
    -
    [docs]def shor(N): - """This function implements Shor's factoring algorithm on the Integer N - - The algorithm starts by picking a random number (a) and seeing if it is - coprime with N. If it isn't, then the gcd of the two numbers is a factor - and we are done. Otherwise, it begins the period_finding subroutine which - finds the period of a in modulo N arithmetic. This period, if even, can - be used to calculate factors by taking a**(r/2)-1 and a**(r/2)+1. - These values are returned. - """ - a = random.randrange(N - 2) + 2 - if igcd(N, a) != 1: - print("got lucky with rand") - return igcd(N, a) - print("a= ", a) - print("N= ", N) - r = period_find(a, N) - print("r= ", r) - if r % 2 == 1: - print("r is not even, begin again") - shor(N) - answer = (igcd(a**(r/2) - 1, N), igcd(a**(r/2) + 1, N)) - return answer - -
    -def getr(x, y, N): - fraction = continued_fraction(x, y) - # Now convert into r - total = ratioize(fraction, N) - return total - - -def ratioize(list, N): - if list[0] > N: - return S.Zero - if len(list) == 1: - return list[0] - return list[0] + ratioize(list[1:], N) - - -
    [docs]def continued_fraction(x, y): - """This applies the continued fraction expansion to two numbers x/y - - x is the numerator and y is the denominator - - >>> from sympy.physics.quantum.shor import continued_fraction - >>> continued_fraction(3, 8) - [0, 2, 1, 2] - """ - x = int(x) - y = int(y) - temp = x//y - if temp*y == x: - return [temp, ] - - list = continued_fraction(y, x - temp*y) - list.insert(0, temp) - return list - -
    -
    [docs]def period_find(a, N): - """Finds the period of a in modulo N arithmetic - - This is quantum part of Shor's algorithm.It takes two registers, - puts first in superposition of states with Hadamards so: ``|k>|0>`` - with k being all possible choices. It then does a controlled mod and - a QFT to determine the order of a. - """ - epsilon = .5 - #picks out t's such that maintains accuracy within epsilon - t = int(2*math.ceil(log(N, 2))) - # make the first half of register be 0's |000...000> - start = [0 for x in range(t)] - #Put second half into superposition of states so we have |1>x|0> + |2>x|0> + ... |k>x>|0> + ... + |2**n-1>x|0> - factor = 1/sqrt(2**t) - qubits = 0 - for arr in variations(list(range(2)), t, repetition=True): - qbitArray = arr + start - qubits = qubits + Qubit(*qbitArray) - circuit = (factor*qubits).expand() - #Controlled second half of register so that we have: - # |1>x|a**1 %N> + |2>x|a**2 %N> + ... + |k>x|a**k %N >+ ... + |2**n-1=k>x|a**k % n> - circuit = CMod(t, a, N)*circuit - #will measure first half of register giving one of the a**k%N's - circuit = qapply(circuit) - print("controlled Mod'd") - for i in range(t): - circuit = measure_partial_oneshot(circuit, i) - # circuit = measure(i)*circuit - # circuit = qapply(circuit) - print("measured 1") - #Now apply Inverse Quantum Fourier Transform on the second half of the register - circuit = qapply(QFT(t, t*2).decompose()*circuit, floatingPoint=True) - print("QFT'd") - for i in range(t): - circuit = measure_partial_oneshot(circuit, i + t) - # circuit = measure(i+t)*circuit - # circuit = qapply(circuit) - print(circuit) - if isinstance(circuit, Qubit): - register = circuit - elif isinstance(circuit, Mul): - register = circuit.args[-1] - else: - register = circuit.args[-1].args[-1] - - print(register) - n = 1 - answer = 0 - for i in range(len(register)/2): - answer += n*register[i + t] - n = n << 1 - if answer == 0: - raise OrderFindingException( - "Order finder returned 0. Happens with chance %f" % epsilon) - #turn answer into r using continued fractions - g = getr(answer, 2**t, N) - print(g) - return g
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/quantum/spin.html b/dev-py3k/_modules/sympy/physics/quantum/spin.html deleted file mode 100644 index efc5efaaeb4..00000000000 --- a/dev-py3k/_modules/sympy/physics/quantum/spin.html +++ /dev/null @@ -1,2192 +0,0 @@ - - - - - - - - - - sympy.physics.quantum.spin — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.quantum.spin

    -"""Quantum mechanical angular momemtum."""
    -
    -from sympy import (Add, binomial, cos, exp, Expr, factorial, I, Integer, Mul,
    -                   pi, Rational, S, sin, simplify, sqrt, Sum, symbols, sympify,
    -                   Tuple)
    -from sympy.matrices import zeros
    -from sympy.printing.pretty.stringpict import prettyForm, stringPict
    -from sympy.printing.pretty.pretty_symbology import pretty_symbol
    -
    -from sympy.physics.quantum.qexpr import QExpr
    -from sympy.physics.quantum.operator import (HermitianOperator, Operator,
    -                                            UnitaryOperator)
    -from sympy.physics.quantum.state import Bra, Ket, State
    -from sympy.functions.special.tensor_functions import KroneckerDelta
    -from sympy.physics.quantum.constants import hbar
    -from sympy.physics.quantum.hilbert import ComplexSpace, DirectSumHilbertSpace
    -from sympy.physics.quantum.tensorproduct import TensorProduct
    -from sympy.physics.quantum.cg import CG
    -from sympy.physics.quantum.qapply import qapply
    -
    -
    -__all__ = [
    -    'm_values',
    -    'Jplus',
    -    'Jminus',
    -    'Jx',
    -    'Jy',
    -    'Jz',
    -    'J2',
    -    'Rotation',
    -    'WignerD',
    -    'JxKet',
    -    'JxBra',
    -    'JyKet',
    -    'JyBra',
    -    'JzKet',
    -    'JzBra',
    -    'JxKetCoupled',
    -    'JxBraCoupled',
    -    'JyKetCoupled',
    -    'JyBraCoupled',
    -    'JzKetCoupled',
    -    'JzBraCoupled',
    -    'couple',
    -    'uncouple'
    -]
    -
    -
    -def m_values(j):
    -    j = sympify(j)
    -    size = 2*j + 1
    -    if not size.is_Integer or not size > 0:
    -        raise ValueError(
    -            'Only integer or half-integer values allowed for j, got: : %r' % j
    -        )
    -    return size, [j - i for i in range(int(2*j + 1))]
    -
    -
    -#-----------------------------------------------------------------------------
    -# Spin Operators
    -#-----------------------------------------------------------------------------
    -
    -
    -class SpinOpBase(object):
    -    """Base class for spin operators."""
    -
    -    @classmethod
    -    def _eval_hilbert_space(cls, label):
    -        # We consider all j values so our space is infinite.
    -        return ComplexSpace(S.Infinity)
    -
    -    @property
    -    def name(self):
    -        return self.args[0]
    -
    -    def _print_contents(self, printer, *args):
    -        return '%s%s' % (str(self.name), self._coord)
    -
    -    def _print_contents_pretty(self, printer, *args):
    -        a = stringPict(str(self.name))
    -        b = stringPict(self._coord)
    -        return self._print_subscript_pretty(a, b)
    -
    -    def _print_contents_latex(self, printer, *args):
    -        return r'%s_%s' % ((str(self.name), self._coord))
    -
    -    def _represent_base(self, basis, **options):
    -        j = options.get('j', Rational(1, 2))
    -        size, mvals = m_values(j)
    -        result = zeros(size, size)
    -        for p in range(size):
    -            for q in range(size):
    -                me = self.matrix_element(j, mvals[p], j, mvals[q])
    -                result[p, q] = me
    -        return result
    -
    -    def _apply_op(self, ket, orig_basis, **options):
    -        state = ket.rewrite(self.basis)
    -        # If the state has only one term
    -        if isinstance(state, State):
    -            ret = (hbar*state.m) * state
    -        # state is a linear combination of states
    -        elif isinstance(state, Sum):
    -            ret = self._apply_operator_Sum(state, **options)
    -        else:
    -            ret = qapply(self*state)
    -        if ret == self*state:
    -            raise NotImplementedError
    -        return ret.rewrite(orig_basis)
    -
    -    def _apply_operator_JxKet(self, ket, **options):
    -        return self._apply_op(ket, 'Jx', **options)
    -
    -    def _apply_operator_JxKetCoupled(self, ket, **options):
    -        return self._apply_op(ket, 'Jx', **options)
    -
    -    def _apply_operator_JyKet(self, ket, **options):
    -        return self._apply_op(ket, 'Jy', **options)
    -
    -    def _apply_operator_JyKetCoupled(self, ket, **options):
    -        return self._apply_op(ket, 'Jy', **options)
    -
    -    def _apply_operator_JzKet(self, ket, **options):
    -        return self._apply_op(ket, 'Jz', **options)
    -
    -    def _apply_operator_JzKetCoupled(self, ket, **options):
    -        return self._apply_op(ket, 'Jz', **options)
    -
    -    def _apply_operator_TensorProduct(self, tp, **options):
    -        # Uncoupling operator is only easily found for coordinate basis spin operators
    -        # TODO: add methods for uncoupling operators
    -        if not (isinstance(self, JxOp) or isinstance(self, JyOp) or isinstance(self, JzOp)):
    -            raise NotImplementedError
    -        result = []
    -        for n in range(len(tp.args)):
    -            arg = []
    -            arg.extend(tp.args[:n])
    -            arg.append(self._apply_operator(tp.args[n]))
    -            arg.extend(tp.args[n + 1:])
    -            result.append(tp.__class__(*arg))
    -        return Add(*result).expand()
    -
    -    # TODO: move this to qapply_Mul
    -    def _apply_operator_Sum(self, s, **options):
    -        new_func = qapply(self * s.function)
    -        if new_func == self*s.function:
    -            raise NotImplementedError
    -        return Sum(new_func, *s.limits)
    -
    -    def _eval_trace(self, **options):
    -        #TODO: use options to use different j values
    -        #For now eval at default basis
    -
    -        # is it efficient to represent each time
    -        # to do a trace?
    -        return self._represent_default_basis().trace()
    -
    -
    -class JplusOp(SpinOpBase, Operator):
    -    """The J+ operator."""
    -
    -    _coord = '+'
    -
    -    basis = 'Jz'
    -
    -    def _eval_commutator_JminusOp(self, other):
    -        return 2*hbar*JzOp(self.name)
    -
    -    def _apply_operator_JzKet(self, ket, **options):
    -        j = ket.j
    -        m = ket.m
    -        if m.is_Number and j.is_Number:
    -            if m >= j:
    -                return S.Zero
    -        return hbar*sqrt(j*(j + S.One) - m*(m + S.One))*JzKet(j, m + S.One)
    -
    -    def _apply_operator_JzKetCoupled(self, ket, **options):
    -        j = ket.j
    -        m = ket.m
    -        jn = ket.jn
    -        coupling = ket.coupling
    -        if m.is_Number and j.is_Number:
    -            if m >= j:
    -                return S.Zero
    -        return hbar*sqrt(j*(j + S.One) - m*(m + S.One))*JzKetCoupled(j, m + S.One, jn, coupling)
    -
    -    def matrix_element(self, j, m, jp, mp):
    -        result = hbar*sqrt(j*(j + S.One) - mp*(mp + S.One))
    -        result *= KroneckerDelta(m, mp + 1)
    -        result *= KroneckerDelta(j, jp)
    -        return result
    -
    -    def _represent_default_basis(self, **options):
    -        return self._represent_JzOp(None, **options)
    -
    -    def _represent_JzOp(self, basis, **options):
    -        return self._represent_base(basis, **options)
    -
    -    def _eval_rewrite_as_xyz(self, *args):
    -        return JxOp(args[0]) + I*JyOp(args[0])
    -
    -
    -class JminusOp(SpinOpBase, Operator):
    -    """The J- operator."""
    -
    -    _coord = '-'
    -
    -    basis = 'Jz'
    -
    -    def _apply_operator_JzKet(self, ket, **options):
    -        j = ket.j
    -        m = ket.m
    -        if m.is_Number and j.is_Number:
    -            if m <= -j:
    -                return S.Zero
    -        return hbar*sqrt(j*(j + S.One) - m*(m - S.One))*JzKet(j, m - S.One)
    -
    -    def _apply_operator_JzKetCoupled(self, ket, **options):
    -        j = ket.j
    -        m = ket.m
    -        jn = ket.jn
    -        coupling = ket.coupling
    -        if m.is_Number and j.is_Number:
    -            if m <= -j:
    -                return S.Zero
    -        return hbar*sqrt(j*(j + S.One) - m*(m - S.One))*JzKetCoupled(j, m - S.One, jn, coupling)
    -
    -    def matrix_element(self, j, m, jp, mp):
    -        result = hbar*sqrt(j*(j + S.One) - mp*(mp - S.One))
    -        result *= KroneckerDelta(m, mp - 1)
    -        result *= KroneckerDelta(j, jp)
    -        return result
    -
    -    def _represent_default_basis(self, **options):
    -        return self._represent_JzOp(None, **options)
    -
    -    def _represent_JzOp(self, basis, **options):
    -        return self._represent_base(basis, **options)
    -
    -    def _eval_rewrite_as_xyz(self, *args):
    -        return JxOp(args[0]) - I*JyOp(args[0])
    -
    -
    -class JxOp(SpinOpBase, HermitianOperator):
    -    """The Jx operator."""
    -
    -    _coord = 'x'
    -
    -    basis = 'Jx'
    -
    -    def _eval_commutator_JyOp(self, other):
    -        return I*hbar*JzOp(self.name)
    -
    -    def _eval_commutator_JzOp(self, other):
    -        return -I*hbar*JyOp(self.name)
    -
    -    def _apply_operator_JzKet(self, ket, **options):
    -        jp = JplusOp(self.name)._apply_operator_JzKet(ket, **options)
    -        jm = JminusOp(self.name)._apply_operator_JzKet(ket, **options)
    -        return (jp + jm)/Integer(2)
    -
    -    def _apply_operator_JzKetCoupled(self, ket, **options):
    -        jp = JplusOp(self.name)._apply_operator_JzKetCoupled(ket, **options)
    -        jm = JminusOp(self.name)._apply_operator_JzKetCoupled(ket, **options)
    -        return (jp + jm)/Integer(2)
    -
    -    def _represent_default_basis(self, **options):
    -        return self._represent_JzOp(None, **options)
    -
    -    def _represent_JzOp(self, basis, **options):
    -        jp = JplusOp(self.name)._represent_JzOp(basis, **options)
    -        jm = JminusOp(self.name)._represent_JzOp(basis, **options)
    -        return (jp + jm)/Integer(2)
    -
    -    def _eval_rewrite_as_plusminus(self, *args):
    -        return (JplusOp(args[0]) + JminusOp(args[0]))/2
    -
    -
    -class JyOp(SpinOpBase, HermitianOperator):
    -    """The Jy operator."""
    -
    -    _coord = 'y'
    -
    -    basis = 'Jy'
    -
    -    def _eval_commutator_JzOp(self, other):
    -        return I*hbar*JxOp(self.name)
    -
    -    def _eval_commutator_JxOp(self, other):
    -        return -I*hbar*J2Op(self.name)
    -
    -    def _apply_operator_JzKet(self, ket, **options):
    -        jp = JplusOp(self.name)._apply_operator_JzKet(ket, **options)
    -        jm = JminusOp(self.name)._apply_operator_JzKet(ket, **options)
    -        return (jp - jm)/(Integer(2)*I)
    -
    -    def _apply_operator_JzKetCoupled(self, ket, **options):
    -        jp = JplusOp(self.name)._apply_operator_JzKetCoupled(ket, **options)
    -        jm = JminusOp(self.name)._apply_operator_JzKetCoupled(ket, **options)
    -        return (jp - jm)/(Integer(2)*I)
    -
    -    def _represent_default_basis(self, **options):
    -        return self._represent_JzOp(None, **options)
    -
    -    def _represent_JzOp(self, basis, **options):
    -        jp = JplusOp(self.name)._represent_JzOp(basis, **options)
    -        jm = JminusOp(self.name)._represent_JzOp(basis, **options)
    -        return (jp - jm)/(Integer(2)*I)
    -
    -    def _eval_rewrite_as_plusminus(self, *args):
    -        return (JplusOp(args[0]) - JminusOp(args[0]))/(2*I)
    -
    -
    -class JzOp(SpinOpBase, HermitianOperator):
    -    """The Jz operator."""
    -
    -    _coord = 'z'
    -
    -    basis = 'Jz'
    -
    -    def _eval_commutator_JxOp(self, other):
    -        return I*hbar*JyOp(self.name)
    -
    -    def _eval_commutator_JyOp(self, other):
    -        return -I*hbar*JxOp(self.name)
    -
    -    def _eval_commutator_JplusOp(self, other):
    -        return hbar*JplusOp(self.name)
    -
    -    def _eval_commutator_JminusOp(self, other):
    -        return -hbar*JminusOp(self.name)
    -
    -    def matrix_element(self, j, m, jp, mp):
    -        result = hbar*mp
    -        result *= KroneckerDelta(m, mp)
    -        result *= KroneckerDelta(j, jp)
    -        return result
    -
    -    def _represent_default_basis(self, **options):
    -        return self._represent_JzOp(None, **options)
    -
    -    def _represent_JzOp(self, basis, **options):
    -        return self._represent_base(basis, **options)
    -
    -
    -class J2Op(SpinOpBase, HermitianOperator):
    -    """The J^2 operator."""
    -
    -    _coord = '2'
    -
    -    def _eval_commutator_JxOp(self, other):
    -        return S.Zero
    -
    -    def _eval_commutator_JyOp(self, other):
    -        return S.Zero
    -
    -    def _eval_commutator_JzOp(self, other):
    -        return S.Zero
    -
    -    def _eval_commutator_JplusOp(self, other):
    -        return S.Zero
    -
    -    def _eval_commutator_JminusOp(self, other):
    -        return S.Zero
    -
    -    def _apply_operator_JxKet(self, ket, **options):
    -        j = ket.j
    -        return hbar**2*j*(j + 1)*ket
    -
    -    def _apply_operator_JxKetCoupled(self, ket, **options):
    -        j = ket.j
    -        return hbar**2*j*(j + 1)*ket
    -
    -    def _apply_operator_JyKet(self, ket, **options):
    -        j = ket.j
    -        return hbar**2*j*(j + 1)*ket
    -
    -    def _apply_operator_JyKetCoupled(self, ket, **options):
    -        j = ket.j
    -        return hbar**2*j*(j + 1)*ket
    -
    -    def _apply_operator_JzKet(self, ket, **options):
    -        j = ket.j
    -        return hbar**2*j*(j + 1)*ket
    -
    -    def _apply_operator_JzKetCoupled(self, ket, **options):
    -        j = ket.j
    -        return hbar**2*j*(j + 1)*ket
    -
    -    def matrix_element(self, j, m, jp, mp):
    -        result = (hbar**2)*j*(j + 1)
    -        result *= KroneckerDelta(m, mp)
    -        result *= KroneckerDelta(j, jp)
    -        return result
    -
    -    def _represent_default_basis(self, **options):
    -        return self._represent_JzOp(None, **options)
    -
    -    def _represent_JzOp(self, basis, **options):
    -        return self._represent_base(basis, **options)
    -
    -    def _print_contents_pretty(self, printer, *args):
    -        a = prettyForm(str(self.name))
    -        b = prettyForm('2')
    -        return a**b
    -
    -    def _print_contents_latex(self, printer, *args):
    -        return r'%s^2' % str(self.name)
    -
    -    def _eval_rewrite_as_xyz(self, *args):
    -        return JxOp(args[0])**2 + JyOp(args[0])**2 + JzOp(args[0])**2
    -
    -    def _eval_rewrite_as_plusminus(self, *args):
    -        a = args[0]
    -        return JzOp(a)**2 + \
    -            Rational(1, 2)*(JplusOp(a)*JminusOp(a) + JminusOp(a)*JplusOp(a))
    -
    -
    -
    [docs]class Rotation(UnitaryOperator): - """Wigner D operator in terms of Euler angles. - - Defines the rotation operator in terms of the Euler angles defined by - the z-y-z convention for a passive transformation. That is the coordinate - axes are rotated first about the z-axis, giving the new x'-y'-z' axes. Then - this new coordinate system is rotated about the new y'-axis, giving new - x''-y''-z'' axes. Then this new coordinate system is rotated about the - z''-axis. Conventions follow those laid out in [1]_. - - Parameters - ========== - - alpha : Number, Symbol - First Euler Angle - beta : Number, Symbol - Second Euler angle - gamma : Number, Symbol - Third Euler angle - - Examples - ======== - - A simple example rotation operator: - - >>> from sympy import pi - >>> from sympy.physics.quantum.spin import Rotation - >>> Rotation(pi, 0, pi/2) - R(pi,0,pi/2) - - With symbolic Euler angles and calculating the inverse rotation operator: - - >>> from sympy import symbols - >>> a, b, c = symbols('a b c') - >>> Rotation(a, b, c) - R(a,b,c) - >>> Rotation(a, b, c).inverse() - R(-c,-b,-a) - - See Also - ======== - - WignerD: Symbolic Wigner-D function - D: Wigner-D function - d: Wigner small-d function - - References - ========== - - .. [1] Varshalovich, D A, Quantum Theory of Angular Momentum. 1988. - """ - - @classmethod - def _eval_args(cls, args): - args = QExpr._eval_args(args) - if len(args) != 3: - raise ValueError('3 Euler angles required, got: %r' % args) - return args - - @classmethod - def _eval_hilbert_space(cls, label): - # We consider all j values so our space is infinite. - return ComplexSpace(S.Infinity) - - @property - def alpha(self): - return self.label[0] - - @property - def beta(self): - return self.label[1] - - @property - def gamma(self): - return self.label[2] - - def _print_operator_name(self, printer, *args): - return 'R' - - def _print_operator_name_pretty(self, printer, *args): - if printer._use_unicode: - return prettyForm("\u211B" + " ") - else: - return prettyForm("R ") - - def _print_operator_name_latex(self, printer, *args): - return r'\mathcal{R}' - - def _eval_inverse(self): - return Rotation(-self.gamma, -self.beta, -self.alpha) - - @classmethod -
    [docs] def D(cls, j, m, mp, alpha, beta, gamma): - """Wigner D-function. - - Returns an instance of the WignerD class corresponding to the Wigner-D - function specified by the parameters. - - Parameters - =========== - - j : Number - Total angular momentum - m : Number - Eigenvalue of angular momentum along axis after rotation - mp : Number - Eigenvalue of angular momentum along rotated axis - alpha : Number, Symbol - First Euler angle of rotation - beta : Number, Symbol - Second Euler angle of rotation - gamma : Number, Symbol - Third Euler angle of rotation - - Examples - ======== - - Return the Wigner-D matrix element for a defined rotation, both - numerical and symbolic: - - >>> from sympy.physics.quantum.spin import Rotation - >>> from sympy import pi, symbols - >>> alpha, beta, gamma = symbols('alpha beta gamma') - >>> Rotation.D(1, 1, 0,pi, pi/2,-pi) - WignerD(1, 1, 0, pi, pi/2, -pi) - - See Also - ======== - - WignerD: Symbolic Wigner-D function - - """ - return WignerD(j, m, mp, alpha, beta, gamma) -
    - @classmethod -
    [docs] def d(cls, j, m, mp, beta): - """Wigner small-d function. - - Returns an instance of the WignerD class corresponding to the Wigner-D - function specified by the parameters with the alpha and gamma angles - given as 0. - - Parameters - =========== - - j : Number - Total angular momentum - m : Number - Eigenvalue of angular momentum along axis after rotation - mp : Number - Eigenvalue of angular momentum along rotated axis - beta : Number, Symbol - Second Euler angle of rotation - - Examples - ======== - - Return the Wigner-D matrix element for a defined rotation, both - numerical and symbolic: - - >>> from sympy.physics.quantum.spin import Rotation - >>> from sympy import pi, symbols - >>> beta = symbols('beta') - >>> Rotation.d(1, 1, 0, pi/2) - WignerD(1, 1, 0, 0, pi/2, 0) - - See Also - ======== - - WignerD: Symbolic Wigner-D function - - """ - return WignerD(j, m, mp, 0, beta, 0) -
    - def matrix_element(self, j, m, jp, mp): - result = self.__class__.D( - jp, m, mp, self.alpha, self.beta, self.gamma - ) - result *= KroneckerDelta(j, jp) - return result - - def _represent_base(self, basis, **options): - j = sympify(options.get('j', Rational(1, 2))) - # TODO: move evaluation up to represent function/implement elsewhere - evaluate = sympify(options.get('doit')) - size, mvals = m_values(j) - result = zeros(size, size) - for p in range(size): - for q in range(size): - me = self.matrix_element(j, mvals[p], j, mvals[q]) - if evaluate: - result[p, q] = me.doit() - else: - result[p, q] = me - return result - - def _represent_default_basis(self, **options): - return self._represent_JzOp(None, **options) - - def _represent_JzOp(self, basis, **options): - return self._represent_base(basis, **options) - -
    -
    [docs]class WignerD(Expr): - """Wigner-D function - - The Wigner D-function gives the matrix elements of the rotation - operator in the jm-representation. For the Euler angles `\\alpha`, - `\\beta`, `\gamma`, the D-function is defined such that: - - .. math :: - <j,m| \mathcal{R}(\\alpha, \\beta, \gamma ) |j',m'> = \delta_{jj'} D(j, m, m', \\alpha, \\beta, \gamma) - - Where the rotation operator is as defined by the Rotation class [1]_. - - The Wigner D-function defined in this way gives: - - .. math :: - D(j, m, m', \\alpha, \\beta, \gamma) = e^{-i m \\alpha} d(j, m, m', \\beta) e^{-i m' \gamma} - - Where d is the Wigner small-d function, which is given by Rotation.d. - - The Wigner small-d function gives the component of the Wigner - D-function that is determined by the second Euler angle. That is the - Wigner D-function is: - - .. math :: - D(j, m, m', \\alpha, \\beta, \gamma) = e^{-i m \\alpha} d(j, m, m', \\beta) e^{-i m' \gamma} - - Where d is the small-d function. The Wigner D-function is given by - Rotation.D. - - Note that to evaluate the D-function, the j, m and mp parameters must - be integer or half integer numbers. - - Parameters - ========== - - j : Number - Total angular momentum - m : Number - Eigenvalue of angular momentum along axis after rotation - mp : Number - Eigenvalue of angular momentum along rotated axis - alpha : Number, Symbol - First Euler angle of rotation - beta : Number, Symbol - Second Euler angle of rotation - gamma : Number, Symbol - Third Euler angle of rotation - - Examples - ======== - - Evaluate the Wigner-D matrix elements of a simple rotation: - - >>> from sympy.physics.quantum.spin import Rotation - >>> from sympy import pi - >>> rot = Rotation.D(1, 1, 0, pi, pi/2, 0) - >>> rot - WignerD(1, 1, 0, pi, pi/2, 0) - >>> rot.doit() - sqrt(2)/2 - - Evaluate the Wigner-d matrix elements of a simple rotation - - >>> rot = Rotation.d(1, 1, 0, pi/2) - >>> rot - WignerD(1, 1, 0, 0, pi/2, 0) - >>> rot.doit() - -sqrt(2)/2 - - See Also - ======== - - Rotation: Rotation operator - - References - ========== - - .. [1] Varshalovich, D A, Quantum Theory of Angular Momentum. 1988. - """ - - is_commutative = True - - def __new__(cls, *args, **hints): - if not len(args) == 6: - raise ValueError('6 parameters expected, got %s' % args) - args = sympify(args) - evaluate = hints.get('evaluate', False) - if evaluate: - return Expr.__new__(cls, *args)._eval_wignerd() - return Expr.__new__(cls, *args) - - @property - def j(self): - return self.args[0] - - @property - def m(self): - return self.args[1] - - @property - def mp(self): - return self.args[2] - - @property - def alpha(self): - return self.args[3] - - @property - def beta(self): - return self.args[4] - - @property - def gamma(self): - return self.args[5] - - def _latex(self, printer, *args): - if self.alpha == 0 and self.gamma == 0: - return r'd^{%s}_{%s,%s}\left(%s\right)' % \ - ( - printer._print(self.j), printer._print( - self.m), printer._print(self.mp), - printer._print(self.beta) ) - return r'D^{%s}_{%s,%s}\left(%s,%s,%s\right)' % \ - ( - printer._print( - self.j), printer._print(self.m), printer._print(self.mp), - printer._print(self.alpha), printer._print(self.beta), printer._print(self.gamma) ) - - def _pretty(self, printer, *args): - top = printer._print(self.j) - - bot = printer._print(self.m) - bot = prettyForm(*bot.right(',')) - bot = prettyForm(*bot.right(printer._print(self.mp))) - - pad = max(top.width(), bot.width()) - top = prettyForm(*top.left(' ')) - bot = prettyForm(*bot.left(' ')) - if pad > top.width(): - top = prettyForm(*top.right(' ' * (pad - top.width()))) - if pad > bot.width(): - bot = prettyForm(*bot.right(' ' * (pad - bot.width()))) - if self.alpha == 0 and self.gamma == 0: - args = printer._print(self.beta) - s = stringPict('d' + ' '*pad) - else: - args = printer._print(self.alpha) - args = prettyForm(*args.right(',')) - args = prettyForm(*args.right(printer._print(self.beta))) - args = prettyForm(*args.right(',')) - args = prettyForm(*args.right(printer._print(self.gamma))) - - s = stringPict('D' + ' '*pad) - - args = prettyForm(*args.parens()) - s = prettyForm(*s.above(top)) - s = prettyForm(*s.below(bot)) - s = prettyForm(*s.right(args)) - return s - - def doit(self, **hints): - hints['evaluate'] = True - return WignerD(*self.args, **hints) - - def _eval_wignerd(self): - j = sympify(self.j) - m = sympify(self.m) - mp = sympify(self.mp) - alpha = sympify(self.alpha) - beta = sympify(self.beta) - gamma = sympify(self.gamma) - if not j.is_number: - raise ValueError( - "j parameter must be numerical to evaluate, got %s", j) - r = 0 - if beta == pi/2: - # Varshalovich Equation (5), Section 4.16, page 113, setting - # alpha=gamma=0. - for k in range(2*j + 1): - if k > j + mp or k > j - m or k < mp - m: - continue - r += (-S(1))**k * binomial(j + mp, k) * binomial(j - mp, k + m - mp) - r *= (-S(1))**(m - mp) / 2**j * sqrt(factorial(j + m) * - factorial(j - m) / (factorial(j + mp) * factorial(j - mp))) - else: - # Varshalovich Equation(5), Section 4.7.2, page 87, where we set - # beta1=beta2=pi/2, and we get alpha=gamma=pi/2 and beta=phi+pi, - # then we use the Eq. (1), Section 4.4. page 79, to simplify: - # d(j, m, mp, beta+pi) = (-1)**(j-mp) * d(j, m, -mp, beta) - # This happens to be almost the same as in Eq.(10), Section 4.16, - # except that we need to substitute -mp for mp. - size, mvals = m_values(j) - for mpp in mvals: - r += Rotation.d(j, m, mpp, pi/2).doit() * (cos(-mpp*beta) + I*sin(-mpp*beta)) * \ - Rotation.d(j, mpp, -mp, pi/2).doit() - # Empirical normalization factor so results match Varshalovich - # Tables 4.3-4.12 - # Note that this exact normalization does not follow from the - # above equations - r = r * I**(2*j - m - mp) * (-1)**(2*m) - # Finally, simplify the whole expression - r = simplify(r) - r *= exp(-I*m*alpha)*exp(-I*mp*gamma) - return r - -
    -Jx = JxOp('J') -Jy = JyOp('J') -Jz = JzOp('J') -J2 = J2Op('J') -Jplus = JplusOp('J') -Jminus = JminusOp('J') - - -#----------------------------------------------------------------------------- -# Spin States -#----------------------------------------------------------------------------- - - -class SpinState(State): - """Base class for angular momentum states.""" - - _label_separator = ',' - - def __new__(cls, j, m): - j = sympify(j) - m = sympify(m) - if j.is_number: - if 2*j != int(2*j): - raise ValueError( - 'j must be integer or half-integer, got: %s' % j) - if j < 0: - raise ValueError('j must be >= 0, got: %s' % j) - if m.is_number: - if 2*m != int(2*m): - raise ValueError( - 'm must be integer or half-integer, got: %s' % m) - if j.is_number and m.is_number: - if abs(m) > j: - raise ValueError('Allowed values for m are -j <= m <= j, got j, m: %s, %s' % (j, m)) - if int(j - m) != j - m: - raise ValueError('Both j and m must be integer or half-integer, got j, m: %s, %s' % (j, m)) - return State.__new__(cls, j, m) - - @property - def j(self): - return self.label[0] - - @property - def m(self): - return self.label[1] - - @classmethod - def _eval_hilbert_space(cls, label): - return ComplexSpace(2*label[0] + 1) - - def _represent_base(self, **options): - j = self.j - m = self.m - alpha = sympify(options.get('alpha', 0)) - beta = sympify(options.get('beta', 0)) - gamma = sympify(options.get('gamma', 0)) - size, mvals = m_values(j) - result = zeros(size, 1) - # TODO: Use KroneckerDelta if all Euler angles == 0 - # breaks finding angles on L930 - for p, mval in enumerate(mvals): - if m.is_number: - result[p, 0] = Rotation.D( - self.j, mval, self.m, alpha, beta, gamma).doit() - else: - result[p, 0] = Rotation.D(self.j, mval, - self.m, alpha, beta, gamma) - return result - - def _eval_rewrite_as_Jx(self, *args, **options): - if isinstance(self, Bra): - return self._rewrite_basis(Jx, JxBra, **options) - return self._rewrite_basis(Jx, JxKet, **options) - - def _eval_rewrite_as_Jy(self, *args, **options): - if isinstance(self, Bra): - return self._rewrite_basis(Jy, JyBra, **options) - return self._rewrite_basis(Jy, JyKet, **options) - - def _eval_rewrite_as_Jz(self, *args, **options): - if isinstance(self, Bra): - return self._rewrite_basis(Jz, JzBra, **options) - return self._rewrite_basis(Jz, JzKet, **options) - - def _rewrite_basis(self, basis, evect, **options): - from sympy.physics.quantum.represent import represent - j = self.j - args = self.args[2:] - if j.is_number: - if isinstance(self, CoupledSpinState): - if j == int(j): - start = j**2 - else: - start = (2*j - 1)*(2*j + 1)/4 - else: - start = 0 - vect = represent(self, basis=basis, **options) - result = Add( - *[vect[start + i] * evect(j, j - i, *args) for i in range(2*j + 1)]) - if isinstance(self, CoupledSpinState) and options.get('coupled') is False: - return uncouple(result) - return result - else: - i = 0 - mi = symbols('mi') - # make sure not to introduce a symbol already in the state - while self.subs(mi, 0) != self: - i += 1 - mi = symbols('mi%d' % i) - break - # TODO: better way to get angles of rotation - if isinstance(self, CoupledSpinState): - test_args = (0, mi, (0, 0)) - else: - test_args = (0, mi) - if isinstance(self, Ket): - angles = represent( - self.__class__(*test_args), basis=basis)[0].args[3:6] - else: - angles = represent(self.__class__( - *test_args), basis=basis)[0].args[0].args[3:6] - if angles == (0, 0, 0): - return self - else: - state = evect(j, mi, *args) - lt = Rotation.D(j, mi, self.m, *angles) - return Sum(lt * state, (mi, -j, j)) - - def _eval_innerproduct_JxBra(self, bra, **hints): - result = KroneckerDelta(self.j, bra.j) - if bra.dual_class() is not self.__class__: - result *= self._represent_JxOp(None)[bra.j - bra.m] - else: - result *= KroneckerDelta( - self.j, bra.j) * KroneckerDelta(self.m, bra.m) - return result - - def _eval_innerproduct_JyBra(self, bra, **hints): - result = KroneckerDelta(self.j, bra.j) - if bra.dual_class() is not self.__class__: - result *= self._represent_JyOp(None)[bra.j - bra.m] - else: - result *= KroneckerDelta( - self.j, bra.j) * KroneckerDelta(self.m, bra.m) - return result - - def _eval_innerproduct_JzBra(self, bra, **hints): - result = KroneckerDelta(self.j, bra.j) - if bra.dual_class() is not self.__class__: - result *= self._represent_JzOp(None)[bra.j - bra.m] - else: - result *= KroneckerDelta( - self.j, bra.j) * KroneckerDelta(self.m, bra.m) - return result - - def _eval_trace(self, bra, **hints): - - # One way to implement this method is to assume the basis set k is - # passed. - # Then we can apply the discrete form of Trace formula here - # Tr(|i><j| ) = \Sum_k <k|i><j|k> - #then we do qapply() on each each inner product and sum over them. - - # OR - - # Inner product of |i><j| = Trace(Outer Product). - # we could just use this unless there are cases when this is not true - - return (bra*self).doit() - - -
    [docs]class JxKet(SpinState, Ket): - """Eigenket of Jx. - - See JzKet for the usage of spin eigenstates. - - See Also - ======== - - JzKet: Usage of spin states - - """ - - @classmethod - def dual_class(self): - return JxBra - - @classmethod - def coupled_class(self): - return JxKetCoupled - - def _represent_default_basis(self, **options): - return self._represent_JxOp(None, **options) - - def _represent_JxOp(self, basis, **options): - return self._represent_base(**options) - - def _represent_JyOp(self, basis, **options): - return self._represent_base(alpha=3*pi/2, **options) - - def _represent_JzOp(self, basis, **options): - return self._represent_base(beta=pi/2, **options) - -
    -
    [docs]class JxBra(SpinState, Bra): - """Eigenbra of Jx. - - See JzKet for the usage of spin eigenstates. - - See Also - ======== - - JzKet: Usage of spin states - - """ - - @classmethod - def dual_class(self): - return JxKet - - @classmethod - def coupled_class(self): - return JxBraCoupled - -
    -
    [docs]class JyKet(SpinState, Ket): - """Eigenket of Jy. - - See JzKet for the usage of spin eigenstates. - - See Also - ======== - - JzKet: Usage of spin states - - """ - - @classmethod - def dual_class(self): - return JyBra - - @classmethod - def coupled_class(self): - return JyKetCoupled - - def _represent_default_basis(self, **options): - return self._represent_JyOp(None, **options) - - def _represent_JxOp(self, basis, **options): - return self._represent_base(gamma=pi/2, **options) - - def _represent_JyOp(self, basis, **options): - return self._represent_base(**options) - - def _represent_JzOp(self, basis, **options): - return self._represent_base(alpha=3*pi/2, beta=-pi/2, gamma=pi/2, **options) - -
    -
    [docs]class JyBra(SpinState, Bra): - """Eigenbra of Jy. - - See JzKet for the usage of spin eigenstates. - - See Also - ======== - - JzKet: Usage of spin states - - """ - - @classmethod - def dual_class(self): - return JyKet - - @classmethod - def coupled_class(self): - return JyBraCoupled - -
    -
    [docs]class JzKet(SpinState, Ket): - """Eigenket of Jz. - - Spin state which is an eigenstate of the Jz operator. Uncoupled states, - that is states representing the interaction of multiple separate spin - states, are defined as a tensor product of states. - - Parameters - ========== - - j : Number, Symbol - Total spin angular momentum - m : Number, Symbol - Eigenvalue of the Jz spin operator - - Examples - ======== - - *Normal States:* - - Defining simple spin states, both numerical and symbolic: - - >>> from sympy.physics.quantum.spin import JzKet, JxKet - >>> from sympy import symbols - >>> JzKet(1, 0) - |1,0> - >>> j, m = symbols('j m') - >>> JzKet(j, m) - |j,m> - - Rewriting the JzKet in terms of eigenkets of the Jx operator: - Note: that the resulting eigenstates are JxKet's - - >>> JzKet(1,1).rewrite("Jx") - |1,-1>/2 - sqrt(2)*|1,0>/2 + |1,1>/2 - - Get the vector representation of a state in terms of the basis elements - of the Jx operator: - - >>> from sympy.physics.quantum.represent import represent - >>> from sympy.physics.quantum.spin import Jx, Jz - >>> represent(JzKet(1,-1), basis=Jx) - [ 1/2] - [sqrt(2)/2] - [ 1/2] - - Apply innerproducts between states: - - >>> from sympy.physics.quantum.innerproduct import InnerProduct - >>> from sympy.physics.quantum.spin import JxBra - >>> i = InnerProduct(JxBra(1,1), JzKet(1,1)) - >>> i - <1,1|1,1> - >>> i.doit() - 1/2 - - *Uncoupled States:* - - Define an uncoupled state as a TensorProduct between two Jz eigenkets: - - >>> from sympy.physics.quantum.tensorproduct import TensorProduct - >>> j1,m1,j2,m2 = symbols('j1 m1 j2 m2') - >>> TensorProduct(JzKet(1,0), JzKet(1,1)) - |1,0>x|1,1> - >>> TensorProduct(JzKet(j1,m1), JzKet(j2,m2)) - |j1,m1>x|j2,m2> - - A TensorProduct can be rewritten, in which case the eigenstates that make - up the tensor product is rewritten to the new basis: - - >>> TensorProduct(JzKet(1,1),JxKet(1,1)).rewrite('Jz') - |1,1>x|1,-1>/2 + sqrt(2)*|1,1>x|1,0>/2 + |1,1>x|1,1>/2 - - The represent method for TensorProduct's gives the vector representation of - the state. Note that the state in the product basis is the equivalent of the - tensor product of the vector representation of the component eigenstates: - - >>> represent(TensorProduct(JzKet(1,0),JzKet(1,1))) - [0] - [0] - [0] - [1] - [0] - [0] - [0] - [0] - [0] - >>> represent(TensorProduct(JzKet(1,1),JxKet(1,1)), basis=Jz) - [ 1/2] - [sqrt(2)/2] - [ 1/2] - [ 0] - [ 0] - [ 0] - [ 0] - [ 0] - [ 0] - - See Also - ======== - - JzKetCoupled: Coupled eigenstates - TensorProduct: Used to specify uncoupled states - uncouple: Uncouples states given coupling parameters - couple: Couples uncoupled states - - """ - - @classmethod - def dual_class(self): - return JzBra - - @classmethod - def coupled_class(self): - return JzKetCoupled - - def _represent_default_basis(self, **options): - return self._represent_JzOp(None, **options) - - def _represent_JxOp(self, basis, **options): - return self._represent_base(beta=3*pi/2, **options) - - def _represent_JyOp(self, basis, **options): - return self._represent_base(alpha=3*pi/2, beta=pi/2, gamma=pi/2, **options) - - def _represent_JzOp(self, basis, **options): - return self._represent_base(**options) - -
    -
    [docs]class JzBra(SpinState, Bra): - """Eigenbra of Jz. - - See the JzKet for the usage of spin eigenstates. - - See Also - ======== - - JzKet: Usage of spin states - - """ - - @classmethod - def dual_class(self): - return JzKet - - @classmethod - def coupled_class(self): - return JzBraCoupled - - -# Method used primarily to create coupled_n and coupled_jn by __new__ in -# CoupledSpinState -# This same method is also used by the uncouple method, and is separated from -# the CoupledSpinState class to maintain consistency in defining coupling
    -def _build_coupled(jcoupling, length): - n_list = [ [n + 1] for n in range(length) ] - coupled_jn = [] - coupled_n = [] - for n1, n2, j_new in jcoupling: - coupled_jn.append(j_new) - coupled_n.append( (n_list[n1 - 1], n_list[n2 - 1]) ) - n_sort = sorted(n_list[n1 - 1] + n_list[n2 - 1]) - n_list[n_sort[0] - 1] = n_sort - return coupled_n, coupled_jn - - -class CoupledSpinState(SpinState): - """Base class for coupled angular momentum states.""" - - def __new__(cls, j, m, jn, *jcoupling): - # Check j and m values using SpinState - SpinState(j, m) - # Build and check coupling scheme from arguments - if len(jcoupling) == 0: - # Use default coupling scheme - jcoupling = [] - for n in range(2, len(jn)): - jcoupling.append( (1, n, Add(*[jn[i] for i in range(n)])) ) - jcoupling.append( (1, len(jn), j) ) - elif len(jcoupling) == 1: - # Use specified coupling scheme - jcoupling = jcoupling[0] - else: - raise TypeError("CoupledSpinState only takes 3 or 4 arguments, got: %s" % (len(jcoupling) + 3) ) - # Check arguments have correct form - if not (isinstance(jn, list) or isinstance(jn, tuple) or isinstance(jn, Tuple)): - raise TypeError('jn must be Tuple, list or tuple, got %s' % - jn.__class__.__name__) - if not (isinstance(jcoupling, list) or isinstance(jcoupling, tuple) or isinstance(jcoupling, Tuple)): - raise TypeError('jcoupling must be Tuple, list or tuple, got %s' % - jcoupling.__class__.__name__) - if not all(isinstance(term, list) or isinstance(term, tuple) or isinstance(term, Tuple) for term in jcoupling): - raise TypeError( - 'All elements of jcoupling must be list, tuple or Tuple') - if not len(jn) - 1 == len(jcoupling): - raise ValueError('jcoupling must have length of %d, got %d' % - (len(jn) - 1, len(jcoupling))) - if not all(len(x) == 3 for x in jcoupling): - raise ValueError('All elements of jcoupling must have length 3') - # Build sympified args - j = sympify(j) - m = sympify(m) - jn = Tuple( *[sympify(ji) for ji in jn] ) - jcoupling = Tuple( *[Tuple(sympify( - n1), sympify(n2), sympify(ji)) for (n1, n2, ji) in jcoupling] ) - # Check values in coupling scheme give physical state - if any(2*ji != int(2*ji) for ji in jn if ji.is_number): - raise ValueError('All elements of jn must be integer or half-integer, got: %s' % jn) - if any(n1 != int(n1) or n2 != int(n2) for (n1, n2, _) in jcoupling): - raise ValueError('Indicies in jcoupling must be integers') - if any(n1 < 1 or n2 < 1 or n1 > len(jn) or n2 > len(jn) for (n1, n2, _) in jcoupling): - raise ValueError('Indicies must be between 1 and the number of coupled spin spaces') - if any(2*ji != int(2*ji) for (_, _, ji) in jcoupling if ji.is_number): - raise ValueError('All coupled j values in coupling scheme must be integer or half-integer') - coupled_n, coupled_jn = _build_coupled(jcoupling, len(jn)) - jvals = list(jn) - for n, (n1, n2) in enumerate(coupled_n): - j1 = jvals[min(n1) - 1] - j2 = jvals[min(n2) - 1] - j3 = coupled_jn[n] - if sympify(j1).is_number and sympify(j2).is_number and sympify(j3).is_number: - if j1 + j2 < j3: - raise ValueError('All couplings must have j1+j2 >= j3, ' - 'in coupling number %d got j1,j2,j3: %d,%d,%d' % (n + 1, j1, j2, j3)) - if abs(j1 - j2) > j3: - raise ValueError("All couplings must have |j1+j2| <= j3, " - "in coupling number %d got j1,j2,j3: %d,%d,%d" % (n + 1, j1, j2, j3)) - if int(j1 + j2) == j1 + j2: - pass - jvals[min(n1 + n2) - 1] = j3 - if len(jcoupling) > 0 and jcoupling[-1][2] != j: - raise ValueError('Last j value coupled together must be the final j of the state') - # Return state - return State.__new__(cls, j, m, jn, jcoupling) - - def _print_label(self, printer, *args): - label = [printer._print(self.j), printer._print(self.m)] - # After 2.5 is dropped: - #for i, ji in enumerate(self.jn, start=1): - # label.append('j%d=%s' % (i, ji) ) - for i, ji in enumerate(self.jn): - label.append('j%d=%s' % ( - i + 1, printer._print(ji) - )) - for jn, (n1, n2) in zip(self.coupled_jn[:-1], self.coupled_n[:-1]): - label.append('j(%s)=%s' % ( - ','.join(str(i) for i in sorted(n1 + n2)), printer._print(jn) - )) - return ','.join(label) - - def _print_label_pretty(self, printer, *args): - label = [self.j, self.m] - # After 2.5 is dropped: - #for i, ji in enumerate(self.jn, start=1): - # n = '%d' % (i) - for i, ji in enumerate(self.jn): - symb = 'j%d' % (i + 1) - symb = pretty_symbol(symb) - symb = prettyForm(symb + '=') - item = prettyForm(*symb.right(printer._print(ji))) - label.append(item) - for jn, (n1, n2) in zip(self.coupled_jn[:-1], self.coupled_n[:-1]): - n = ','.join(pretty_symbol("j%d" % i)[-1] for i in sorted(n1 + n2)) - symb = prettyForm('j' + n + '=') - item = prettyForm(*symb.right(printer._print(jn))) - label.append(item) - return self._print_sequence_pretty( - label, self._label_separator, printer, *args - ) - - def _print_label_latex(self, printer, *args): - label = [self.j, self.m] - # After 2.5 dropped - #for i, ji in enumerate(self.jn, start=1): - # label.append('j_{%d}=%s' % (i, printer._print(ji)) ) - for i, ji in enumerate(self.jn): - label.append('j_{%d}=%s' % (i + 1, printer._print(ji)) ) - for jn, (n1, n2) in zip(self.coupled_jn[:-1], self.coupled_n[:-1]): - n = ','.join(str(i) for i in sorted(n1 + n2)) - label.append('j_{%s}=%s' % (n, printer._print(jn)) ) - return self._print_sequence( - label, self._label_separator, printer, *args - ) - - @property - def jn(self): - return self.label[2] - - @property - def coupling(self): - return self.label[3] - - @property - def coupled_jn(self): - return _build_coupled(self.label[3], len(self.label[2]))[1] - - @property - def coupled_n(self): - return _build_coupled(self.label[3], len(self.label[2]))[0] - - @classmethod - def _eval_hilbert_space(cls, label): - j = Add(*label[2]) - if j.is_number: - return DirectSumHilbertSpace(*[ ComplexSpace(x) for x in range(int(2*j + 1), 0, -2) ]) - else: - # TODO: Need hilbert space fix, see issue 2633 - # Desired behavior: - #ji = symbols('ji') - #ret = Sum(ComplexSpace(2*ji + 1), (ji, 0, j)) - # Temporary fix: - return ComplexSpace(2*j + 1) - - def _represent_coupled_base(self, **options): - evect = self.uncoupled_class() - if not self.j.is_number: - raise ValueError( - 'State must not have symbolic j value to represent') - if not self.hilbert_space.dimension.is_number: - raise ValueError( - 'State must not have symbolic j values to represent') - result = zeros(self.hilbert_space.dimension, 1) - if self.j == int(self.j): - start = self.j**2 - else: - start = (2*self.j - 1)*(1 + 2*self.j)/4 - result[start:start + 2*self.j + 1, 0] = evect( - self.j, self.m)._represent_base(**options) - return result - - def _eval_rewrite_as_Jx(self, *args, **options): - if isinstance(self, Bra): - return self._rewrite_basis(Jx, JxBraCoupled, **options) - return self._rewrite_basis(Jx, JxKetCoupled, **options) - - def _eval_rewrite_as_Jy(self, *args, **options): - if isinstance(self, Bra): - return self._rewrite_basis(Jy, JyBraCoupled, **options) - return self._rewrite_basis(Jy, JyKetCoupled, **options) - - def _eval_rewrite_as_Jz(self, *args, **options): - if isinstance(self, Bra): - return self._rewrite_basis(Jz, JzBraCoupled, **options) - return self._rewrite_basis(Jz, JzKetCoupled, **options) - - -
    [docs]class JxKetCoupled(CoupledSpinState, Ket): - """Coupled eigenket of Jx. - - See JzKetCoupled for the usage of coupled spin eigenstates. - - See Also - ======== - - JzKetCoupled: Usage of coupled spin states - - """ - - @classmethod - def dual_class(self): - return JxBraCoupled - - @classmethod - def uncoupled_class(self): - return JxKet - - def _represent_default_basis(self, **options): - return self._represent_JzOp(None, **options) - - def _represent_JxOp(self, basis, **options): - return self._represent_coupled_base(**options) - - def _represent_JyOp(self, basis, **options): - return self._represent_coupled_base(alpha=3*pi/2, **options) - - def _represent_JzOp(self, basis, **options): - return self._represent_coupled_base(beta=pi/2, **options) - -
    -
    [docs]class JxBraCoupled(CoupledSpinState, Bra): - """Coupled eigenbra of Jx. - - See JzKetCoupled for the usage of coupled spin eigenstates. - - See Also - ======== - - JzKetCoupled: Usage of coupled spin states - - """ - - @classmethod - def dual_class(self): - return JxKetCoupled - - @classmethod - def uncoupled_class(self): - return JxBra - -
    -
    [docs]class JyKetCoupled(CoupledSpinState, Ket): - """Coupled eigenket of Jy. - - See JzKetCoupled for the usage of coupled spin eigenstates. - - See Also - ======== - - JzKetCoupled: Usage of coupled spin states - - """ - - @classmethod - def dual_class(self): - return JyBraCoupled - - @classmethod - def uncoupled_class(self): - return JyKet - - def _represent_default_basis(self, **options): - return self._represent_JzOp(None, **options) - - def _represent_JxOp(self, basis, **options): - return self._represent_coupled_base(gamma=pi/2, **options) - - def _represent_JyOp(self, basis, **options): - return self._represent_coupled_base(**options) - - def _represent_JzOp(self, basis, **options): - return self._represent_coupled_base(alpha=3*pi/2, beta=-pi/2, gamma=pi/2, **options) - -
    -
    [docs]class JyBraCoupled(CoupledSpinState, Bra): - """Coupled eigenbra of Jy. - - See JzKetCoupled for the usage of coupled spin eigenstates. - - See Also - ======== - - JzKetCoupled: Usage of coupled spin states - - """ - - @classmethod - def dual_class(self): - return JyKetCoupled - - @classmethod - def uncoupled_class(self): - return JyBra - -
    -
    [docs]class JzKetCoupled(CoupledSpinState, Ket): - """Coupled eigenket of Jz - - Spin state that is an eigenket of Jz which represents the coupling of - separate spin spaces. - - The arguments for creating instances of JzKetCoupled are ``j``, ``m``, - ``jn`` and an optional ``jcoupling`` argument. The ``j`` and ``m`` options - are the total angular momentum quantum numbers, as used for normal states - (e.g. JzKet). - - The other required parameter in ``jn``, which is a tuple defining the `j_n` - angular momentum quantum numbers of the product spaces. So for example, if - a state represented the coupling of the product basis state - `|j_1,m_1\\rangle\\times|j_2,m_2\\rangle`, the ``jn`` for this state would be - ``(j1,j2)``. - - The final option is ``jcoupling``, which is used to define how the spaces - specified by ``jn`` are coupled, which includes both the order these spaces - are coupled together and the quantum numbers that arise from these - couplings. The ``jcoupling`` parameter itself is a list of lists, such that - each of the sublists defines a single coupling between the spin spaces. If - there are N coupled angular momentum spaces, that is ``jn`` has N elements, - then there must be N-1 sublists. Each of these sublists making up the - ``jcoupling`` parameter have length 3. The first two elements are the - indicies of the product spaces that are considered to be coupled together. - For example, if we want to couple `j_1` and `j_4`, the indicies would be 1 - and 4. If a state has already been coupled, it is referenced by the - smallest index that is coupled, so if `j_2` and `j_4` has already been - coupled to some `j_{24}`, then this value can be coupled by referencing it - with index 2. The final element of the sublist is the quantum number of the - coupled state. So putting everything together, into a valid sublist for - ``jcoupling``, if `j_1` and `j_2` are coupled to an angular momentum space - with quantum number `j_{12}` with the value ``j12``, the sublist would be - ``(1,2,j12)``, N-1 of these sublists are used in the list for - ``jcoupling``. - - Note the ``jcoupling`` parameter is optional, if it is not specified, the - default coupling is taken. This default value is to coupled the spaces in - order and take the quantum number of the coupling to be the maximum value. - For example, if the spin spaces are `j_1`, `j_2`, `j_3`, `j_4`, then the - default coupling couples `j_1` and `j_2` to `j_{12}=j_1+j_2`, then, - `j_{12}` and `j_3` are coupled to `j_{123}=j_{12}+j_3`, and finally - `j_{123}` and `j_4` to `j=j_{123}+j_4`. The jcoupling value that would - correspond to this is: - - ``((1,2,j1+j2),(1,3,j1+j2+j3))`` - - Parameters - ========== - - args : tuple - The arguments that must be passed are ``j``, ``m``, ``jn``, and - ``jcoupling``. The ``j`` value is the total angular momentum. The ``m`` - value is the eigenvalue of the Jz spin operator. The ``jn`` list are - the j values of argular momentum spaces coupled together. The - ``jcoupling`` parameter is an optional parameter defining how the spaces - are coupled together. See the above description for how these coupling - parameters are defined. - - Examples - ======== - - Defining simple spin states, both numerical and symbolic: - - >>> from sympy.physics.quantum.spin import JzKetCoupled - >>> from sympy import symbols - >>> JzKetCoupled(1, 0, (1, 1)) - |1,0,j1=1,j2=1> - >>> j, m, j1, j2 = symbols('j m j1 j2') - >>> JzKetCoupled(j, m, (j1, j2)) - |j,m,j1=j1,j2=j2> - - Defining coupled spin states for more than 2 coupled spaces with various - coupling parameters: - - >>> JzKetCoupled(2, 1, (1, 1, 1)) - |2,1,j1=1,j2=1,j3=1,j(1,2)=2> - >>> JzKetCoupled(2, 1, (1, 1, 1), ((1,2,2),(1,3,2)) ) - |2,1,j1=1,j2=1,j3=1,j(1,2)=2> - >>> JzKetCoupled(2, 1, (1, 1, 1), ((2,3,1),(1,2,2)) ) - |2,1,j1=1,j2=1,j3=1,j(2,3)=1> - - Rewriting the JzKetCoupled in terms of eigenkets of the Jx operator: - Note: that the resulting eigenstates are JxKetCoupled - - >>> JzKetCoupled(1,1,(1,1)).rewrite("Jx") - |1,-1,j1=1,j2=1>/2 - sqrt(2)*|1,0,j1=1,j2=1>/2 + |1,1,j1=1,j2=1>/2 - - The rewrite method can be used to convert a coupled state to an uncoupled - state. This is done by passing coupled=False to the rewrite function: - - >>> JzKetCoupled(1, 0, (1, 1)).rewrite('Jz', coupled=False) - -sqrt(2)*|1,-1>x|1,1>/2 + sqrt(2)*|1,1>x|1,-1>/2 - - Get the vector representation of a state in terms of the basis elements - of the Jx operator: - - >>> from sympy.physics.quantum.represent import represent - >>> from sympy.physics.quantum.spin import Jx - >>> from sympy import S - >>> represent(JzKetCoupled(1,-1,(S(1)/2,S(1)/2)), basis=Jx) - [ 0] - [ 1/2] - [sqrt(2)/2] - [ 1/2] - - See Also - ======== - - JzKet: Normal spin eigenstates - uncouple: Uncoupling of coupling spin states - couple: Coupling of uncoupled spin states - - """ - - @classmethod - def dual_class(self): - return JzBraCoupled - - @classmethod - def uncoupled_class(self): - return JzKet - - def _represent_default_basis(self, **options): - return self._represent_JzOp(None, **options) - - def _represent_JxOp(self, basis, **options): - return self._represent_coupled_base(beta=3*pi/2, **options) - - def _represent_JyOp(self, basis, **options): - return self._represent_coupled_base(alpha=3*pi/2, beta=pi/2, gamma=pi/2, **options) - - def _represent_JzOp(self, basis, **options): - return self._represent_coupled_base(**options) - -
    -
    [docs]class JzBraCoupled(CoupledSpinState, Bra): - """Coupled eigenbra of Jz. - - See the JzKetCoupled for the usage of coupled spin eigenstates. - - See Also - ======== - - JzKetCoupled: Usage of coupled spin states - - """ - - @classmethod - def dual_class(self): - return JzKetCoupled - - @classmethod - def uncoupled_class(self): - return JzBra - -#----------------------------------------------------------------------------- -# Coupling/uncoupling -#----------------------------------------------------------------------------- - -
    -
    [docs]def couple(expr, jcoupling_list=None): - """ Couple a tensor product of spin states - - This function can be used to couple an uncoupled tensor product of spin - states. All of the eigenstates to be coupled must be of the same class. It - will return a linear combination of eigenstates that are subclasses of - CoupledSpinState determined by Clebsch-Gordan angular momentum coupling - coefficients. - - Parameters - ========== - - expr : Expr - An expression involving TensorProducts of spin states to be coupled. - Each state must be a subclass of SpinState and they all must be the - same class. - - jcoupling_list : list or tuple - Elements of this list are sub-lists of length 2 specifying the order of - the coupling of the spin spaces. The length of this must be N-1, where N - is the number of states in the tensor product to be coupled. The - elements of this sublist are the same as the first two elements of each - sublist in the ``jcoupling`` parameter defined for JzKetCoupled. If this - parameter is not specified, the default value is taken, which couples - the first and second product basis spaces, then couples this new coupled - space to the third product space, etc - - Examples - ======== - - Couple a tensor product of numerical states for two spaces: - - >>> from sympy.physics.quantum.spin import JzKet, couple - >>> from sympy.physics.quantum.tensorproduct import TensorProduct - >>> couple(TensorProduct(JzKet(1,0), JzKet(1,1))) - -sqrt(2)*|1,1,j1=1,j2=1>/2 + sqrt(2)*|2,1,j1=1,j2=1>/2 - - - Numerical coupling of three spaces using the default coupling method, i.e. - first and second spaces couple, then this couples to the third space: - - >>> couple(TensorProduct(JzKet(1,1), JzKet(1,1), JzKet(1,0))) - sqrt(6)*|2,2,j1=1,j2=1,j3=1,j(1,2)=2>/3 + sqrt(3)*|3,2,j1=1,j2=1,j3=1,j(1,2)=2>/3 - - Perform this same coupling, but we define the coupling to first couple - the first and third spaces: - - >>> couple(TensorProduct(JzKet(1,1), JzKet(1,1), JzKet(1,0)), ((1,3),(1,2)) ) - sqrt(2)*|2,2,j1=1,j2=1,j3=1,j(1,3)=1>/2 - sqrt(6)*|2,2,j1=1,j2=1,j3=1,j(1,3)=2>/6 + sqrt(3)*|3,2,j1=1,j2=1,j3=1,j(1,3)=2>/3 - - Couple a tensor product of symbolic states: - - >>> from sympy import symbols - >>> j1,m1,j2,m2 = symbols('j1 m1 j2 m2') - >>> couple(TensorProduct(JzKet(j1,m1), JzKet(j2,m2))) - Sum(CG(j1, m1, j2, m2, j, m1 + m2)*|j,m1 + m2,j1=j1,j2=j2>, (j, m1 + m2, j1 + j2)) - - """ - a = expr.atoms(TensorProduct) - for tp in a: - # Allow other tensor products to be in expression - if not all([ isinstance(state, SpinState) for state in tp.args]): - continue - # If tensor product has all spin states, raise error for invalid tensor product state - if not all([state.__class__ is tp.args[0].__class__ for state in tp.args]): - raise TypeError('All states must be the same basis') - expr = expr.subs(tp, _couple(tp, jcoupling_list)) - return expr - -
    -def _couple(tp, jcoupling_list): - states = tp.args - coupled_evect = states[0].coupled_class() - - # Define default coupling if none is specified - if jcoupling_list is None: - jcoupling_list = [] - for n in range(1, len(states)): - jcoupling_list.append( (1, n + 1) ) - - # Check jcoupling_list valid - if not len(jcoupling_list) == len(states) - 1: - raise TypeError('jcoupling_list must be length %d, got %d' % - (len(states) - 1, len(jcoupling_list))) - if not all( len(coupling) == 2 for coupling in jcoupling_list): - raise ValueError('Each coupling must define 2 spaces') - if any([n1 == n2 for n1, n2 in jcoupling_list]): - raise ValueError('Spin spaces cannot couple to themselves') - if all([sympify(n1).is_number and sympify(n2).is_number for n1, n2 in jcoupling_list]): - j_test = [0]*len(states) - for n1, n2 in jcoupling_list: - if j_test[n1 - 1] == -1 or j_test[n2 - 1] == -1: - raise ValueError('Spaces coupling j_n\'s are referenced by smallest n value') - j_test[max(n1, n2) - 1] = -1 - - # j values of states to be coupled together - jn = [state.j for state in states] - mn = [state.m for state in states] - - # Create coupling_list, which defines all the couplings between all - # the spaces from jcoupling_list - coupling_list = [] - n_list = [ [i + 1] for i in range(len(states)) ] - for j_coupling in jcoupling_list: - # Least n for all j_n which is coupled as first and second spaces - n1, n2 = j_coupling - # List of all n's coupled in first and second spaces - j1_n = list(n_list[n1 - 1]) - j2_n = list(n_list[n2 - 1]) - coupling_list.append( (j1_n, j2_n) ) - # Set new j_n to be coupling of all j_n in both first and second spaces - n_list[ min(n1, n2) - 1 ] = sorted(j1_n + j2_n) - - if all(state.j.is_number and state.m.is_number for state in states): - # Numerical coupling - # Iterate over difference between maximum possible j value of each coupling and the actual value - diff_max = [ Add( *[ jn[n - 1] - mn[n - 1] for n in coupling[0] + - coupling[1] ] ) for coupling in coupling_list ] - result = [] - for diff in range(diff_max[-1] + 1): - # Determine available configurations - n = len(coupling_list) - tot = binomial(diff + n - 1, diff) - - for config_num in range(tot): - diff_list = _confignum_to_difflist(config_num, diff, n) - - # Skip the configuration if non-physical - # This is a lazy check for physical states given the loose restrictions of diff_max - if any( [ d > m for d, m in zip(diff_list, diff_max) ] ): - continue - - # Determine term - cg_terms = [] - coupled_j = list(jn) - jcoupling = [] - for (j1_n, j2_n), coupling_diff in zip(coupling_list, diff_list): - j1 = coupled_j[ min(j1_n) - 1 ] - j2 = coupled_j[ min(j2_n) - 1 ] - j3 = j1 + j2 - coupling_diff - coupled_j[ min(j1_n + j2_n) - 1 ] = j3 - m1 = Add( *[ mn[x - 1] for x in j1_n] ) - m2 = Add( *[ mn[x - 1] for x in j2_n] ) - m3 = m1 + m2 - cg_terms.append( (j1, m1, j2, m2, j3, m3) ) - jcoupling.append( (min(j1_n), min(j2_n), j3) ) - # Better checks that state is physical - if any([ abs(term[5]) > term[4] for term in cg_terms ]): - continue - if any([ term[0] + term[2] < term[4] for term in cg_terms ]): - continue - if any([ abs(term[0] - term[2]) > term[4] for term in cg_terms ]): - continue - coeff = Mul( *[ CG(*term).doit() for term in cg_terms] ) - state = coupled_evect(j3, m3, jn, jcoupling) - result.append(coeff*state) - return Add(*result) - else: - # Symbolic coupling - cg_terms = [] - jcoupling = [] - sum_terms = [] - coupled_j = list(jn) - for j1_n, j2_n in coupling_list: - j1 = coupled_j[ min(j1_n) - 1 ] - j2 = coupled_j[ min(j2_n) - 1 ] - if len(j1_n + j2_n) == len(states): - j3 = symbols('j') - else: - j3_name = 'j' + ''.join(["%s" % n for n in j1_n + j2_n]) - j3 = symbols(j3_name) - coupled_j[ min(j1_n + j2_n) - 1 ] = j3 - m1 = Add( *[ mn[x - 1] for x in j1_n] ) - m2 = Add( *[ mn[x - 1] for x in j2_n] ) - m3 = m1 + m2 - cg_terms.append( (j1, m1, j2, m2, j3, m3) ) - jcoupling.append( (min(j1_n), min(j2_n), j3) ) - sum_terms.append((j3, m3, j1 + j2)) - coeff = Mul( *[ CG(*term) for term in cg_terms] ) - state = coupled_evect(j3, m3, jn, jcoupling) - return Sum(coeff*state, *sum_terms) - - -
    [docs]def uncouple(expr, jn=None, jcoupling_list=None): - """ Uncouple a coupled spin state - - Gives the uncoupled representation of a coupled spin state. Arguments must - be either a spin state that is a subclass of CoupledSpinState or a spin - state that is a subclass of SpinState and an array giving the j values - of the spaces that are to be coupled - - Parameters - ========== - - expr : Expr - The expression containing states that are to be coupled. If the states - are a subclass of SpinState, the ``jn`` and ``jcoupling`` parameters - must be defined. If the states are a subclass of CoupledSpinState, - ``jn`` and ``jcoupling`` will be taken from the state. - - jn : list or tuple - The list of the j-values that are coupled. If state is a - CoupledSpinState, this parameter is ignored. This must be defined if - state is not a subclass of CoupledSpinState. The syntax of this - parameter is the same as the ``jn`` parameter of JzKetCoupled. - - jcoupling_list : list or tuple - The list defining how the j-values are coupled together. If state is a - CoupledSpinState, this parameter is ignored. This must be defined if - state is not a subclass of CoupledSpinState. The syntax of this - parameter is the same as the ``jcoupling`` parameter of JzKetCoupled. - - Examples - ======== - - Uncouple a numerical state using a CoupledSpinState state: - - >>> from sympy.physics.quantum.spin import JzKetCoupled, uncouple - >>> from sympy import S - >>> uncouple(JzKetCoupled(1, 0, (S(1)/2, S(1)/2))) - sqrt(2)*|1/2,-1/2>x|1/2,1/2>/2 + sqrt(2)*|1/2,1/2>x|1/2,-1/2>/2 - - Perform the same calculation using a SpinState state: - - >>> from sympy.physics.quantum.spin import JzKet - >>> uncouple(JzKet(1, 0), (S(1)/2, S(1)/2)) - sqrt(2)*|1/2,-1/2>x|1/2,1/2>/2 + sqrt(2)*|1/2,1/2>x|1/2,-1/2>/2 - - Uncouple a numerical state of three coupled spaces using a CoupledSpinState state: - - >>> uncouple(JzKetCoupled(1, 1, (1, 1, 1), ((1,3,1),(1,2,1)) )) - |1,-1>x|1,1>x|1,1>/2 - |1,0>x|1,0>x|1,1>/2 + |1,1>x|1,0>x|1,0>/2 - |1,1>x|1,1>x|1,-1>/2 - - Perform the same calculation using a SpinState state: - - >>> uncouple(JzKet(1, 1), (1, 1, 1), ((1,3,1),(1,2,1)) ) - |1,-1>x|1,1>x|1,1>/2 - |1,0>x|1,0>x|1,1>/2 + |1,1>x|1,0>x|1,0>/2 - |1,1>x|1,1>x|1,-1>/2 - - Uncouple a symbolic state using a CoupledSpinState state: - - >>> from sympy import symbols - >>> j,m,j1,j2 = symbols('j m j1 j2') - >>> uncouple(JzKetCoupled(j, m, (j1, j2))) - Sum(CG(j1, m1, j2, m2, j, m)*|j1,m1>x|j2,m2>, (m1, -j1, j1), (m2, -j2, j2)) - - Perform the same calculation using a SpinState state - - >>> uncouple(JzKet(j, m), (j1, j2)) - Sum(CG(j1, m1, j2, m2, j, m)*|j1,m1>x|j2,m2>, (m1, -j1, j1), (m2, -j2, j2)) - - """ - a = expr.atoms(SpinState) - for state in a: - expr = expr.subs(state, _uncouple(state, jn, jcoupling_list)) - return expr - -
    -def _uncouple(state, jn, jcoupling_list): - if isinstance(state, CoupledSpinState): - jn = state.jn - coupled_n = state.coupled_n - coupled_jn = state.coupled_jn - evect = state.uncoupled_class() - elif isinstance(state, SpinState): - if jn is None: - raise ValueError("Must specify j-values for coupled state") - if not (isinstance(jn, list) or isinstance(jn, tuple)): - raise TypeError("jn must be list or tuple") - if jcoupling_list is None: - # Use default - jcoupling_list = [] - for i in range(1, len(jn)): - jcoupling_list.append( - (1, 1 + i, Add(*[jn[j] for j in range(i + 1)])) ) - if not (isinstance(jcoupling_list, list) or isinstance(jcoupling_list, tuple)): - raise TypeError("jcoupling must be a list or tuple") - if not len(jcoupling_list) == len(jn) - 1: - raise ValueError("Must specify 2 fewer coupling terms than the number of j values") - coupled_n, coupled_jn = _build_coupled(jcoupling_list, len(jn)) - evect = state.__class__ - else: - raise TypeError("state must be a spin state") - j = state.j - m = state.m - coupling_list = [] - j_list = list(jn) - - # Create coupling, which defines all the couplings between all the spaces - for j3, (n1, n2) in zip(coupled_jn, coupled_n): - # j's which are coupled as first and second spaces - j1 = j_list[n1[0] - 1] - j2 = j_list[n2[0] - 1] - # Build coupling list - coupling_list.append( (n1, n2, j1, j2, j3) ) - # Set new value in j_list - j_list[min(n1 + n2) - 1] = j3 - - if j.is_number and m.is_number: - diff_max = [ 2*x for x in jn ] - diff = Add(*jn) - m - - n = len(jn) - tot = binomial(diff + n - 1, diff) - - result = [] - for config_num in range(tot): - diff_list = _confignum_to_difflist(config_num, diff, n) - if any( [ d > p for d, p in zip(diff_list, diff_max) ] ): - continue - - cg_terms = [] - for coupling in coupling_list: - j1_n, j2_n, j1, j2, j3 = coupling - m1 = Add( *[ jn[x - 1] - diff_list[x - 1] for x in j1_n ] ) - m2 = Add( *[ jn[x - 1] - diff_list[x - 1] for x in j2_n ] ) - m3 = m1 + m2 - cg_terms.append( (j1, m1, j2, m2, j3, m3) ) - coeff = Mul( *[ CG(*term).doit() for term in cg_terms ] ) - state = TensorProduct( - *[ evect(j, j - d) for j, d in zip(jn, diff_list) ] ) - result.append(coeff*state) - return Add(*result) - else: - # Symbolic coupling - m_str = "m1:%d" % (len(jn) + 1) - mvals = symbols(m_str) - cg_terms = [(j1, Add(*[mvals[n - 1] for n in j1_n]), - j2, Add(*[mvals[n - 1] for n in j2_n]), - j3, Add(*[mvals[n - 1] for n in j1_n + j2_n])) for j1_n, j2_n, j1, j2, j3 in coupling_list[:-1] ] - cg_terms.append(*[(j1, Add(*[mvals[n - 1] for n in j1_n]), - j2, Add(*[mvals[n - 1] for n in j2_n]), - j, m) for j1_n, j2_n, j1, j2, j3 in [coupling_list[-1]] ]) - cg_coeff = Mul(*[CG(*cg_term) for cg_term in cg_terms]) - sum_terms = [ (m, -j, j) for j, m in zip(jn, mvals) ] - state = TensorProduct( *[ evect(j, m) for j, m in zip(jn, mvals) ] ) - return Sum(cg_coeff*state, *sum_terms) - - -def _confignum_to_difflist(config_num, diff, list_len): - # Determines configuration of diffs into list_len number of slots - diff_list = [] - for n in range(list_len): - prev_diff = diff - # Number of spots after current one - rem_spots = list_len - n - 1 - # Number of configurations of distributing diff among the remaining spots - rem_configs = binomial(diff + rem_spots - 1, diff) - while config_num >= rem_configs: - config_num -= rem_configs - diff -= 1 - rem_configs = binomial(diff + rem_spots - 1, diff) - diff_list.append(prev_diff - diff) - return diff_list -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/quantum/state.html b/dev-py3k/_modules/sympy/physics/quantum/state.html deleted file mode 100644 index 7a5f4109537..00000000000 --- a/dev-py3k/_modules/sympy/physics/quantum/state.html +++ /dev/null @@ -1,1067 +0,0 @@ - - - - - - - - - - sympy.physics.quantum.state — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.quantum.state

    -"""Dirac notation for states."""
    -
    -
    -from sympy import (cacheit, conjugate, Expr, Function, integrate, oo, sqrt,
    -                   Tuple)
    -from sympy.printing.pretty.stringpict import prettyForm, stringPict
    -from sympy.physics.quantum.qexpr import QExpr, dispatch_method
    -
    -__all__ = [
    -    'KetBase',
    -    'BraBase',
    -    'StateBase',
    -    'State',
    -    'Ket',
    -    'Bra',
    -    'TimeDepState',
    -    'TimeDepBra',
    -    'TimeDepKet',
    -    'Wavefunction'
    -]
    -
    -
    -#-----------------------------------------------------------------------------
    -# States, bras and kets.
    -#-----------------------------------------------------------------------------
    -
    -# ASCII brackets
    -_lbracket = "<"
    -_rbracket = ">"
    -_straight_bracket = "|"
    -
    -
    -# Unicode brackets
    -# MATHEMATICAL ANGLE BRACKETS
    -_lbracket_ucode = "\u27E8"
    -_rbracket_ucode = "\u27E9"
    -# LIGHT VERTICAL BAR
    -_straight_bracket_ucode = "\u2758"
    -
    -# Other options for unicode printing of <, > and | for Dirac notation.
    -
    -# LEFT-POINTING ANGLE BRACKET
    -# _lbracket = u"\u2329"
    -# _rbracket = u"\u232A"
    -
    -# LEFT ANGLE BRACKET
    -# _lbracket = u"\u3008"
    -# _rbracket = u"\u3009"
    -
    -# VERTICAL LINE
    -# _straight_bracket = u"\u007C"
    -
    -
    -
    [docs]class StateBase(QExpr): - """Abstract base class for general abstract states in quantum mechanics. - - All other state classes defined will need to inherit from this class. It - carries the basic structure for all other states such as dual, _eval_adjoint - and label. - - This is an abstract base class and you should not instantiate it directly, - instead use State. - """ - - @classmethod - def _operators_to_state(self, ops, **options): - """ Returns the eigenstate instance for the passed operators. - - This method should be overridden in subclasses. It will handle being - passed either an Operator instance or set of Operator instances. It - should return the corresponding state INSTANCE or simply raise a - NotImplementedError. See cartesian.py for an example. - """ - - raise NotImplementedError("Cannot map operators to states in this class. Method not implemented!") - - def _state_to_operators(self, op_classes, **options): - """ Returns the operators which this state instance is an eigenstate - of. - - This method should be overridden in subclasses. It will be called on - state instances and be passed the operator classes that we wish to make - into instances. The state instance will then transform the classes - appropriately, or raise a NotImplementedError if it cannot return - operator instances. See cartesian.py for examples, - """ - - raise NotImplementedError( - "Cannot map this state to operators. Method not implemented!") - - @property -
    [docs] def operators(self): - """Return the operator(s) that this state is an eigenstate of""" - from .operatorset import state_to_operators # import internally to avoid circular import errors - return state_to_operators(self) -
    - def _enumerate_state(self, num_states, **options): - raise NotImplementedError("Cannot enumerate this state!") - - def _represent_default_basis(self, **options): - return self._represent(basis=self.operators) - - #------------------------------------------------------------------------- - # Dagger/dual - #------------------------------------------------------------------------- - - @property -
    [docs] def dual(self): - """Return the dual state of this one.""" - return self.dual_class()._new_rawargs(self.hilbert_space, *self.args) -
    - @classmethod -
    [docs] def dual_class(self): - """Return the class used to construt the dual.""" - raise NotImplementedError( - 'dual_class must be implemented in a subclass' - ) -
    - def _eval_adjoint(self): - """Compute the dagger of this state using the dual.""" - return self.dual - - #------------------------------------------------------------------------- - # Printing - #------------------------------------------------------------------------- - - def _pretty_brackets(self, height, use_unicode=True): - # Return pretty printed brackets for the state - # Ideally, this could be done by pform.parens but it does not support the angled < and > - - # Setup for unicode vs ascii - if use_unicode: - lbracket, rbracket = self.lbracket_ucode, self.rbracket_ucode - slash, bslash, vert = '\u2571', '\u2572', '\u2502' - else: - lbracket, rbracket = self.lbracket, self.rbracket - slash, bslash, vert = '/', '\\', '|' - - # If height is 1, just return brackets - if height == 1: - return stringPict(lbracket), stringPict(rbracket) - # Make height even - height += (height % 2) - - brackets = [] - for bracket in lbracket, rbracket: - # Create left bracket - if bracket in set([_lbracket, _lbracket_ucode]): - bracket_args = [ ' ' * (height//2 - i - 1) + - slash for i in range(height // 2)] - bracket_args.extend( - [ ' ' * i + bslash for i in range(height // 2)]) - # Create right bracket - elif bracket in set([_rbracket, _rbracket_ucode]): - bracket_args = [ ' ' * i + bslash for i in range(height // 2)] - bracket_args.extend([ ' ' * ( - height//2 - i - 1) + slash for i in range(height // 2)]) - # Create straight bracket - elif bracket in set([_straight_bracket, _straight_bracket_ucode]): - bracket_args = [vert for i in range(height)] - else: - raise ValueError(bracket) - brackets.append( - stringPict('\n'.join(bracket_args), baseline=height//2)) - return brackets - - def _sympystr(self, printer, *args): - contents = self._print_contents(printer, *args) - return '%s%s%s' % (self.lbracket, contents, self.rbracket) - - def _pretty(self, printer, *args): - from sympy.printing.pretty.stringpict import prettyForm - # Get brackets - pform = self._print_contents_pretty(printer, *args) - lbracket, rbracket = self._pretty_brackets( - pform.height(), printer._use_unicode) - # Put together state - pform = prettyForm(*pform.left(lbracket)) - pform = prettyForm(*pform.right(rbracket)) - return pform - - def _latex(self, printer, *args): - contents = self._print_contents_latex(printer, *args) - # The extra {} brackets are needed to get matplotlib's latex - # rendered to render this properly. - return '{%s%s%s}' % (self.lbracket_latex, contents, self.rbracket_latex) - -
    -
    [docs]class KetBase(StateBase): - """Base class for Kets. - - This class defines the dual property and the brackets for printing. This is - an abstract base class and you should not instantiate it directly, instead - use Ket. - """ - - lbracket = _straight_bracket - rbracket = _rbracket - lbracket_ucode = _straight_bracket_ucode - rbracket_ucode = _rbracket_ucode - lbracket_latex = r'\left|' - rbracket_latex = r'\right\rangle ' - - @classmethod - def default_args(self): - return ("psi",) - - @classmethod - def dual_class(self): - return BraBase - - def __mul__(self, other): - """KetBase*other""" - from sympy.physics.quantum.operator import OuterProduct - if isinstance(other, BraBase): - return OuterProduct(self, other) - else: - return Expr.__mul__(self, other) - - def __rmul__(self, other): - """other*KetBase""" - from sympy.physics.quantum.innerproduct import InnerProduct - if isinstance(other, BraBase): - return InnerProduct(other, self) - else: - return Expr.__rmul__(self, other) - - #------------------------------------------------------------------------- - # _eval_* methods - #------------------------------------------------------------------------- - - def _eval_innerproduct(self, bra, **hints): - """Evaluate the inner product betweeen this ket and a bra. - - This is called to compute <bra|ket>, where the ket is ``self``. - - This method will dispatch to sub-methods having the format:: - - ``def _eval_innerproduct_BraClass(self, **hints):`` - - Subclasses should define these methods (one for each BraClass) to - teach the ket how to take inner products with bras. - """ - return dispatch_method(self, '_eval_innerproduct', bra, **hints) - - def _apply_operator(self, op, **options): - """Apply an Operator to this Ket. - - This method will dispatch to methods having the format:: - - ``def _apply_operator_OperatorName(op, **options):`` - - Subclasses should define these methods (one for each OperatorName) to - teach the Ket how operators act on it. - - Parameters - ========== - - op : Operator - The Operator that is acting on the Ket. - options : dict - A dict of key/value pairs that control how the operator is applied - to the Ket. - """ - return dispatch_method(self, '_apply_operator', op, **options) - -
    -
    [docs]class BraBase(StateBase): - """Base class for Bras. - - This class defines the dual property and the brackets for printing. This - is an abstract base class and you should not instantiate it directly, - instead use Bra. - """ - - lbracket = _lbracket - rbracket = _straight_bracket - lbracket_ucode = _lbracket_ucode - rbracket_ucode = _straight_bracket_ucode - lbracket_latex = r'\left\langle ' - rbracket_latex = r'\right|' - - @classmethod - def _operators_to_state(self, ops, **options): - state = self.dual_class().operators_to_state(ops, **options) - return state.dual - - def _state_to_operators(self, op_classes, **options): - return self.dual._state_to_operators(op_classes, **options) - - def _enumerate_state(self, num_states, **options): - dual_states = self.dual._enumerate_state(num_states, **options) - return [x.dual for x in dual_states] - - @classmethod - def default_args(self): - return self.dual_class().default_args() - - @classmethod - def dual_class(self): - return KetBase - - def __mul__(self, other): - """BraBase*other""" - from sympy.physics.quantum.innerproduct import InnerProduct - if isinstance(other, KetBase): - return InnerProduct(self, other) - else: - return Expr.__mul__(self, other) - - def __rmul__(self, other): - """other*BraBase""" - from sympy.physics.quantum.operator import OuterProduct - if isinstance(other, KetBase): - return OuterProduct(other, self) - else: - return Expr.__rmul__(self, other) - - def _represent(self, **options): - """A default represent that uses the Ket's version.""" - from sympy.physics.quantum.dagger import Dagger - return Dagger(self.dual._represent(**options)) - -
    -
    [docs]class State(StateBase): - """General abstract quantum state used as a base class for Ket and Bra.""" - pass - -
    -
    [docs]class Ket(State, KetBase): - """A general time-independent Ket in quantum mechanics. - - Inherits from State and KetBase. This class should be used as the base - class for all physical, time-independent Kets in a system. This class - and its subclasses will be the main classes that users will use for - expressing Kets in Dirac notation [1]_. - - Parameters - ========== - - args : tuple - The list of numbers or parameters that uniquely specify the - ket. This will usually be its symbol or its quantum numbers. For - time-dependent state, this will include the time. - - Examples - ======== - - Create a simple Ket and looking at its properties:: - - >>> from sympy.physics.quantum import Ket, Bra - >>> from sympy import symbols, I - >>> k = Ket('psi') - >>> k - |psi> - >>> k.hilbert_space - H - >>> k.is_commutative - False - >>> k.label - (psi,) - - Ket's know about their associated bra:: - - >>> k.dual - <psi| - >>> k.dual_class() - <class 'sympy.physics.quantum.state.Bra'> - - Take a linear combination of two kets:: - - >>> k0 = Ket(0) - >>> k1 = Ket(1) - >>> 2*I*k0 - 4*k1 - 2*I*|0> - 4*|1> - - Compound labels are passed as tuples:: - - >>> n, m = symbols('n,m') - >>> k = Ket(n,m) - >>> k - |nm> - - References - ========== - - .. [1] http://en.wikipedia.org/wiki/Bra-ket_notation - """ - - @classmethod - def dual_class(self): - return Bra - -
    -
    [docs]class Bra(State, BraBase): - """A general time-independent Bra in quantum mechanics. - - Inherits from State and BraBase. A Bra is the dual of a Ket [1]_. This - class and its subclasses will be the main classes that users will use for - expressing Bras in Dirac notation. - - Parameters - ========== - - args : tuple - The list of numbers or parameters that uniquely specify the - ket. This will usually be its symbol or its quantum numbers. For - time-dependent state, this will include the time. - - Examples - ======== - - Create a simple Bra and look at its properties:: - - >>> from sympy.physics.quantum import Ket, Bra - >>> from sympy import symbols, I - >>> b = Bra('psi') - >>> b - <psi| - >>> b.hilbert_space - H - >>> b.is_commutative - False - - Bra's know about their dual Ket's:: - - >>> b.dual - |psi> - >>> b.dual_class() - <class 'sympy.physics.quantum.state.Ket'> - - Like Kets, Bras can have compound labels and be manipulated in a similar - manner:: - - >>> n, m = symbols('n,m') - >>> b = Bra(n,m) - I*Bra(m,n) - >>> b - -I*<mn| + <nm| - - Symbols in a Bra can be substituted using ``.subs``:: - - >>> b.subs(n,m) - <mm| - I*<mm| - - References - ========== - - .. [1] http://en.wikipedia.org/wiki/Bra-ket_notation - """ - - @classmethod - def dual_class(self): - return Ket - -#----------------------------------------------------------------------------- -# Time dependent states, bras and kets. -#----------------------------------------------------------------------------- - -
    -
    [docs]class TimeDepState(StateBase): - """Base class for a general time-dependent quantum state. - - This class is used as a base class for any time-dependent state. The main - difference between this class and the time-independent state is that this - class takes a second argument that is the time in addition to the usual - label argument. - - Parameters - ========== - - args : tuple - The list of numbers or parameters that uniquely specify the ket. This - will usually be its symbol or its quantum numbers. For time-dependent - state, this will include the time as the final argument. - """ - - #------------------------------------------------------------------------- - # Initialization - #------------------------------------------------------------------------- - - @classmethod - def default_args(self): - return ("psi", "t") - - #------------------------------------------------------------------------- - # Properties - #------------------------------------------------------------------------- - - @property -
    [docs] def label(self): - """The label of the state.""" - return self.args[:-1] -
    - @property -
    [docs] def time(self): - """The time of the state.""" - return self.args[-1] - - #------------------------------------------------------------------------- - # Printing - #------------------------------------------------------------------------- -
    - def _print_time(self, printer, *args): - return printer._print(self.time, *args) - - _print_time_repr = _print_time - _print_time_latex = _print_time - - def _print_time_pretty(self, printer, *args): - pform = printer._print(self.time, *args) - return pform - - def _print_contents(self, printer, *args): - label = self._print_label(printer, *args) - time = self._print_time(printer, *args) - return '%s;%s' % (label, time) - - def _print_label_repr(self, printer, *args): - label = self._print_sequence(self.label, ',', printer, *args) - time = self._print_time_repr(printer, *args) - return '%s,%s' % (label, time) - - def _print_contents_pretty(self, printer, *args): - label = self._print_label_pretty(printer, *args) - time = self._print_time_pretty(printer, *args) - return printer._print_seq((label, time), delimiter=';') - - def _print_contents_latex(self, printer, *args): - label = self._print_sequence( - self.label, self._label_separator, printer, *args) - time = self._print_time_latex(printer, *args) - return '%s;%s' % (label, time) - -
    -
    [docs]class TimeDepKet(TimeDepState, KetBase): - """General time-dependent Ket in quantum mechanics. - - This inherits from ``TimeDepState`` and ``KetBase`` and is the main class - that should be used for Kets that vary with time. Its dual is a - ``TimeDepBra``. - - Parameters - ========== - - args : tuple - The list of numbers or parameters that uniquely specify the ket. This - will usually be its symbol or its quantum numbers. For time-dependent - state, this will include the time as the final argument. - - Examples - ======== - - Create a TimeDepKet and look at its attributes:: - - >>> from sympy.physics.quantum import TimeDepKet - >>> k = TimeDepKet('psi', 't') - >>> k - |psi;t> - >>> k.time - t - >>> k.label - (psi,) - >>> k.hilbert_space - H - - TimeDepKets know about their dual bra:: - - >>> k.dual - <psi;t| - >>> k.dual_class() - <class 'sympy.physics.quantum.state.TimeDepBra'> - """ - - @classmethod - def dual_class(self): - return TimeDepBra - -
    -
    [docs]class TimeDepBra(TimeDepState, BraBase): - """General time-dependent Bra in quantum mechanics. - - This inherits from TimeDepState and BraBase and is the main class that - should be used for Bras that vary with time. Its dual is a TimeDepBra. - - Parameters - ========== - - args : tuple - The list of numbers or parameters that uniquely specify the ket. This - will usually be its symbol or its quantum numbers. For time-dependent - state, this will include the time as the final argument. - - Examples - ======== - - >>> from sympy.physics.quantum import TimeDepBra - >>> from sympy import symbols, I - >>> b = TimeDepBra('psi', 't') - >>> b - <psi;t| - >>> b.time - t - >>> b.label - (psi,) - >>> b.hilbert_space - H - >>> b.dual - |psi;t> - """ - - @classmethod - def dual_class(self): - return TimeDepKet - -
    -
    [docs]class Wavefunction(Function): - """Class for representations in continuous bases - - This class takes an expression and coordinates in its constructor. It can - be used to easily calculate normalizations and probabilities. - - Parameters - ========== - - expr : Expr - The expression representing the functional form of the w.f. - - coords : Symbol or tuple - The coordinates to be integrated over, and their bounds - - Examples - ======== - - Particle in a box, specifying bounds in the more primitive way of using - Piecewise: - - >>> from sympy import Symbol, Piecewise, pi, N - >>> from sympy.functions import sqrt, sin - >>> from sympy.physics.quantum.state import Wavefunction - >>> x = Symbol('x', real=True) - >>> n = 1 - >>> L = 1 - >>> g = Piecewise((0, x < 0), (0, x > L), (sqrt(2//L)*sin(n*pi*x/L), True)) - >>> f = Wavefunction(g, x) - >>> f.norm - 1 - >>> f.is_normalized - True - >>> p = f.prob() - >>> p(0) - 0 - >>> p(L) - 0 - >>> p(0.5) - 2 - >>> p(0.85*L) - 2*sin(0.85*pi)**2 - >>> N(p(0.85*L)) - 0.412214747707527 - - Additionally, you can specify the bounds of the function and the indices in - a more compact way: - - >>> from sympy import symbols, pi, diff - >>> from sympy.functions import sqrt, sin - >>> from sympy.physics.quantum.state import Wavefunction - >>> x, L = symbols('x,L', positive=True) - >>> n = symbols('n', integer=True) - >>> g = sqrt(2/L)*sin(n*pi*x/L) - >>> f = Wavefunction(g, (x, 0, L)) - >>> f.norm - 1 - >>> f(L+1) - 0 - >>> f(L-1) - sqrt(2)*sin(pi*n*(L - 1)/L)/sqrt(L) - >>> f(-1) - 0 - >>> f(0.85) - sqrt(2)*sin(0.85*pi*n/L)/sqrt(L) - >>> f(0.85, n=1, L=1) - sqrt(2)*sin(0.85*pi) - >>> f.is_commutative - False - - All arguments are automatically sympified, so you can define the variables - as strings rather than symbols: - - >>> expr = x**2 - >>> f = Wavefunction(expr, 'x') - >>> type(f.variables[0]) - <class 'sympy.core.symbol.Symbol'> - - Derivatives of Wavefunctions will return Wavefunctions: - - >>> diff(f, x) - Wavefunction(2*x, x) - - """ - - #Any passed tuples for coordinates and their bounds need to be - #converted to Tuples before Function's constructor is called, to - #avoid errors from calling is_Float in the constructor - def __new__(cls, *args, **options): - new_args = [None for i in args] - ct = 0 - for arg in args: - if isinstance(arg, tuple): - new_args[ct] = Tuple(*arg) - else: - new_args[ct] = arg - ct += 1 - - return super(Function, cls).__new__(cls, *new_args, **options) - - def __call__(self, *args, **options): - var = self.variables - - if len(args) != len(var): - raise NotImplementedError( - "Incorrect number of arguments to function!") - - ct = 0 - #If the passed value is outside the specified bounds, return 0 - for v in var: - lower, upper = self.limits[v] - - #Do the comparison to limits only if the passed symbol is actually - #a symbol present in the limits; - #Had problems with a comparison of x > L - if isinstance(args[ct], Expr) and \ - not (lower in args[ct].free_symbols - or upper in args[ct].free_symbols): - continue - - if args[ct] < lower or args[ct] > upper: - return 0 - - ct += 1 - - expr = self.expr - - #Allows user to make a call like f(2, 4, m=1, n=1) - for symbol in list(expr.free_symbols): - if str(symbol) in list(options.keys()): - val = options[str(symbol)] - expr = expr.subs(symbol, val) - - return expr.subs(list(zip(var, args))) - - def _eval_derivative(self, symbol): - expr = self.expr - deriv = expr._eval_derivative(symbol) - - return Wavefunction(deriv, *self.args[1:]) - - def _eval_conjugate(self): - return Wavefunction(conjugate(self.expr), *self.args[1:]) - - def _eval_transpose(self): - return self - - @property - def free_symbols(self): - return self.expr.free_symbols - - @property -
    [docs] def is_commutative(self): - """ - Override Function's is_commutative so that order is preserved in - represented expressions - """ - return False -
    - @classmethod - def eval(self, *args): - return None - - @property -
    [docs] def variables(self): - """ - Return the coordinates which the wavefunction depends on - - Examples - ======== - - >>> from sympy.physics.quantum.state import Wavefunction - >>> from sympy import symbols - >>> x,y = symbols('x,y') - >>> f = Wavefunction(x*y, x, y) - >>> f.variables - (x, y) - >>> g = Wavefunction(x*y, x) - >>> g.variables - (x,) - - """ - var = [g[0] if isinstance(g, Tuple) else g for g in self._args[1:]] - return tuple(var) -
    - @property -
    [docs] def limits(self): - """ - Return the limits of the coordinates which the w.f. depends on If no - limits are specified, defaults to ``(-oo, oo)``. - - Examples - ======== - - >>> from sympy.physics.quantum.state import Wavefunction - >>> from sympy import symbols - >>> x, y = symbols('x, y') - >>> f = Wavefunction(x**2, (x, 0, 1)) - >>> f.limits - {x: (0, 1)} - >>> f = Wavefunction(x**2, x) - >>> f.limits - {x: (-oo, oo)} - >>> f = Wavefunction(x**2 + y**2, x, (y, -1, 2)) - >>> f.limits - {x: (-oo, oo), y: (-1, 2)} - - """ - limits = [(g[1], g[2]) if isinstance(g, Tuple) else (-oo, oo) - for g in self._args[1:]] - return dict(list(zip(self.variables, tuple(limits)))) -
    - @property -
    [docs] def expr(self): - """ - Return the expression which is the functional form of the Wavefunction - - Examples - ======== - - >>> from sympy.physics.quantum.state import Wavefunction - >>> from sympy import symbols - >>> x, y = symbols('x, y') - >>> f = Wavefunction(x**2, x) - >>> f.expr - x**2 - - """ - return self._args[0] -
    - @property -
    [docs] def is_normalized(self): - """ - Returns true if the Wavefunction is properly normalized - - Examples - ======== - - >>> from sympy import symbols, pi - >>> from sympy.functions import sqrt, sin - >>> from sympy.physics.quantum.state import Wavefunction - >>> x, L = symbols('x,L', positive=True) - >>> n = symbols('n', integer=True) - >>> g = sqrt(2/L)*sin(n*pi*x/L) - >>> f = Wavefunction(g, (x, 0, L)) - >>> f.is_normalized - True - - """ - - return (self.norm == 1.0) -
    - @property - @cacheit -
    [docs] def norm(self): - """ - Return the normalization of the specified functional form. - - This function integrates over the coordinates of the Wavefunction, with - the bounds specified. - - Examples - ======== - - >>> from sympy import symbols, pi - >>> from sympy.functions import sqrt, sin - >>> from sympy.physics.quantum.state import Wavefunction - >>> x, L = symbols('x,L', positive=True) - >>> n = symbols('n', integer=True) - >>> g = sqrt(2/L)*sin(n*pi*x/L) - >>> f = Wavefunction(g, (x, 0, L)) - >>> f.norm - 1 - >>> g = sin(n*pi*x/L) - >>> f = Wavefunction(g, (x, 0, L)) - >>> f.norm - sqrt(2)*sqrt(L)/2 - - """ - - exp = self.expr*conjugate(self.expr) - var = self.variables - limits = self.limits - - for v in var: - curr_limits = limits[v] - exp = integrate(exp, (v, curr_limits[0], curr_limits[1])) - - return sqrt(exp) -
    -
    [docs] def normalize(self): - """ - Return a normalized version of the Wavefunction - - Examples - ======== - - >>> from sympy import symbols, pi - >>> from sympy.functions import sqrt, sin - >>> from sympy.physics.quantum.state import Wavefunction - >>> x, L = symbols('x,L', real=True) - >>> n = symbols('n', integer=True) - >>> g = sin(n*pi*x/L) - >>> f = Wavefunction(g, (x, 0, L)) - >>> f.normalize() - Wavefunction(sqrt(2)*sin(pi*n*x/L)/sqrt(L), (x, 0, L)) - - """ - const = self.norm - - if const == oo: - raise NotImplementedError("The function is not normalizable!") - else: - return Wavefunction((const)**(-1)*self.expr, *self.args[1:]) -
    -
    [docs] def prob(self): - """ - Return the absolute magnitude of the w.f., `|\psi(x)|^2` - - Examples - ======== - - >>> from sympy import symbols, pi - >>> from sympy.functions import sqrt, sin - >>> from sympy.physics.quantum.state import Wavefunction - >>> x, L = symbols('x,L', real=True) - >>> n = symbols('n', integer=True) - >>> g = sin(n*pi*x/L) - >>> f = Wavefunction(g, (x, 0, L)) - >>> f.prob() - Wavefunction(sin(pi*n*x/L)**2, x) - - """ - - return Wavefunction(self.expr*conjugate(self.expr), *self.variables)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/quantum/tensorproduct.html b/dev-py3k/_modules/sympy/physics/quantum/tensorproduct.html deleted file mode 100644 index 8893f817c3c..00000000000 --- a/dev-py3k/_modules/sympy/physics/quantum/tensorproduct.html +++ /dev/null @@ -1,444 +0,0 @@ - - - - - - - - - - sympy.physics.quantum.tensorproduct — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.quantum.tensorproduct

    -"""Abstract tensor product."""
    -
    -from sympy import Expr, Add, Mul, Matrix, Pow, sympify
    -from sympy.printing.pretty.stringpict import prettyForm
    -
    -from sympy.physics.quantum.qexpr import QuantumError
    -from sympy.physics.quantum.dagger import Dagger
    -from sympy.physics.quantum.commutator import Commutator
    -from sympy.physics.quantum.anticommutator import AntiCommutator
    -from sympy.physics.quantum.matrixutils import (
    -    numpy_ndarray,
    -    scipy_sparse_matrix,
    -    matrix_tensor_product
    -)
    -from sympy.core.trace import Tr
    -
    -__all__ = [
    -    'TensorProduct',
    -    'tensor_product_simp'
    -]
    -
    -#-----------------------------------------------------------------------------
    -# Tensor product
    -#-----------------------------------------------------------------------------
    -
    -
    -
    [docs]class TensorProduct(Expr): - """The tensor product of two or more arguments. - - For matrices, this uses ``matrix_tensor_product`` to compute the Kronecker - or tensor product matrix. For other objects a symbolic ``TensorProduct`` - instance is returned. The tensor product is a non-commutative - multiplication that is used primarily with operators and states in quantum - mechanics. - - Currently, the tensor product distinguishes between commutative and non- - commutative arguments. Commutative arguments are assumed to be scalars and - are pulled out in front of the ``TensorProduct``. Non-commutative arguments - remain in the resulting ``TensorProduct``. - - Parameters - ========== - - args : tuple - A sequence of the objects to take the tensor product of. - - Examples - ======== - - Start with a simple tensor product of sympy matrices:: - - >>> from sympy import I, Matrix, symbols - >>> from sympy.physics.quantum import TensorProduct - - >>> m1 = Matrix([[1,2],[3,4]]) - >>> m2 = Matrix([[1,0],[0,1]]) - >>> TensorProduct(m1, m2) - [1, 0, 2, 0] - [0, 1, 0, 2] - [3, 0, 4, 0] - [0, 3, 0, 4] - >>> TensorProduct(m2, m1) - [1, 2, 0, 0] - [3, 4, 0, 0] - [0, 0, 1, 2] - [0, 0, 3, 4] - - We can also construct tensor products of non-commutative symbols: - - >>> from sympy import Symbol - >>> A = Symbol('A',commutative=False) - >>> B = Symbol('B',commutative=False) - >>> tp = TensorProduct(A, B) - >>> tp - AxB - - We can take the dagger of a tensor product (note the order does NOT reverse - like the dagger of a normal product): - - >>> from sympy.physics.quantum import Dagger - >>> Dagger(tp) - Dagger(A)xDagger(B) - - Expand can be used to distribute a tensor product across addition: - - >>> C = Symbol('C',commutative=False) - >>> tp = TensorProduct(A+B,C) - >>> tp - (A + B)xC - >>> tp.expand(tensorproduct=True) - AxC + BxC - """ - is_commutative = False - - def __new__(cls, *args): - if isinstance(args[0], (Matrix, numpy_ndarray, scipy_sparse_matrix)): - return matrix_tensor_product(*args) - c_part, new_args = cls.flatten(sympify(args)) - c_part = Mul(*c_part) - if len(new_args) == 0: - return c_part - elif len(new_args) == 1: - return c_part*new_args[0] - else: - tp = Expr.__new__(cls, *new_args) - return c_part*tp - - @classmethod - def flatten(cls, args): - # TODO: disallow nested TensorProducts. - c_part = [] - nc_parts = [] - for arg in args: - cp, ncp = arg.args_cnc() - c_part.extend(list(cp)) - nc_parts.append(Mul._from_args(ncp)) - return c_part, nc_parts - - def _eval_adjoint(self): - return TensorProduct(*[Dagger(i) for i in self.args]) - - def _eval_rewrite(self, pattern, rule, **hints): - sargs = self.args - terms = [ t._eval_rewrite(pattern, rule, **hints) for t in sargs] - return TensorProduct(*terms).expand(tensorproduct=True) - - def _sympystr(self, printer, *args): - from sympy.printing.str import sstr - length = len(self.args) - s = '' - for i in range(length): - if isinstance(self.args[i], (Add, Pow, Mul)): - s = s + '(' - s = s + sstr(self.args[i]) - if isinstance(self.args[i], (Add, Pow, Mul)): - s = s + ')' - if i != length - 1: - s = s + 'x' - return s - - def _pretty(self, printer, *args): - length = len(self.args) - pform = printer._print('', *args) - for i in range(length): - next_pform = printer._print(self.args[i], *args) - if isinstance(self.args[i], (Add, Mul)): - next_pform = prettyForm( - *next_pform.parens(left='(', right=')') - ) - pform = prettyForm(*pform.right(next_pform)) - if i != length - 1: - if printer._use_unicode: - pform = prettyForm(*pform.right('\u2a02' + ' ')) - else: - pform = prettyForm(*pform.right('x' + ' ')) - return pform - - def _latex(self, printer, *args): - length = len(self.args) - s = '' - for i in range(length): - if isinstance(self.args[i], (Add, Mul)): - s = s + '\\left(' - # The extra {} brackets are needed to get matplotlib's latex - # rendered to render this properly. - s = s + '{' + printer._print(self.args[i], *args) + '}' - if isinstance(self.args[i], (Add, Mul)): - s = s + '\\right)' - if i != length - 1: - s = s + '\\otimes ' - return s - - def doit(self, **hints): - return TensorProduct(*[item.doit(**hints) for item in self.args]) - - def _eval_expand_tensorproduct(self, **hints): - """Distribute TensorProducts across addition.""" - args = self.args - add_args = [] - stop = False - for i in range(len(args)): - if isinstance(args[i], Add): - for aa in args[i].args: - tp = TensorProduct(*args[:i] + (aa,) + args[i + 1:]) - if isinstance(tp, TensorProduct): - tp = tp._eval_expand_tensorproduct() - add_args.append(tp) - break - - if add_args: - return Add(*add_args) - else: - return self - - def _eval_trace(self, **kwargs): - indices = kwargs.get('indices', None) - exp = tensor_product_simp(self) - - if indices is None or len(indices) == 0: - return Mul(*[Tr(arg).doit() for arg in exp.args]) - else: - return Mul(*[Tr(value).doit() if idx in indices else value - for idx, value in enumerate(exp.args)]) - -
    -def tensor_product_simp_Mul(e): - """Simplify a Mul with TensorProducts. - - Current the main use of this is to simplify a ``Mul`` of ``TensorProduct``s - to a ``TensorProduct`` of ``Muls``. It currently only works for relatively - simple cases where the initial ``Mul`` only has scalars and raw - ``TensorProduct``s, not ``Add``, ``Pow``, ``Commutator``s of - ``TensorProduct``s. - - Parameters - ========== - - e : Expr - A ``Mul`` of ``TensorProduct``s to be simplified. - - Returns - ======= - - e : Expr - A ``TensorProduct`` of ``Mul``s. - - Examples - ======== - - This is an example of the type of simplification that this function - performs:: - - >>> from sympy.physics.quantum.tensorproduct import \ - tensor_product_simp_Mul, TensorProduct - >>> from sympy import Symbol - >>> A = Symbol('A',commutative=False) - >>> B = Symbol('B',commutative=False) - >>> C = Symbol('C',commutative=False) - >>> D = Symbol('D',commutative=False) - >>> e = TensorProduct(A,B)*TensorProduct(C,D) - >>> e - AxB*CxD - >>> tensor_product_simp_Mul(e) - (A*C)x(B*D) - - """ - # TODO: This won't work with Muls that have other composites of - # TensorProducts, like an Add, Pow, Commutator, etc. - # TODO: This only works for the equivalent of single Qbit gates. - if not isinstance(e, Mul): - return e - c_part, nc_part = e.args_cnc() - n_nc = len(nc_part) - if n_nc == 0 or n_nc == 1: - return e - elif e.has(TensorProduct): - current = nc_part[0] - if not isinstance(current, TensorProduct): - raise TypeError('TensorProduct expected, got: %r' % current) - n_terms = len(current.args) - new_args = list(current.args) - for next in nc_part[1:]: - # TODO: check the hilbert spaces of next and current here. - if isinstance(next, TensorProduct): - if n_terms != len(next.args): - raise QuantumError( - 'TensorProducts of different lengths: %r and %r' % - (current, next) - ) - for i in range(len(new_args)): - new_args[i] = new_args[i]*next.args[i] - else: - # this won't quite work as we don't want next in the TensorProduct - for i in range(len(new_args)): - new_args[i] = new_args[i]*next - current = next - return Mul(*c_part)*TensorProduct(*new_args) - else: - return e - - -
    [docs]def tensor_product_simp(e, **hints): - """Try to simplify and combine TensorProducts. - - In general this will try to pull expressions inside of ``TensorProducts``. - It currently only works for relatively simple cases where the products have - only scalars, raw ``TensorProducts``, not ``Add``, ``Pow``, ``Commutators`` - of ``TensorProducts``. It is best to see what it does by showing examples. - - Examples - ======== - - >>> from sympy.physics.quantum import tensor_product_simp - >>> from sympy.physics.quantum import TensorProduct - >>> from sympy import Symbol - >>> A = Symbol('A',commutative=False) - >>> B = Symbol('B',commutative=False) - >>> C = Symbol('C',commutative=False) - >>> D = Symbol('D',commutative=False) - - First see what happens to products of tensor products: - - >>> e = TensorProduct(A,B)*TensorProduct(C,D) - >>> e - AxB*CxD - >>> tensor_product_simp(e) - (A*C)x(B*D) - - This is the core logic of this function, and it works inside, powers, sums, - commutators and anticommutators as well: - - >>> tensor_product_simp(e**2) - (A*C)x(B*D)**2 - - """ - if isinstance(e, Add): - return Add(*[tensor_product_simp(arg) for arg in e.args]) - elif isinstance(e, Pow): - return tensor_product_simp(e.base)**e.exp - elif isinstance(e, Mul): - return tensor_product_simp_Mul(e) - elif isinstance(e, Commutator): - return Commutator(*[tensor_product_simp(arg) for arg in e.args]) - elif isinstance(e, AntiCommutator): - return AntiCommutator(*[tensor_product_simp(arg) for arg in e.args]) - else: - return e
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/secondquant.html b/dev-py3k/_modules/sympy/physics/secondquant.html deleted file mode 100644 index b7a1a61a843..00000000000 --- a/dev-py3k/_modules/sympy/physics/secondquant.html +++ /dev/null @@ -1,3179 +0,0 @@ - - - - - - - - - - sympy.physics.secondquant — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.secondquant

    -"""
    -Second quantization operators and states for bosons.
    -
    -This follow the formulation of Fetter and Welecka, "Quantum Theory
    -of Many-Particle Systems."
    -"""
    -from collections import defaultdict
    -
    -from sympy import (Add, Basic, cacheit, Dummy, Expr, Function, I,
    -                   KroneckerDelta, Mul, Pow, S, sqrt, Symbol, sympify, Tuple,
    -                   zeros)
    -from sympy.core.compatibility import reduce
    -from sympy.printing.str import StrPrinter
    -
    -from sympy.physics.quantum.qexpr import split_commutative_parts
    -from sympy.core.compatibility import reduce
    -from sympy.utilities.iterables import has_dups
    -from sympy.utilities import default_sort_key
    -from functools import reduce
    -
    -__all__ = [
    -    'Dagger',
    -    'KroneckerDelta',
    -    'BosonicOperator',
    -    'AnnihilateBoson',
    -    'CreateBoson',
    -    'AnnihilateFermion',
    -    'CreateFermion',
    -    'FockState',
    -    'FockStateBra',
    -    'FockStateKet',
    -    'FockStateBosonKet',
    -    'FockStateBosonBra',
    -    'BBra',
    -    'BKet',
    -    'FBra',
    -    'FKet',
    -    'F',
    -    'Fd',
    -    'B',
    -    'Bd',
    -    'apply_operators',
    -    'InnerProduct',
    -    'BosonicBasis',
    -    'VarBosonicBasis',
    -    'FixedBosonicBasis',
    -    'Commutator',
    -    'matrix_rep',
    -    'contraction',
    -    'wicks',
    -    'NO',
    -    'evaluate_deltas',
    -    'AntiSymmetricTensor',
    -    'substitute_dummies',
    -    'PermutationOperator',
    -    'simplify_index_permutations',
    -]
    -
    -
    -class SecondQuantizationError(Exception):
    -    pass
    -
    -
    -class AppliesOnlyToSymbolicIndex(SecondQuantizationError):
    -    pass
    -
    -
    -class ContractionAppliesOnlyToFermions(SecondQuantizationError):
    -    pass
    -
    -
    -class ViolationOfPauliPrinciple(SecondQuantizationError):
    -    pass
    -
    -
    -class SubstitutionOfAmbigousOperatorFailed(SecondQuantizationError):
    -    pass
    -
    -
    -class WicksTheoremDoesNotApply(SecondQuantizationError):
    -    pass
    -
    -
    -
    [docs]class Dagger(Expr): - """ - Hermitian conjugate of creation/annihilation operators. - - Examples - ======== - - >>> from sympy import I - >>> from sympy.physics.secondquant import Dagger, B, Bd - >>> Dagger(2*I) - -2*I - >>> Dagger(B(0)) - CreateBoson(0) - >>> Dagger(Bd(0)) - AnnihilateBoson(0) - - """ - - def __new__(cls, arg): - arg = sympify(arg) - r = cls.eval(arg) - if isinstance(r, Basic): - return r - obj = Basic.__new__(cls, arg) - return obj - - @classmethod -
    [docs] def eval(cls, arg): - """ - Evaluates the Dagger instance. - - Examples - ======== - - >>> from sympy import I - >>> from sympy.physics.secondquant import Dagger, B, Bd - >>> Dagger(2*I) - -2*I - >>> Dagger(B(0)) - CreateBoson(0) - >>> Dagger(Bd(0)) - AnnihilateBoson(0) - - The eval() method is called automatically. - - """ - try: - d = arg._dagger_() - except AttributeError: - if isinstance(arg, Basic): - if arg.is_Add: - return Add(*tuple(map(Dagger, arg.args))) - if arg.is_Mul: - return Mul(*tuple(map(Dagger, reversed(arg.args)))) - if arg.is_Number: - return arg - if arg.is_Pow: - return Pow(Dagger(arg.args[0]), arg.args[1]) - if arg == I: - return -arg - else: - return None - else: - return d -
    - def _dagger_(self): - return self.args[0] - -
    -class TensorSymbol(Expr): - - is_commutative = True - - -
    [docs]class AntiSymmetricTensor(TensorSymbol): - """Stores upper and lower indices in separate Tuple's. - - Each group of indices is assumed to be antisymmetric. - - Examples - ======== - - >>> from sympy import symbols - >>> from sympy.physics.secondquant import AntiSymmetricTensor - >>> i, j = symbols('i j', below_fermi=True) - >>> a, b = symbols('a b', above_fermi=True) - >>> AntiSymmetricTensor('v', (a, i), (b, j)) - AntiSymmetricTensor(v, (a, i), (b, j)) - >>> AntiSymmetricTensor('v', (i, a), (b, j)) - -AntiSymmetricTensor(v, (a, i), (b, j)) - - As you can see, the indices are automatically sorted to a canonical form. - - """ - - nargs = 3 - - def __new__(cls, symbol, upper, lower): - - try: - upper, signu = _sort_anticommuting_fermions( - upper, key=cls._sortkey) - lower, signl = _sort_anticommuting_fermions( - lower, key=cls._sortkey) - - except ViolationOfPauliPrinciple: - return S.Zero - - symbol = sympify(symbol) - upper = Tuple(*upper) - lower = Tuple(*lower) - - if (signu + signl) % 2: - return -TensorSymbol.__new__(cls, symbol, upper, lower) - else: - - return TensorSymbol.__new__(cls, symbol, upper, lower) - - @classmethod - def _sortkey(cls, index): - """Key for sorting of indices. - - particle < hole < general - - FIXME: This is a bottle-neck, can we do it faster? - """ - h = hash(index) - if isinstance(index, Dummy): - if index.assumptions0.get('above_fermi'): - return (20, h) - elif index.assumptions0.get('below_fermi'): - return (21, h) - else: - return (22, h) - - if index.assumptions0.get('above_fermi'): - return (10, h) - elif index.assumptions0.get('below_fermi'): - return (11, h) - else: - return (12, h) - - def _latex(self, printer): - return "%s^{%s}_{%s}" % ( - self.symbol, - "".join([ i.name for i in self.args[1]]), - "".join([ i.name for i in self.args[2]]) - ) - - @property -
    [docs] def symbol(self): - """ - Returns the symbol of the tensor. - - Examples - ======== - - >>> from sympy import symbols - >>> from sympy.physics.secondquant import AntiSymmetricTensor - >>> i, j = symbols('i,j', below_fermi=True) - >>> a, b = symbols('a,b', above_fermi=True) - >>> AntiSymmetricTensor('v', (a, i), (b, j)) - AntiSymmetricTensor(v, (a, i), (b, j)) - >>> AntiSymmetricTensor('v', (a, i), (b, j)).symbol - v - - """ - return self.args[0] -
    - @property -
    [docs] def upper(self): - """ - Returns the upper indices. - - Examples - ======== - - >>> from sympy import symbols - >>> from sympy.physics.secondquant import AntiSymmetricTensor - >>> i, j = symbols('i,j', below_fermi=True) - >>> a, b = symbols('a,b', above_fermi=True) - >>> AntiSymmetricTensor('v', (a, i), (b, j)) - AntiSymmetricTensor(v, (a, i), (b, j)) - >>> AntiSymmetricTensor('v', (a, i), (b, j)).upper - (a, i) - - - """ - return self.args[1] -
    - @property -
    [docs] def lower(self): - """ - Returns the lower indices. - - Examples - ======== - - >>> from sympy import symbols - >>> from sympy.physics.secondquant import AntiSymmetricTensor - >>> i, j = symbols('i,j', below_fermi=True) - >>> a, b = symbols('a,b', above_fermi=True) - >>> AntiSymmetricTensor('v', (a, i), (b, j)) - AntiSymmetricTensor(v, (a, i), (b, j)) - >>> AntiSymmetricTensor('v', (a, i), (b, j)).lower - (b, j) - - """ - return self.args[2] -
    - def __str__(self): - return "%s(%s,%s)" % self.args - -
    [docs] def doit(self, **kw_args): - """ - Returns self. - - Examples - ======== - - >>> from sympy import symbols - >>> from sympy.physics.secondquant import AntiSymmetricTensor - >>> i, j = symbols('i,j', below_fermi=True) - >>> a, b = symbols('a,b', above_fermi=True) - >>> AntiSymmetricTensor('v', (a, i), (b, j)).doit() - AntiSymmetricTensor(v, (a, i), (b, j)) - """ - return self - -
    -class SqOperator(Expr): - """ - Base class for Second Quantization operators. - """ - - op_symbol = 'sq' - - is_commutative = False - - def __new__(cls, k): - obj = Basic.__new__(cls, sympify(k)) - return obj - - @property - def state(self): - """ - Returns the state index related to this operator. - - >>> from sympy import Symbol - >>> from sympy.physics.secondquant import F, Fd, B, Bd - >>> p = Symbol('p') - >>> F(p).state - p - >>> Fd(p).state - p - >>> B(p).state - p - >>> Bd(p).state - p - - """ - return self.args[0] - - @property - def is_symbolic(self): - """ - Returns True if the state is a symbol (as opposed to a number). - - >>> from sympy import Symbol - >>> from sympy.physics.secondquant import F - >>> p = Symbol('p') - >>> F(p).is_symbolic - True - >>> F(1).is_symbolic - False - - """ - if self.state.is_Integer: - return False - else: - return True - - def doit(self, **kw_args): - """ - FIXME: hack to prevent crash further up... - """ - return self - - def __repr__(self): - return NotImplemented - - def __str__(self): - return "%s(%r)" % (self.op_symbol, self.state) - - def apply_operator(self, state): - """ - Applies an operator to itself. - """ - raise NotImplementedError('implement apply_operator in a subclass') - - -class BosonicOperator(SqOperator): - pass - - -class Annihilator(SqOperator): - pass - - -class Creator(SqOperator): - pass - - -
    [docs]class AnnihilateBoson(BosonicOperator, Annihilator): - """ - Bosonic annihilation operator. - - Examples - ======== - - >>> from sympy.physics.secondquant import B - >>> from sympy.abc import x - >>> B(x) - AnnihilateBoson(x) - """ - - op_symbol = 'b' - - def _dagger_(self): - return CreateBoson(self.state) - -
    [docs] def apply_operator(self, state): - """ - Apply state to self if self is not symbolic and state is a FockStateKet, else - multiply self by state. - - Examples - ======== - - >>> from sympy.physics.secondquant import B, BKet - >>> from sympy.abc import x, y, n - >>> B(x).apply_operator(y) - y*AnnihilateBoson(x) - >>> B(0).apply_operator(BKet((n,))) - sqrt(n)*FockStateBosonKet((n - 1,)) - - """ - if not self.is_symbolic and isinstance(state, FockStateKet): - element = self.state - amp = sqrt(state[element]) - return amp*state.down(element) - else: - return Mul(self, state) -
    - def __repr__(self): - return "AnnihilateBoson(%s)" % self.state - -
    -
    [docs]class CreateBoson(BosonicOperator, Creator): - """ - Bosonic creation operator. - """ - - op_symbol = 'b+' - - def _dagger_(self): - return AnnihilateBoson(self.state) - -
    [docs] def apply_operator(self, state): - """ - Apply state to self if self is not symbolic and state is a FockStateKet, else - multiply self by state. - - Examples - ======== - - >>> from sympy.physics.secondquant import B, Dagger, BKet - >>> from sympy.abc import x, y, n - >>> Dagger(B(x)).apply_operator(y) - y*CreateBoson(x) - >>> B(0).apply_operator(BKet((n,))) - sqrt(n)*FockStateBosonKet((n - 1,)) - """ - if not self.is_symbolic and isinstance(state, FockStateKet): - element = self.state - amp = sqrt(state[element] + 1) - return amp*state.up(element) - else: - return Mul(self, state) -
    - def __repr__(self): - return "CreateBoson(%s)" % self.state -
    -B = AnnihilateBoson -Bd = CreateBoson - - -class FermionicOperator(SqOperator): - - @property - def is_restricted(self): - """ - Is this FermionicOperator restricted with respect to fermi level? - - Return values: - 1 : restricted to orbits above fermi - 0 : no restriction - -1 : restricted to orbits below fermi - - >>> from sympy import Symbol - >>> from sympy.physics.secondquant import F, Fd - >>> a = Symbol('a', above_fermi=True) - >>> i = Symbol('i', below_fermi=True) - >>> p = Symbol('p') - - >>> F(a).is_restricted - 1 - >>> Fd(a).is_restricted - 1 - >>> F(i).is_restricted - -1 - >>> Fd(i).is_restricted - -1 - >>> F(p).is_restricted - 0 - >>> Fd(p).is_restricted - 0 - - """ - ass = self.args[0].assumptions0 - if ass.get("below_fermi"): - return -1 - if ass.get("above_fermi"): - return 1 - return 0 - - @property - def is_above_fermi(self): - """ - Does the index of this FermionicOperator allow values above fermi? - - - >>> from sympy import Symbol - >>> from sympy.physics.secondquant import F - >>> a = Symbol('a', above_fermi=True) - >>> i = Symbol('i', below_fermi=True) - >>> p = Symbol('p') - - >>> F(a).is_above_fermi - True - >>> F(i).is_above_fermi - False - >>> F(p).is_above_fermi - True - - The same applies to creation operators Fd - - """ - return not self.args[0].assumptions0.get("below_fermi") - - @property - def is_below_fermi(self): - """ - Does the index of this FermionicOperator allow values below fermi? - - >>> from sympy import Symbol - >>> from sympy.physics.secondquant import F - >>> a = Symbol('a', above_fermi=True) - >>> i = Symbol('i', below_fermi=True) - >>> p = Symbol('p') - - >>> F(a).is_below_fermi - False - >>> F(i).is_below_fermi - True - >>> F(p).is_below_fermi - True - - The same applies to creation operators Fd - - """ - return not self.args[0].assumptions0.get("above_fermi") - - @property - def is_only_below_fermi(self): - """ - Is the index of this FermionicOperator restricted to values below fermi? - - >>> from sympy import Symbol - >>> from sympy.physics.secondquant import F - >>> a = Symbol('a', above_fermi=True) - >>> i = Symbol('i', below_fermi=True) - >>> p = Symbol('p') - - >>> F(a).is_only_below_fermi - False - >>> F(i).is_only_below_fermi - True - >>> F(p).is_only_below_fermi - False - - The same applies to creation operators Fd - """ - return self.is_below_fermi and not self.is_above_fermi - - @property - def is_only_above_fermi(self): - """ - Is the index of this FermionicOperator restricted to values above fermi? - - >>> from sympy import Symbol - >>> from sympy.physics.secondquant import F - >>> a = Symbol('a', above_fermi=True) - >>> i = Symbol('i', below_fermi=True) - >>> p = Symbol('p') - - >>> F(a).is_only_above_fermi - True - >>> F(i).is_only_above_fermi - False - >>> F(p).is_only_above_fermi - False - - The same applies to creation operators Fd - """ - return self.is_above_fermi and not self.is_below_fermi - - def _sortkey(self): - h = hash(self) - label = str(self.args[0]) - - if self.is_only_q_creator: - return 1, label, h - if self.is_only_q_annihilator: - return 4, label, h - if isinstance(self, Annihilator): - return 3, label, h - if isinstance(self, Creator): - return 2, label, h - - -
    [docs]class AnnihilateFermion(FermionicOperator, Annihilator): - """ - Fermionic annihilation operator. - """ - - op_symbol = 'f' - - def _dagger_(self): - return CreateFermion(self.state) - -
    [docs] def apply_operator(self, state): - """ - Apply state to self if self is not symbolic and state is a FockStateKet, else - multiply self by state. - - Examples - ======== - - >>> from sympy.physics.secondquant import B, Dagger, BKet - >>> from sympy.abc import x, y, n - >>> Dagger(B(x)).apply_operator(y) - y*CreateBoson(x) - >>> B(0).apply_operator(BKet((n,))) - sqrt(n)*FockStateBosonKet((n - 1,)) - """ - if isinstance(state, FockStateFermionKet): - element = self.state - return state.down(element) - - elif isinstance(state, Mul): - c_part, nc_part = state.args_cnc() - if isinstance(nc_part[0], FockStateFermionKet): - element = self.state - return Mul(*(c_part + [nc_part[0].down(element)] + nc_part[1:])) - else: - return Mul(self, state) - - else: - return Mul(self, state) -
    - @property -
    [docs] def is_q_creator(self): - """ - Can we create a quasi-particle? (create hole or create particle) - If so, would that be above or below the fermi surface? - - >>> from sympy import Symbol - >>> from sympy.physics.secondquant import F - >>> a = Symbol('a', above_fermi=True) - >>> i = Symbol('i', below_fermi=True) - >>> p = Symbol('p') - - >>> F(a).is_q_creator - 0 - >>> F(i).is_q_creator - -1 - >>> F(p).is_q_creator - -1 - - """ - if self.is_below_fermi: - return -1 - return 0 -
    - @property -
    [docs] def is_q_annihilator(self): - """ - Can we destroy a quasi-particle? (annihilate hole or annihilate particle) - If so, would that be above or below the fermi surface? - - >>> from sympy import Symbol - >>> from sympy.physics.secondquant import F - >>> a = Symbol('a', above_fermi=1) - >>> i = Symbol('i', below_fermi=1) - >>> p = Symbol('p') - - >>> F(a).is_q_annihilator - 1 - >>> F(i).is_q_annihilator - 0 - >>> F(p).is_q_annihilator - 1 - - """ - if self.is_above_fermi: - return 1 - return 0 -
    - @property -
    [docs] def is_only_q_creator(self): - """ - Always create a quasi-particle? (create hole or create particle) - - - >>> from sympy import Symbol - >>> from sympy.physics.secondquant import F - >>> a = Symbol('a', above_fermi=True) - >>> i = Symbol('i', below_fermi=True) - >>> p = Symbol('p') - - >>> F(a).is_only_q_creator - False - >>> F(i).is_only_q_creator - True - >>> F(p).is_only_q_creator - False - - """ - return self.is_only_below_fermi -
    - @property -
    [docs] def is_only_q_annihilator(self): - """ - Always destroy a quasi-particle? (annihilate hole or annihilate particle) - - >>> from sympy import Symbol - >>> from sympy.physics.secondquant import F - >>> a = Symbol('a', above_fermi=True) - >>> i = Symbol('i', below_fermi=True) - >>> p = Symbol('p') - - >>> F(a).is_only_q_annihilator - True - >>> F(i).is_only_q_annihilator - False - >>> F(p).is_only_q_annihilator - False - - """ - return self.is_only_above_fermi -
    - def __repr__(self): - return "AnnihilateFermion(%s)" % self.state - - def _latex(self, printer): - return "a_{%s}" % self.state.name - -
    -
    [docs]class CreateFermion(FermionicOperator, Creator): - """ - Fermionic creation operator. - """ - - op_symbol = 'f+' - - def _dagger_(self): - return AnnihilateFermion(self.state) - -
    [docs] def apply_operator(self, state): - """ - Apply state to self if self is not symbolic and state is a FockStateKet, else - multiply self by state. - - Examples - ======== - - >>> from sympy.physics.secondquant import B, Dagger, BKet - >>> from sympy.abc import x, y, n - >>> Dagger(B(x)).apply_operator(y) - y*CreateBoson(x) - >>> B(0).apply_operator(BKet((n,))) - sqrt(n)*FockStateBosonKet((n - 1,)) - """ - if isinstance(state, FockStateFermionKet): - element = self.state - return state.up(element) - - elif isinstance(state, Mul): - c_part, nc_part = state.args_cnc() - if isinstance(nc_part[0], FockStateFermionKet): - element = self.state - return Mul(*(c_part + [nc_part[0].up(element)] + nc_part[1:])) - - return Mul(self, state) -
    - @property -
    [docs] def is_q_creator(self): - """ - Can we create a quasi-particle? (create hole or create particle) - If so, would that be above or below the fermi surface? - - >>> from sympy import Symbol - >>> from sympy.physics.secondquant import Fd - >>> a = Symbol('a', above_fermi=True) - >>> i = Symbol('i', below_fermi=True) - >>> p = Symbol('p') - - >>> Fd(a).is_q_creator - 1 - >>> Fd(i).is_q_creator - 0 - >>> Fd(p).is_q_creator - 1 - - """ - if self.is_above_fermi: - return 1 - return 0 -
    - @property -
    [docs] def is_q_annihilator(self): - """ - Can we destroy a quasi-particle? (annihilate hole or annihilate particle) - If so, would that be above or below the fermi surface? - - >>> from sympy import Symbol - >>> from sympy.physics.secondquant import Fd - >>> a = Symbol('a', above_fermi=1) - >>> i = Symbol('i', below_fermi=1) - >>> p = Symbol('p') - - >>> Fd(a).is_q_annihilator - 0 - >>> Fd(i).is_q_annihilator - -1 - >>> Fd(p).is_q_annihilator - -1 - - """ - if self.is_below_fermi: - return -1 - return 0 -
    - @property -
    [docs] def is_only_q_creator(self): - """ - Always create a quasi-particle? (create hole or create particle) - - >>> from sympy import Symbol - >>> from sympy.physics.secondquant import Fd - >>> a = Symbol('a', above_fermi=True) - >>> i = Symbol('i', below_fermi=True) - >>> p = Symbol('p') - - >>> Fd(a).is_only_q_creator - True - >>> Fd(i).is_only_q_creator - False - >>> Fd(p).is_only_q_creator - False - - """ - return self.is_only_above_fermi -
    - @property -
    [docs] def is_only_q_annihilator(self): - """ - Always destroy a quasi-particle? (annihilate hole or annihilate particle) - - >>> from sympy import Symbol - >>> from sympy.physics.secondquant import Fd - >>> a = Symbol('a', above_fermi=True) - >>> i = Symbol('i', below_fermi=True) - >>> p = Symbol('p') - - >>> Fd(a).is_only_q_annihilator - False - >>> Fd(i).is_only_q_annihilator - True - >>> Fd(p).is_only_q_annihilator - False - - """ - return self.is_only_below_fermi -
    - def __repr__(self): - return "CreateFermion(%s)" % self.state - - def _latex(self, printer): - return "a^\\dagger_{%s}" % self.state.name -
    -Fd = CreateFermion -F = AnnihilateFermion - - -
    [docs]class FockState(Expr): - """ - Many particle Fock state with a sequence of occupation numbers. - - Anywhere you can have a FockState, you can also have S.Zero. - All code must check for this! - - Base class to represent FockStates. - """ - is_commutative = False - - def __new__(cls, occupations): - """ - occupations is a list with two possible meanings: - - - For bosons it is a list of occupation numbers. - Element i is the number of particles in state i. - - - For fermions it is a list of occupied orbits. - Element 0 is the state that was occupied first, element i - is the i'th occupied state. - """ - occupations = list(map(sympify, occupations)) - obj = Basic.__new__(cls, Tuple(*occupations)) - return obj - - def __getitem__(self, i): - i = int(i) - return self.args[0][i] - - def __repr__(self): - return ("FockState(%r)") % (self.args) - - def __str__(self): - return "%s%r%s" % (self.lbracket, self._labels(), self.rbracket) - - def _labels(self): - return self.args[0] - - def __len__(self): - return len(self.args[0]) - -
    -class BosonState(FockState): - """ - Base class for FockStateBoson(Ket/Bra). - """ - - def up(self, i): - """ - Performs the action of a creation operator. - - Examples - ======== - - >>> from sympy.physics.secondquant import BBra - >>> b = BBra([1, 2]) - >>> b - FockStateBosonBra((1, 2)) - >>> b.up(1) - FockStateBosonBra((1, 3)) - """ - i = int(i) - new_occs = list(self.args[0]) - new_occs[i] = new_occs[i] + S.One - return self.__class__(new_occs) - - def down(self, i): - """ - Performs the action of an annihilation operator. - - Examples - ======== - - >>> from sympy.physics.secondquant import BBra - >>> b = BBra([1, 2]) - >>> b - FockStateBosonBra((1, 2)) - >>> b.down(1) - FockStateBosonBra((1, 1)) - """ - i = int(i) - new_occs = list(self.args[0]) - if new_occs[i] == S.Zero: - return S.Zero - else: - new_occs[i] = new_occs[i] - S.One - return self.__class__(new_occs) - - -class FermionState(FockState): - """ - Base class for FockStateFermion(Ket/Bra). - """ - - fermi_level = 0 - - def __new__(cls, occupations, fermi_level=0): - occupations = list(map(sympify, occupations)) - if len(occupations) > 1: - try: - (occupations, sign) = _sort_anticommuting_fermions( - occupations, key=hash) - except ViolationOfPauliPrinciple: - return S.Zero - else: - sign = 0 - - cls.fermi_level = fermi_level - - if cls._count_holes(occupations) > fermi_level: - return S.Zero - - if sign % 2: - return S.NegativeOne*FockState.__new__(cls, occupations) - else: - return FockState.__new__(cls, occupations) - - def up(self, i): - """ - Performs the action of a creation operator. - - If below fermi we try to remove a hole, - if above fermi we try to create a particle. - - if general index p we return Kronecker(p,i)*self - where i is a new symbol with restriction above or below. - - >>> from sympy import Symbol - >>> from sympy.physics.secondquant import FKet - >>> a = Symbol('a', above_fermi=True) - >>> i = Symbol('i', below_fermi=True) - >>> p = Symbol('p') - - >>> FKet([]).up(a) - FockStateFermionKet((a,)) - - A creator acting on vacuum below fermi vanishes - >>> FKet([]).up(i) - 0 - - - """ - present = i in self.args[0] - - if self._only_above_fermi(i): - if present: - return S.Zero - else: - return self._add_orbit(i) - elif self._only_below_fermi(i): - if present: - return self._remove_orbit(i) - else: - return S.Zero - else: - if present: - hole = Dummy("i", below_fermi=True) - return KroneckerDelta(i, hole)*self._remove_orbit(i) - else: - particle = Dummy("a", above_fermi=True) - return KroneckerDelta(i, particle)*self._add_orbit(i) - - def down(self, i): - """ - Performs the action of an annihilation operator. - - If below fermi we try to create a hole, - if above fermi we try to remove a particle. - - if general index p we return Kronecker(p,i)*self - where i is a new symbol with restriction above or below. - - >>> from sympy import Symbol - >>> from sympy.physics.secondquant import FKet - >>> a = Symbol('a', above_fermi=True) - >>> i = Symbol('i', below_fermi=True) - >>> p = Symbol('p') - - An annihilator acting on vacuum above fermi vanishes - >>> FKet([]).down(a) - 0 - - Also below fermi, it vanishes, unless we specify a fermi level > 0 - >>> FKet([]).down(i) - 0 - >>> FKet([],4).down(i) - FockStateFermionKet((i,)) - - """ - present = i in self.args[0] - - if self._only_above_fermi(i): - if present: - return self._remove_orbit(i) - else: - return S.Zero - - elif self._only_below_fermi(i): - if present: - return S.Zero - else: - return self._add_orbit(i) - else: - if present: - hole = Dummy("i", below_fermi=True) - return KroneckerDelta(i, hole)*self._add_orbit(i) - else: - particle = Dummy("a", above_fermi=True) - return KroneckerDelta(i, particle)*self._remove_orbit(i) - - @classmethod - def _only_below_fermi(cls, i): - """ - Tests if given orbit is only below fermi surface. - - If nothing can be concluded we return a conservative False. - """ - if i.is_number: - return i <= cls.fermi_level - if i.assumptions0.get('below_fermi'): - return True - return False - - @classmethod - def _only_above_fermi(cls, i): - """ - Tests if given orbit is only above fermi surface. - - If fermi level has not been set we return True. - If nothing can be concluded we return a conservative False. - """ - if i.is_number: - return i > cls.fermi_level - if i.assumptions0.get('above_fermi'): - return True - return not cls.fermi_level - - def _remove_orbit(self, i): - """ - Removes particle/fills hole in orbit i. No input tests performed here. - """ - new_occs = list(self.args[0]) - pos = new_occs.index(i) - del new_occs[pos] - if (pos) % 2: - return S.NegativeOne*self.__class__(new_occs, self.fermi_level) - else: - return self.__class__(new_occs, self.fermi_level) - - def _add_orbit(self, i): - """ - Adds particle/creates hole in orbit i. No input tests performed here. - """ - return self.__class__((i,) + self.args[0], self.fermi_level) - - @classmethod - def _count_holes(cls, list): - """ - returns number of identified hole states in list. - """ - return len([i for i in list if cls._only_below_fermi(i)]) - - def _negate_holes(self, list): - return tuple([-i if i <= self.fermi_level else i for i in list]) - - def __repr__(self): - if self.fermi_level: - return "FockStateKet(%r, fermi_level=%s)" % (self.args[0], self.fermi_level) - else: - return "FockStateKet(%r)" % (self.args[0],) - - def _labels(self): - return self._negate_holes(self.args[0]) - - -
    [docs]class FockStateKet(FockState): - """ - Representation of a ket. - """ - lbracket = '|' - rbracket = '>' - -
    -
    [docs]class FockStateBra(FockState): - """ - Representation of a bra. - """ - lbracket = '<' - rbracket = '|' - - def __mul__(self, other): - if isinstance(other, FockStateKet): - return InnerProduct(self, other) - else: - return Expr.__mul__(self, other) - -
    -
    [docs]class FockStateBosonKet(BosonState, FockStateKet): - """ - Many particle Fock state with a sequence of occupation numbers. - - Occupation numbers can be any integer >= 0. - - Examples - ======== - - >>> from sympy.physics.secondquant import BKet - >>> BKet([1, 2]) - FockStateBosonKet((1, 2)) - """ - def _dagger_(self): - return FockStateBosonBra(*self.args) - -
    -
    [docs]class FockStateBosonBra(BosonState, FockStateBra): - """ - Describes a collection of BosonBra particles. - - Examples - ======== - - >>> from sympy.physics.secondquant import BBra - >>> BBra([1, 2]) - FockStateBosonBra((1, 2)) - """ - def _dagger_(self): - return FockStateBosonKet(*self.args) - -
    -class FockStateFermionKet(FermionState, FockStateKet): - """ - Many-particle Fock state with a sequence of occupied orbits. - - Each state can only have one particle, so we choose to store a list of - occupied orbits rather than a tuple with occupation numbers (zeros and ones). - - states below fermi level are holes, and are represented by negative labels - in the occupation list. - - For symbolic state labels, the fermi_level caps the number of allowed hole- - states. - - Examples - ======== - - >>> from sympy.physics.secondquant import FKet - >>> FKet([1, 2]) #doctest: +SKIP - FockStateFermionKet((1, 2)) - """ - def _dagger_(self): - return FockStateFermionBra(*self.args) - - -class FockStateFermionBra(FermionState, FockStateBra): - """ - See Also - ======== - - FockStateFermionKet - - Examples - ======== - - >>> from sympy.physics.secondquant import FBra - >>> FBra([1, 2]) #doctest: +SKIP - FockStateFermionBra((1, 2)) - """ - def _dagger_(self): - return FockStateFermionKet(*self.args) - -BBra = FockStateBosonBra -BKet = FockStateBosonKet -FBra = FockStateFermionBra -FKet = FockStateFermionKet - - -def _apply_Mul(m): - """ - Take a Mul instance with operators and apply them to states. - - This method applies all operators with integer state labels - to the actual states. For symbolic state labels, nothing is done. - When inner products of FockStates are encountered (like <a|b>), - they are converted to instances of InnerProduct. - - This does not currently work on double inner products like, - <a|b><c|d>. - - If the argument is not a Mul, it is simply returned as is. - """ - if not isinstance(m, Mul): - return m - c_part, nc_part = m.args_cnc() - n_nc = len(nc_part) - if n_nc == 0 or n_nc == 1: - return m - else: - last = nc_part[-1] - next_to_last = nc_part[-2] - if isinstance(last, FockStateKet): - if isinstance(next_to_last, SqOperator): - if next_to_last.is_symbolic: - return m - else: - result = next_to_last.apply_operator(last) - if result == 0: - return S.Zero - else: - return _apply_Mul(Mul(*(c_part + nc_part[:-2] + [result]))) - elif isinstance(next_to_last, Pow): - if isinstance(next_to_last.base, SqOperator) and \ - next_to_last.exp.is_Integer: - if next_to_last.base.is_symbolic: - return m - else: - result = last - for i in range(next_to_last.exp): - result = next_to_last.base.apply_operator(result) - if result == 0: - break - if result == 0: - return S.Zero - else: - return _apply_Mul(Mul(*(c_part + nc_part[:-2] + [result]))) - else: - return m - elif isinstance(next_to_last, FockStateBra): - result = InnerProduct(next_to_last, last) - if result == 0: - return S.Zero - else: - return _apply_Mul(Mul(*(c_part + nc_part[:-2] + [result]))) - else: - return m - else: - return m - - -
    [docs]def apply_operators(e): - """ - Take a sympy expression with operators and states and apply the operators. - - Examples - ======== - - >>> from sympy.physics.secondquant import apply_operators - >>> from sympy import sympify - >>> apply_operators(sympify(3)+4) - 7 - """ - e = e.expand() - muls = e.atoms(Mul) - subs_list = [(m, _apply_Mul(m)) for m in iter(muls)] - return e.subs(subs_list) - -
    -
    [docs]class InnerProduct(Basic): - """ - An unevaluated inner product between a bra and ket. - - Currently this class just reduces things to a product of - Kronecker Deltas. In the future, we could introduce abstract - states like ``|a>`` and ``|b>``, and leave the inner product unevaluated as - ``<a|b>``. - - """ - is_commutative = True - - def __new__(cls, bra, ket): - assert isinstance(bra, FockStateBra), 'must be a bra' - assert isinstance(ket, FockStateKet), 'must be a key' - return cls.eval(bra, ket) - - @classmethod - def eval(cls, bra, ket): - result = S.One - for i, j in zip(bra.args[0], ket.args[0]): - result *= KroneckerDelta(i, j) - if result == 0: - break - return result - - @property -
    [docs] def bra(self): - """Returns the bra part of the state""" - return self.args[0] -
    - @property -
    [docs] def ket(self): - """Returns the ket part of the state""" - return self.args[1] -
    - def __repr__(self): - sbra = repr(self.bra) - sket = repr(self.ket) - return "%s|%s" % (sbra[:-1], sket[1:]) - - def __str__(self): - return self.__repr__() - -
    -
    [docs]def matrix_rep(op, basis): - """ - Find the representation of an operator in a basis. - - Examples - ======== - - >>> from sympy.physics.secondquant import VarBosonicBasis, B, matrix_rep - >>> b = VarBosonicBasis(5) - >>> o = B(0) - >>> matrix_rep(o, b) - [0, 1, 0, 0, 0] - [0, 0, sqrt(2), 0, 0] - [0, 0, 0, sqrt(3), 0] - [0, 0, 0, 0, 2] - [0, 0, 0, 0, 0] - """ - a = zeros(len(basis)) - for i in range(len(basis)): - for j in range(len(basis)): - a[i, j] = apply_operators(Dagger(basis[i])*op*basis[j]) - return a - -
    -
    [docs]class BosonicBasis(object): - """ - Base class for a basis set of bosonic Fock states. - """ - pass - -
    -
    [docs]class VarBosonicBasis(object): - """ - A single state, variable particle number basis set. - - Examples - ======== - - >>> from sympy.physics.secondquant import VarBosonicBasis - >>> b = VarBosonicBasis(5) - >>> b - [FockState((0,)), FockState((1,)), FockState((2,)), - FockState((3,)), FockState((4,))] - """ - - def __init__(self, n_max): - self.n_max = n_max - self._build_states() - - def _build_states(self): - self.basis = [] - for i in range(self.n_max): - self.basis.append(FockStateBosonKet([i])) - self.n_basis = len(self.basis) - -
    [docs] def index(self, state): - """ - Returns the index of state in basis. - - Examples - ======== - - >>> from sympy.physics.secondquant import VarBosonicBasis - >>> b = VarBosonicBasis(3) - >>> state = b.state(1) - >>> b - [FockState((0,)), FockState((1,)), FockState((2,))] - >>> state - FockStateBosonKet((1,)) - >>> b.index(state) - 1 - """ - return self.basis.index(state) -
    -
    [docs] def state(self, i): - """ - The state of a single basis. - - Examples - ======== - - >>> from sympy.physics.secondquant import VarBosonicBasis - >>> b = VarBosonicBasis(5) - >>> b.state(3) - FockStateBosonKet((3,)) - """ - return self.basis[i] -
    - def __getitem__(self, i): - return self.state(i) - - def __len__(self): - return len(self.basis) - - def __repr__(self): - return repr(self.basis) - -
    -
    [docs]class FixedBosonicBasis(BosonicBasis): - """ - Fixed particle number basis set. - - Examples - ======== - - >>> from sympy.physics.secondquant import FixedBosonicBasis - >>> b = FixedBosonicBasis(2, 2) - >>> state = b.state(1) - >>> b - [FockState((2, 0)), FockState((1, 1)), FockState((0, 2))] - >>> state - FockStateBosonKet((1, 1)) - >>> b.index(state) - 1 - """ - def __init__(self, n_particles, n_levels): - self.n_particles = n_particles - self.n_levels = n_levels - self._build_particle_locations() - self._build_states() - - def _build_particle_locations(self): - tup = ["i%i" % i for i in range(self.n_particles)] - first_loop = "for i0 in range(%i)" % self.n_levels - other_loops = '' - for cur, prev in zip(tup[1:], tup): - temp = "for %s in range(%s + 1) " % (cur, prev) - other_loops = other_loops + temp - tup_string = "(%s)" % ", ".join(tup) - list_comp = "[%s %s %s]" % (tup_string, first_loop, other_loops) - result = eval(list_comp) - if self.n_particles == 1: - result = [(item,) for item in result] - self.particle_locations = result - - def _build_states(self): - self.basis = [] - for tuple_of_indices in self.particle_locations: - occ_numbers = self.n_levels*[0] - for level in tuple_of_indices: - occ_numbers[level] += 1 - self.basis.append(FockStateBosonKet(occ_numbers)) - self.n_basis = len(self.basis) - -
    [docs] def index(self, state): - """Returns the index of state in basis. - - Examples - ======== - - >>> from sympy.physics.secondquant import FixedBosonicBasis - >>> b = FixedBosonicBasis(2, 3) - >>> b.index(b.state(3)) - 3 - """ - return self.basis.index(state) -
    -
    [docs] def state(self, i): - """Returns the state that lies at index i of the basis - - Examples - ======== - - >>> from sympy.physics.secondquant import FixedBosonicBasis - >>> b = FixedBosonicBasis(2, 3) - >>> b.state(3) - FockStateBosonKet((1, 0, 1)) - """ - return self.basis[i] -
    - def __getitem__(self, i): - return self.state(i) - - def __len__(self): - return len(self.basis) - - def __repr__(self): - return repr(self.basis) - - -# def move(e, i, d): -# """ -# Takes the expression "e" and moves the operator at the position i by "d". -# """ -# if e.is_Mul: -# if d == 1: -# # e = a*b*c*d -# a = Mul(*e.args[:i]) -# b = e.args[i] -# c = e.args[i+1] -# d = Mul(*e.args[i+2:]) -# if isinstance(b, Dagger) and not isinstance(c, Dagger): -# i, j = b.args[0].args[0], c.args[0] -# return a*c*b*d-a*KroneckerDelta(i, j)*d -# elif not isinstance(b, Dagger) and isinstance(c, Dagger): -# i, j = b.args[0], c.args[0].args[0] -# return a*c*b*d-a*KroneckerDelta(i, j)*d -# else: -# return a*c*b*d -# elif d == -1: -# # e = a*b*c*d -# a = Mul(*e.args[:i-1]) -# b = e.args[i-1] -# c = e.args[i] -# d = Mul(*e.args[i+1:]) -# if isinstance(b, Dagger) and not isinstance(c, Dagger): -# i, j = b.args[0].args[0], c.args[0] -# return a*c*b*d-a*KroneckerDelta(i, j)*d -# elif not isinstance(b, Dagger) and isinstance(c, Dagger): -# i, j = b.args[0], c.args[0].args[0] -# return a*c*b*d-a*KroneckerDelta(i, j)*d -# else: -# return a*c*b*d -# else: -# if d > 1: -# while d >= 1: -# e = move(e, i, 1) -# d -= 1 -# i += 1 -# return e -# elif d < -1: -# while d <= -1: -# e = move(e, i, -1) -# d += 1 -# i -= 1 -# return e -# elif isinstance(e, Add): -# a, b = e.as_two_terms() -# return move(a, i, d) + move(b, i, d) -# raise NotImplementedError() -
    -
    [docs]class Commutator(Function): - """ - The Commutator: [A, B] = A*B - B*A - - The arguments are ordered according to .__cmp__() - - >>> from sympy import symbols - >>> from sympy.physics.secondquant import Commutator - >>> A, B = symbols('A,B', commutative=False) - >>> Commutator(B, A) - -Commutator(A, B) - - Evaluate the commutator with .doit() - - >>> comm = Commutator(A,B); comm - Commutator(A, B) - >>> comm.doit() - A*B - B*A - - - For two second quantization operators the commutator is evaluated - immediately: - - >>> from sympy.physics.secondquant import Fd, F - >>> a = symbols('a', above_fermi=True) - >>> i = symbols('i', below_fermi=True) - >>> p,q = symbols('p,q') - - >>> Commutator(Fd(a),Fd(i)) - 2*NO(CreateFermion(a)*CreateFermion(i)) - - But for more complicated expressions, the evaluation is triggered by - a call to .doit() - - >>> comm = Commutator(Fd(p)*Fd(q),F(i)); comm - Commutator(CreateFermion(p)*CreateFermion(q), AnnihilateFermion(i)) - >>> comm.doit(wicks=True) - -KroneckerDelta(p, i)*CreateFermion(q) + - KroneckerDelta(q, i)*CreateFermion(p) - - """ - - is_commutative = False - nargs = 2 - - @classmethod -
    [docs] def eval(cls, a, b): - """ - The Commutator [A,B] is on canonical form if A < B. - - Examples - ======== - - >>> from sympy.physics.secondquant import Commutator, F, Fd - >>> from sympy.abc import x - >>> c1 = Commutator(F(x), Fd(x)) - >>> c2 = Commutator(Fd(x), F(x)) - >>> Commutator.eval(c1, c2) - 0 - """ - if not (a and b): - return S.Zero - if a == b: - return S.Zero - if a.is_commutative or b.is_commutative: - return S.Zero - - # - # [A+B,C] -> [A,C] + [B,C] - # - a = a.expand() - if isinstance(a, Add): - return Add(*[cls(term, b) for term in a.args]) - b = b.expand() - if isinstance(b, Add): - return Add(*[cls(a, term) for term in b.args]) - - # - # [xA,yB] -> xy*[A,B] - # - ca, nca = a.args_cnc() - cb, ncb = b.args_cnc() - c_part = list(ca) + list(cb) - if c_part: - return Mul(Mul(*c_part), cls(Mul._from_args(nca), Mul._from_args(ncb))) - - # - # single second quantization operators - # - if isinstance(a, BosonicOperator) and isinstance(b, BosonicOperator): - if isinstance(b, CreateBoson) and isinstance(a, AnnihilateBoson): - return KroneckerDelta(a.state, b.state) - if isinstance(a, CreateBoson) and isinstance(b, AnnihilateBoson): - return S.NegativeOne*KroneckerDelta(a.state, b.state) - else: - return S.Zero - if isinstance(a, FermionicOperator) and isinstance(b, FermionicOperator): - return wicks(a*b) - wicks(b*a) - - # - # Canonical ordering of arguments - # - if a.sort_key() > b.sort_key(): - return S.NegativeOne*cls(b, a) -
    -
    [docs] def doit(self, **hints): - """ - Enables the computation of complex expressions. - - Examples - ======== - - >>> from sympy.physics.secondquant import Commutator, F, Fd - >>> from sympy import symbols - >>> i, j = symbols('i,j', below_fermi=True) - >>> a, b = symbols('a,b', above_fermi=True) - >>> c = Commutator(Fd(a)*F(i),Fd(b)*F(j)) - >>> c.doit(wicks=True) - 0 - """ - a = self.args[0] - b = self.args[1] - - if hints.get("wicks"): - a = a.doit(**hints) - b = b.doit(**hints) - try: - return wicks(a*b) - wicks(b*a) - except ContractionAppliesOnlyToFermions: - pass - except WicksTheoremDoesNotApply: - pass - - return (a*b - b*a).doit(**hints) -
    - def __repr__(self): - return "Commutator(%s,%s)" % (self.args[0], self.args[1]) - - def __str__(self): - return "[%s,%s]" % (self.args[0], self.args[1]) - - def _latex(self, printer): - return "\\left[%s,%s\\right]" % tuple([ - printer._print(arg) for arg in self.args]) - -
    -
    [docs]class NO(Expr): - """ - This Object is used to represent normal ordering brackets. - - i.e. {abcd} sometimes written :abcd: - - Applying the function NO(arg) to an argument means that all operators in - the argument will be assumed to anticommute, and have vanishing - contractions. This allows an immediate reordering to canonical form - upon object creation. - - >>> from sympy import symbols - >>> from sympy.physics.secondquant import NO, F, Fd - >>> p,q = symbols('p,q') - >>> NO(Fd(p)*F(q)) - NO(CreateFermion(p)*AnnihilateFermion(q)) - >>> NO(F(q)*Fd(p)) - -NO(CreateFermion(p)*AnnihilateFermion(q)) - - - Note: - If you want to generate a normal ordered equivalent of an expression, you - should use the function wicks(). This class only indicates that all - operators inside the brackets anticommute, and have vanishing contractions. - Nothing more, nothing less. - - """ - nargs = 1 - is_commutative = False - - def __new__(cls, arg): - """ - Use anticommutation to get canonical form of operators. - - Employ associativity of normal ordered product: {ab{cd}} = {abcd} - but note that {ab}{cd} /= {abcd}. - - We also employ distributivity: {ab + cd} = {ab} + {cd}. - - Canonical form also implies expand() {ab(c+d)} = {abc} + {abd}. - - """ - - # {ab + cd} = {ab} + {cd} - arg = sympify(arg) - arg = arg.expand() - if arg.is_Add: - return Add(*[ cls(term) for term in arg.args]) - - if arg.is_Mul: - - # take coefficient outside of normal ordering brackets - c_part, seq = arg.args_cnc() - if c_part: - coeff = Mul(*c_part) - if not seq: - return coeff - else: - coeff = S.One - - # {ab{cd}} = {abcd} - newseq = [] - foundit = False - for fac in seq: - if isinstance(fac, NO): - newseq.extend(fac.args) - foundit = True - else: - newseq.append(fac) - if foundit: - return coeff*cls(Mul(*newseq)) - - # We assume that the user don't mix B and F operators - if isinstance(seq[0], BosonicOperator): - raise NotImplementedError - - try: - newseq, sign = _sort_anticommuting_fermions(seq) - except ViolationOfPauliPrinciple: - return S.Zero - - if sign % 2: - return (S.NegativeOne*coeff)*cls(Mul(*newseq)) - elif sign: - return coeff*cls(Mul(*newseq)) - else: - pass # since sign==0, no permutations was necessary - - # if we couldn't do anything with Mul object, we just - # mark it as normal ordered - if coeff != S.One: - return coeff*cls(Mul(*newseq)) - return Expr.__new__(cls, Mul(*newseq)) - - if isinstance(arg, NO): - return arg - - # if object was not Mul or Add, normal ordering does not apply - return arg - - @property -
    [docs] def has_q_creators(self): - """ - Return 0 if the leftmost argument of the first argument is a not a - q_creator, else 1 if it is above fermi or -1 if it is below fermi. - - Examples - ======== - - >>> from sympy import symbols - >>> from sympy.physics.secondquant import NO, F, Fd - - >>> a = symbols('a', above_fermi=True) - >>> i = symbols('i', below_fermi=True) - >>> NO(Fd(a)*Fd(i)).has_q_creators - 1 - >>> NO(F(i)*F(a)).has_q_creators - -1 - >>> NO(Fd(i)*F(a)).has_q_creators #doctest: +SKIP - 0 - - """ - return self.args[0].args[0].is_q_creator -
    - @property -
    [docs] def has_q_annihilators(self): - """ - Return 0 if the rightmost argument of the first argument is a not a - q_annihilator, else 1 if it is above fermi or -1 if it is below fermi. - - Examples - ======== - - >>> from sympy import symbols - >>> from sympy.physics.secondquant import NO, F, Fd - - >>> a = symbols('a', above_fermi=True) - >>> i = symbols('i', below_fermi=True) - >>> NO(Fd(a)*Fd(i)).has_q_annihilators - -1 - >>> NO(F(i)*F(a)).has_q_annihilators - 1 - >>> NO(Fd(a)*F(i)).has_q_annihilators - 0 - - """ - return self.args[0].args[-1].is_q_annihilator -
    -
    [docs] def doit(self, **kw_args): - """ - Either removes the brackets or enables complex computations - in its arguments. - - Examples - ======== - - >>> from sympy.physics.secondquant import NO, Fd, F - >>> from textwrap import fill - >>> from sympy import symbols, Dummy - >>> p,q = symbols('p,q', cls=Dummy) - >>> print(fill(str(NO(Fd(p)*F(q)).doit()))) - KroneckerDelta(_a, _p)*KroneckerDelta(_a, - _q)*CreateFermion(_a)*AnnihilateFermion(_a) + KroneckerDelta(_a, - _p)*KroneckerDelta(_i, _q)*CreateFermion(_a)*AnnihilateFermion(_i) - - KroneckerDelta(_a, _q)*KroneckerDelta(_i, - _p)*AnnihilateFermion(_a)*CreateFermion(_i) - KroneckerDelta(_i, - _p)*KroneckerDelta(_i, _q)*AnnihilateFermion(_i)*CreateFermion(_i) - """ - if kw_args.get("remove_brackets", True): - return self._remove_brackets() - else: - return self.__new__(type(self), self.args[0].doit(**kw_args)) -
    - def _remove_brackets(self): - """ - Returns the sorted string without normal order brackets. - - The returned string have the property that no nonzero - contractions exist. - """ - - # check if any creator is also an annihilator - subslist = [] - for i in self.iter_q_creators(): - if self[i].is_q_annihilator: - assume = self[i].state.assumptions0 - - # only operators with a dummy index can be split in two terms - if isinstance(self[i].state, Dummy): - - # create indices with fermi restriction - assume.pop("above_fermi", None) - assume["below_fermi"] = True - below = Dummy('i', **assume) - assume.pop("below_fermi", None) - assume["above_fermi"] = True - above = Dummy('a', **assume) - - cls = type(self[i]) - split = ( - self[i].__new__(cls, below) - * KroneckerDelta(below, self[i].state) - + self[i].__new__(cls, above) - * KroneckerDelta(above, self[i].state) - ) - subslist.append((self[i], split)) - else: - raise SubstitutionOfAmbigousOperatorFailed(self[i]) - if subslist: - result = NO(self.subs(subslist)) - if isinstance(result, Add): - return Add(*[term.doit() for term in result.args]) - else: - return self.args[0] - - def _expand_operators(self): - """ - Returns a sum of NO objects that contain no ambiguous q-operators. - - If an index q has range both above and below fermi, the operator F(q) - is ambiguous in the sense that it can be both a q-creator and a q-annihilator. - If q is dummy, it is assumed to be a summation variable and this method - rewrites it into a sum of NO terms with unambiguous operators: - - {Fd(p)*F(q)} = {Fd(a)*F(b)} + {Fd(a)*F(i)} + {Fd(j)*F(b)} -{F(i)*Fd(j)} - - where a,b are above and i,j are below fermi level. - """ - return NO(self._remove_brackets) - - def __getitem__(self, i): - if isinstance(i, slice): - indices = i.indices(len(self)) - return [self.args[0].args[i] for i in range(*indices)] - else: - return self.args[0].args[i] - - def __len__(self): - return len(self.args[0].args) - -
    [docs] def iter_q_annihilators(self): - """ - Iterates over the annihilation operators. - - Examples - ======== - - >>> from sympy import symbols - >>> i, j = symbols('i j', below_fermi=True) - >>> a, b = symbols('a b', above_fermi=True) - >>> from sympy.physics.secondquant import NO, F, Fd - >>> no = NO(Fd(a)*F(i)*F(b)*Fd(j)) - - >>> no.iter_q_creators() - <generator object... at 0x...> - >>> list(no.iter_q_creators()) - [0, 1] - >>> list(no.iter_q_annihilators()) - [3, 2] - - """ - ops = self.args[0].args - iter = range(len(ops) - 1, -1, -1) - for i in iter: - if ops[i].is_q_annihilator: - yield i - else: - break -
    -
    [docs] def iter_q_creators(self): - """ - Iterates over the creation operators. - - Examples - ======== - - >>> from sympy import symbols - >>> i, j = symbols('i j', below_fermi=True) - >>> a, b = symbols('a b', above_fermi=True) - >>> from sympy.physics.secondquant import NO, F, Fd - >>> no = NO(Fd(a)*F(i)*F(b)*Fd(j)) - - >>> no.iter_q_creators() - <generator object... at 0x...> - >>> list(no.iter_q_creators()) - [0, 1] - >>> list(no.iter_q_annihilators()) - [3, 2] - - """ - - ops = self.args[0].args - iter = range(0, len(ops)) - for i in iter: - if ops[i].is_q_creator: - yield i - else: - break -
    -
    [docs] def get_subNO(self, i): - """ - Returns a NO() without FermionicOperator at index i. - - Examples - ======== - - >>> from sympy import symbols - >>> from sympy.physics.secondquant import F, NO - >>> p,q,r = symbols('p,q,r') - - >>> NO(F(p)*F(q)*F(r)).get_subNO(1) # doctest: +SKIP - NO(AnnihilateFermion(p)*AnnihilateFermion(r)) - - """ - arg0 = self.args[0] # it's a Mul by definition of how it's created - mul = arg0._new_rawargs(arg0.args[:i] + arg0.args[i + 1:]) - return NO(mul) -
    - def _latex(self, printer): - return "\\left\\{%s\\right\\}" % printer._print(self.args[0]) - - def __repr__(self): - return "NO(%s)" % self.args[0] - - def __str__(self): - return ":%s:" % self.args[0] - - -# @cacheit
    -
    [docs]def contraction(a, b): - """ - Calculates contraction of Fermionic operators a and b. - - Examples - ======== - - >>> from sympy import symbols - >>> from sympy.physics.secondquant import F, Fd, contraction - >>> p, q = symbols('p,q') - >>> a, b = symbols('a,b', above_fermi=True) - >>> i, j = symbols('i,j', below_fermi=True) - - A contraction is non-zero only if a quasi-creator is to the right of a - quasi-annihilator: - - >>> contraction(F(a),Fd(b)) - KroneckerDelta(a, b) - >>> contraction(Fd(i),F(j)) - KroneckerDelta(i, j) - - For general indices a non-zero result restricts the indices to below/above - the fermi surface: - - >>> contraction(Fd(p),F(q)) - KroneckerDelta(p, q)*KroneckerDelta(q, _i) - >>> contraction(F(p),Fd(q)) - KroneckerDelta(p, q)*KroneckerDelta(q, _a) - - Two creators or two annihilators always vanishes: - - >>> contraction(F(p),F(q)) - 0 - >>> contraction(Fd(p),Fd(q)) - 0 - - """ - if isinstance(b, FermionicOperator) and isinstance(a, FermionicOperator): - if isinstance(a, AnnihilateFermion) and isinstance(b, CreateFermion): - if b.state.assumptions0.get("below_fermi"): - return S.Zero - if a.state.assumptions0.get("below_fermi"): - return S.Zero - if b.state.assumptions0.get("above_fermi"): - return KroneckerDelta(a.state, b.state) - if a.state.assumptions0.get("above_fermi"): - return KroneckerDelta(a.state, b.state) - - return (KroneckerDelta(a.state, b.state)* - KroneckerDelta(b.state, Dummy('a', above_fermi=True))) - if isinstance(b, AnnihilateFermion) and isinstance(a, CreateFermion): - if b.state.assumptions0.get("above_fermi"): - return S.Zero - if a.state.assumptions0.get("above_fermi"): - return S.Zero - if b.state.assumptions0.get("below_fermi"): - return KroneckerDelta(a.state, b.state) - if a.state.assumptions0.get("below_fermi"): - return KroneckerDelta(a.state, b.state) - - return (KroneckerDelta(a.state, b.state)* - KroneckerDelta(b.state, Dummy('i', below_fermi=True))) - - # vanish if 2xAnnihilator or 2xCreator - return S.Zero - - else: - #not fermion operators - t = ( isinstance(i, FermionicOperator) for i in (a, b) ) - raise ContractionAppliesOnlyToFermions(*t) - -
    -def _sqkey(sq_operator): - """Generates key for canonical sorting of SQ operators.""" - return sq_operator._sortkey() - - -def _sort_anticommuting_fermions(string1, key=_sqkey): - """Sort fermionic operators to canonical order, assuming all pairs anticommute. - - Uses a bidirectional bubble sort. Items in string1 are not referenced - so in principle they may be any comparable objects. The sorting depends on the - operators '>' and '=='. - - If the Pauli principle is violated, an exception is raised. - - Returns - ======= - - tuple (sorted_str, sign) - - sorted_str: list containing the sorted operators - sign: int telling how many times the sign should be changed - (if sign==0 the string was already sorted) - """ - - verified = False - sign = 0 - rng = list(range(len(string1) - 1)) - rev = list(range(len(string1) - 3, -1, -1)) - - keys = list(map(key, string1)) - key_val = dict(list(zip(keys, string1))) - - while not verified: - verified = True - for i in rng: - left = keys[i] - right = keys[i + 1] - if left == right: - raise ViolationOfPauliPrinciple([left, right]) - if left > right: - verified = False - keys[i:i + 2] = [right, left] - sign = sign + 1 - if verified: - break - for i in rev: - left = keys[i] - right = keys[i + 1] - if left == right: - raise ViolationOfPauliPrinciple([left, right]) - if left > right: - verified = False - keys[i:i + 2] = [right, left] - sign = sign + 1 - string1 = [ key_val[k] for k in keys ] - return (string1, sign) - - -
    [docs]def evaluate_deltas(e): - """ - We evaluate KroneckerDelta symbols in the expression assuming Einstein summation. - - If one index is repeated it is summed over and in effect substituted with - the other one. If both indices are repeated we substitute according to what - is the preferred index. this is determined by - KroneckerDelta.preferred_index and KroneckerDelta.killable_index. - - In case there are no possible substitutions or if a substitution would - imply a loss of information, nothing is done. - - In case an index appears in more than one KroneckerDelta, the resulting - substitution depends on the order of the factors. Since the ordering is platform - dependent, the literal expression resulting from this function may be hard to - predict. - - Examples - ======== - - We assume the following: - - >>> from sympy import symbols, Function, Dummy, KroneckerDelta - >>> from sympy.physics.secondquant import evaluate_deltas - >>> i,j = symbols('i j', below_fermi=True, cls=Dummy) - >>> a,b = symbols('a b', above_fermi=True, cls=Dummy) - >>> p,q = symbols('p q', cls=Dummy) - >>> f = Function('f') - >>> t = Function('t') - - The order of preference for these indices according to KroneckerDelta is - (a, b, i, j, p, q). - - Trivial cases: - - >>> evaluate_deltas(KroneckerDelta(i,j)*f(i)) # d_ij f(i) -> f(j) - f(_j) - >>> evaluate_deltas(KroneckerDelta(i,j)*f(j)) # d_ij f(j) -> f(i) - f(_i) - >>> evaluate_deltas(KroneckerDelta(i,p)*f(p)) # d_ip f(p) -> f(i) - f(_i) - >>> evaluate_deltas(KroneckerDelta(q,p)*f(p)) # d_qp f(p) -> f(q) - f(_q) - >>> evaluate_deltas(KroneckerDelta(q,p)*f(q)) # d_qp f(q) -> f(p) - f(_p) - - More interesting cases: - - >>> evaluate_deltas(KroneckerDelta(i,p)*t(a,i)*f(p,q)) - f(_i, _q)*t(_a, _i) - >>> evaluate_deltas(KroneckerDelta(a,p)*t(a,i)*f(p,q)) - f(_a, _q)*t(_a, _i) - >>> evaluate_deltas(KroneckerDelta(p,q)*f(p,q)) - f(_p, _p) - - Finally, here are some cases where nothing is done, because that would - imply a loss of information: - - >>> evaluate_deltas(KroneckerDelta(i,p)*f(q)) - f(_q)*KroneckerDelta(_i, _p) - >>> evaluate_deltas(KroneckerDelta(i,p)*f(i)) - f(_i)*KroneckerDelta(_i, _p) - """ - - # We treat Deltas only in mul objects - # for general function objects we don't evaluate KroneckerDeltas in arguments, - # but here we hard code exceptions to this rule - accepted_functions = ( - Add, - ) - if isinstance(e, accepted_functions): - return e.func(*[evaluate_deltas(arg) for arg in e.args]) - - elif isinstance(e, Mul): - # find all occurences of delta function and count each index present in - # expression. - deltas = [] - indices = {} - for i in e.args: - for s in i.atoms(): - if s in indices: - indices[s] += 1 - else: - indices[s] = 0 # geek counting simplifies logic below - if isinstance(i, KroneckerDelta): - deltas.append(i) - - for d in deltas: - # If we do something, and there are more deltas, we should recurse - # to treat the resulting expression properly - if indices[d.killable_index]: - e = e.subs(d.killable_index, d.preferred_index) - if len(deltas) > 1: - return evaluate_deltas(e) - elif indices[d.preferred_index] and d.indices_contain_equal_information: - e = e.subs(d.preferred_index, d.killable_index) - if len(deltas) > 1: - return evaluate_deltas(e) - else: - pass - - return e - # nothing to do, maybe we hit a Symbol or a number - else: - return e - -
    -
    [docs]def substitute_dummies(expr, new_indices=False, pretty_indices={}): - """ - Collect terms by substitution of dummy variables. - - This routine allows simplification of Add expressions containing terms - which differ only due to dummy variables. - - The idea is to substitute all dummy variables consistently depending on - the structure of the term. For each term, we obtain a sequence of all - dummy variables, where the order is determined by the index range, what - factors the index belongs to and its position in each factor. See - _get_ordered_dummies() for more inforation about the sorting of dummies. - The index sequence is then substituted consistently in each term. - - Examples - ======== - - >>> from sympy import symbols, Function, Dummy - >>> from sympy.physics.secondquant import substitute_dummies - >>> a,b,c,d = symbols('a b c d', above_fermi=True, cls=Dummy) - >>> i,j = symbols('i j', below_fermi=True, cls=Dummy) - >>> f = Function('f') - - >>> expr = f(a,b) + f(c,d); expr - f(_a, _b) + f(_c, _d) - - Since a, b, c and d are equivalent summation indices, the expression can be - simplified to a single term (for which the dummy indices are still summed over) - - >>> substitute_dummies(expr) - 2*f(_a, _b) - - - Controlling output: - - By default the dummy symbols that are already present in the expression - will be reused in a different permuation. However, if new_indices=True, - new dummies will be generated and inserted. The keyword 'pretty_indices' - can be used to control this generation of new symbols. - - By default the new dummies will be generated on the form i_1, i_2, a_1, - etc. If you supply a dictionary with key:value pairs in the form: - - { index_group: string_of_letters } - - The letters will be used as labels for the new dummy symbols. The - index_groups must be one of 'above', 'below' or 'general'. - - >>> expr = f(a,b,i,j) - >>> my_dummies = { 'above':'st', 'below':'uv' } - >>> substitute_dummies(expr, new_indices=True, pretty_indices=my_dummies) - f(_s, _t, _u, _v) - - If we run out of letters, or if there is no keyword for some index_group - the default dummy generator will be used as a fallback: - - >>> p,q = symbols('p q', cls=Dummy) # general indices - >>> expr = f(p,q) - >>> substitute_dummies(expr, new_indices=True, pretty_indices=my_dummies) - f(_p_0, _p_1) - - """ - - # setup the replacing dummies - if new_indices: - letters_above = pretty_indices.get('above', "") - letters_below = pretty_indices.get('below', "") - letters_general = pretty_indices.get('general', "") - len_above = len(letters_above) - len_below = len(letters_below) - len_general = len(letters_general) - - def _i(number): - try: - return letters_below[number] - except IndexError: - return 'i_' + str(number - len_below) - - def _a(number): - try: - return letters_above[number] - except IndexError: - return 'a_' + str(number - len_above) - - def _p(number): - try: - return letters_general[number] - except IndexError: - return 'p_' + str(number - len_general) - - aboves = [] - belows = [] - generals = [] - - dummies = expr.atoms(Dummy) - if not new_indices: - dummies = sorted(dummies, key=default_sort_key) - - # generate lists with the dummies we will insert - a = i = p = 0 - for d in dummies: - assum = d.assumptions0 - - if assum.get("above_fermi"): - if new_indices: - sym = _a(a) - a += 1 - l1 = aboves - elif assum.get("below_fermi"): - if new_indices: - sym = _i(i) - i += 1 - l1 = belows - else: - if new_indices: - sym = _p(p) - p += 1 - l1 = generals - - if new_indices: - l1.append(Dummy(sym, **assum)) - else: - l1.append(d) - - expr = expr.expand() - terms = Add.make_args(expr) - new_terms = [] - for term in terms: - i = iter(belows) - a = iter(aboves) - p = iter(generals) - ordered = _get_ordered_dummies(term) - subsdict = {} - for d in ordered: - if d.assumptions0.get('below_fermi'): - subsdict[d] = next(i) - elif d.assumptions0.get('above_fermi'): - subsdict[d] = next(a) - else: - subsdict[d] = next(p) - subslist = [] - final_subs = [] - for k, v in subsdict.items(): - if k == v: - continue - if v in subsdict: - # We check if the sequence of substitutions end quickly. In - # that case, we can avoid temporary symbols if we ensure the - # correct substitution order. - if subsdict[v] in subsdict: - # (x, y) -> (y, x), we need a temporary variable - x = Dummy('x') - subslist.append((k, x)) - final_subs.append((x, v)) - else: - # (x, y) -> (y, a), x->y must be done last - # but before temporary variables are resolved - final_subs.insert(0, (k, v)) - else: - subslist.append((k, v)) - subslist.extend(final_subs) - new_terms.append(term.subs(subslist)) - return Add(*new_terms) - -
    -class KeyPrinter(StrPrinter): - """Printer for which only equal objects are equal in print""" - def _print_Dummy(self, expr): - return "(%s_%i)" % (expr.name, expr.dummy_index) - - -def __kprint(expr): - p = KeyPrinter() - return p.doprint(expr) - - -def _get_ordered_dummies(mul, verbose=False): - """Returns all dummies in the mul sorted in canonical order - - The purpose of the canonical ordering is that dummies can be substituted - consistently across terms with the result that equivalent terms can be - simplified. - - It is not possible to determine if two terms are equivalent based solely on - the dummy order. However, a consistent substitution guided by the ordered - dummies should lead to trivially (non-)equivalent terms, thereby revealing - the equivalence. This also means that if two terms have identical sequences of - dummies, the (non-)equivalence should already be apparent. - - Strategy - -------- - - The canoncial order is given by an arbitrary sorting rule. A sort key - is determined for each dummy as a tuple that depends on all factors where - the index is present. The dummies are thereby sorted according to the - contraction structure of the term, instead of sorting based solely on the - dummy symbol itself. - - After all dummies in the term has been assigned a key, we check for identical - keys, i.e. unorderable dummies. If any are found, we call a specialized - method, _determine_ambiguous(), that will determine a unique order based - on recursive calls to _get_ordered_dummies(). - - Key description - --------------- - - A high level description of the sort key: - - 1. Range of the dummy index - 2. Relation to external (non-dummy) indices - 3. Position of the index in the first factor - 4. Position of the index in the second factor - - The sort key is a tuple with the following components: - - 1. A single character indicating the range of the dummy (above, below - or general.) - 2. A list of strings with fully masked string representations of all - factors where the dummy is present. By masked, we mean that dummies - are represented by a symbol to indicate either below fermi, above or - general. No other information is displayed about the dummies at - this point. The list is sorted stringwise. - 3. An integer number indicating the position of the index, in the first - factor as sorted in 2. - 4. An integer number indicating the position of the index, in the second - factor as sorted in 2. - - If a factor is either of type AntiSymmetricTensor or SqOperator, the index - position in items 3 and 4 is indicated as 'upper' or 'lower' only. - (Creation operators are considered upper and annihilation operators lower.) - - If the masked factors are identical, the two factors cannot be ordered - unambiguously in item 2. In this case, items 3, 4 are left out. If several - indices are contracted between the unorderable factors, it will be handled by - _determine_ambiguous() - - - """ - # setup dicts to avoid repeated calculations in key() - args = Mul.make_args(mul) - fac_dum = dict([ (fac, fac.atoms(Dummy)) for fac in args] ) - fac_repr = dict([ (fac, __kprint(fac)) for fac in args] ) - all_dums = reduce(set.union, list(fac_dum.values()), set()) - mask = {} - for d in all_dums: - if d.assumptions0.get('below_fermi'): - mask[d] = '0' - elif d.assumptions0.get('above_fermi'): - mask[d] = '1' - else: - mask[d] = '2' - dum_repr = dict([ (d, __kprint(d)) for d in all_dums ]) - - def _key(d): - dumstruct = [ fac for fac in fac_dum if d in fac_dum[fac] ] - other_dums = reduce( - set.union, [ fac_dum[fac] for fac in dumstruct ], set()) - fac = dumstruct[-1] - if other_dums is fac_dum[fac]: - other_dums = fac_dum[fac].copy() - other_dums.remove(d) - masked_facs = [ fac_repr[fac] for fac in dumstruct ] - for d2 in other_dums: - masked_facs = [ fac.replace(dum_repr[d2], mask[d2]) - for fac in masked_facs ] - all_masked = [ fac.replace(dum_repr[d], mask[d]) - for fac in masked_facs ] - masked_facs = dict(list(zip(dumstruct, masked_facs))) - - # dummies for which the ordering cannot be determined - if has_dups(all_masked): - all_masked.sort() - return mask[d], tuple(all_masked) # positions are ambiguous - - # sort factors according to fully masked strings - keydict = dict(list(zip(dumstruct, all_masked))) - dumstruct.sort(key=lambda x: keydict[x]) - all_masked.sort() - - pos_val = [] - for fac in dumstruct: - if isinstance(fac, AntiSymmetricTensor): - if d in fac.upper: - pos_val.append('u') - if d in fac.lower: - pos_val.append('l') - elif isinstance(fac, Creator): - pos_val.append('u') - elif isinstance(fac, Annihilator): - pos_val.append('l') - elif isinstance(fac, NO): - ops = [ op for op in fac if op.has(d) ] - for op in ops: - if isinstance(op, Creator): - pos_val.append('u') - else: - pos_val.append('l') - else: - # fallback to position in string representation - facpos = -1 - while 1: - facpos = masked_facs[fac].find(dum_repr[d], facpos + 1) - if facpos == -1: - break - pos_val.append(facpos) - return (mask[d], tuple(all_masked), pos_val[0], pos_val[-1]) - dumkey = dict(list(zip(all_dums, list(map(_key, all_dums))))) - result = sorted(all_dums, key=lambda x: dumkey[x]) - if has_dups(iter(dumkey.values())): - # We have ambiguities - unordered = defaultdict(set) - for d, k in dumkey.items(): - unordered[k].add(d) - for k in [ k for k in unordered if len(unordered[k]) < 2 ]: - del unordered[k] - - unordered = [ unordered[k] for k in sorted(unordered) ] - result = _determine_ambiguous(mul, result, unordered) - return result - - -def _determine_ambiguous(term, ordered, ambiguous_groups): - # We encountered a term for which the dummy substitution is ambiguous. - # This happens for terms with 2 or more contractions between factors that - # cannot be uniquely ordered independent of summation indices. For - # example: - # - # Sum(p, q) v^{p, .}_{q, .}v^{q, .}_{p, .} - # - # Assuming that the indices represented by . are dummies with the - # same range, the factors cannot be ordered, and there is no - # way to determine a consistent ordering of p and q. - # - # The strategy employed here, is to relabel all unambiguous dummies with - # non-dummy symbols and call _get_ordered_dummies again. This procedure is - # applied to the entire term so there is a possibility that - # _determine_ambiguous() is called again from a deeper recursion level. - - # break recursion if there are no ordered dummies - all_ambiguous = set() - for dummies in ambiguous_groups: - all_ambiguous |= dummies - all_ordered = set(ordered) - all_ambiguous - if not all_ordered: - # FIXME: If we arrive here, there are no ordered dummies. A method to - # handle this needs to be implemented. In order to return something - # useful nevertheless, we choose arbitrarily the first dummy and - # determine the rest from this one. This method is dependent on the - # actual dummy labels which violates an assumption for the canonization - # procedure. A better implementation is needed. - group = [ d for d in ordered if d in ambiguous_groups[0] ] - d = group[0] - all_ordered.add(d) - ambiguous_groups[0].remove(d) - - stored_counter = _symbol_factory._counter - subslist = [] - for d in [ d for d in ordered if d in all_ordered ]: - nondum = _symbol_factory._next() - subslist.append((d, nondum)) - newterm = term.subs(subslist) - neworder = _get_ordered_dummies(newterm) - _symbol_factory._set_counter(stored_counter) - - # update ordered list with new information - for group in ambiguous_groups: - ordered_group = [ d for d in neworder if d in group ] - ordered_group.reverse() - result = [] - for d in ordered: - if d in group: - result.append(ordered_group.pop()) - else: - result.append(d) - ordered = result - return ordered - - -class _SymbolFactory(object): - def __init__(self, label): - self._counterVar = 0 - self._label = label - - def _set_counter(self, value): - """ - Sets counter to value. - """ - self._counterVar = value - - @property - def _counter(self): - """ - What counter is currently at. - """ - return self._counterVar - - def _next(self): - """ - Generates the next symbols and increments counter by 1. - """ - s = Symbol("%s%i" % (self._label, self._counterVar)) - self._counterVar += 1 - return s -_symbol_factory = _SymbolFactory('_]"]_') # most certainly a unique label - - -@cacheit -def _get_contractions(string1, keep_only_fully_contracted=False): - """ - Returns Add-object with contracted terms. - - Uses recursion to find all contractions. -- Internal helper function -- - - Will find nonzero contractions in string1 between indices given in - leftrange and rightrange. - - """ - - # Should we store current level of contraction? - if keep_only_fully_contracted and string1: - result = [] - else: - result = [NO(Mul(*string1))] - - for i in range(len(string1) - 1): - for j in range(i + 1, len(string1)): - - c = contraction(string1[i], string1[j]) - - if c: - # print "found contraction",c - - sign = (j - i + 1) % 2 - if sign: - coeff = S.NegativeOne*c - else: - coeff = c - - # - # Call next level of recursion - # ============================ - # - # We now need to find more contractions among operators - # - # oplist = string1[:i]+ string1[i+1:j] + string1[j+1:] - # - # To prevent overcounting, we don't allow contractions - # we have already encountered. i.e. contractions between - # string1[:i] <---> string1[i+1:j] - # and string1[:i] <---> string1[j+1:]. - # - # This leaves the case: - oplist = string1[i + 1:j] + string1[j + 1:] - - if oplist: - - result.append(coeff*NO( - Mul(*string1[:i])*_get_contractions( oplist, - keep_only_fully_contracted=keep_only_fully_contracted))) - - else: - result.append(coeff*NO( Mul(*string1[:i]))) - - if keep_only_fully_contracted: - break # next iteration over i leaves leftmost operator string1[0] uncontracted - - return Add(*result) - - -# @cacheit -
    [docs]def wicks(e, **kw_args): - """ - Returns the normal ordered equivalent of an expression using Wicks Theorem. - - - Examples - ======== - - >>> from sympy import symbols, Function, Dummy - >>> from sympy.physics.secondquant import wicks, F, Fd, NO - >>> p,q,r = symbols('p,q,r') - >>> wicks(Fd(p)*F(q)) # doctest: +SKIP - d(p, q)*d(q, _i) + NO(CreateFermion(p)*AnnihilateFermion(q)) - - By default, the expression is expanded: - - >>> wicks(F(p)*(F(q)+F(r))) # doctest: +SKIP - NO(AnnihilateFermion(p)*AnnihilateFermion(q)) + NO( - AnnihilateFermion(p)*AnnihilateFermion(r)) - - With the keyword 'keep_only_fully_contracted=True', only fully contracted - terms are returned. - - By request, the result can be simplified in the following order: - -- KroneckerDelta functions are evaluated - -- Dummy variables are substituted consistently across terms - - >>> p, q, r = symbols('p q r', cls=Dummy) - >>> wicks(Fd(p)*(F(q)+F(r)), keep_only_fully_contracted=True) # doctest: +SKIP - KroneckerDelta(_i, _q)*KroneckerDelta( - _p, _q) + KroneckerDelta(_i, _r)*KroneckerDelta(_p, _r) - - """ - - if not e: - return S.Zero - - opts = { - 'simplify_kronecker_deltas': False, - 'expand': True, - 'simplify_dummies': False, - 'keep_only_fully_contracted': False - } - opts.update(kw_args) - - # check if we are already normally ordered - if isinstance(e, NO): - if opts['keep_only_fully_contracted']: - return S.Zero - else: - return e - elif isinstance(e, FermionicOperator): - if opts['keep_only_fully_contracted']: - return S.Zero - else: - return e - - # break up any NO-objects, and evaluate commutators - e = e.doit(wicks=True) - - # make sure we have only one term to consider - e = e.expand() - if isinstance(e, Add): - if opts['simplify_dummies']: - return substitute_dummies(Add(*[ wicks(term, **kw_args) for term in e.args])) - else: - return Add(*[ wicks(term, **kw_args) for term in e.args]) - - # For Mul-objects we can actually do something - if isinstance(e, Mul): - - # we dont want to mess around with commuting part of Mul - # so we factorize it out before starting recursion - c_part = [] - string1 = [] - for factor in e.args: - if factor.is_commutative: - c_part.append(factor) - else: - string1.append(factor) - n = len(string1) - - # catch trivial cases - if n == 0: - result = e - elif n == 1: - if opts['keep_only_fully_contracted']: - return S.Zero - else: - result = e - - else: # non-trivial - - if isinstance(string1[0], BosonicOperator): - raise NotImplementedError - - string1 = tuple(string1) - - # recursion over higher order contractions - result = _get_contractions(string1, - keep_only_fully_contracted=opts['keep_only_fully_contracted'] ) - result = Mul(*c_part)*result - - if opts['expand']: - result = result.expand() - if opts['simplify_kronecker_deltas']: - result = evaluate_deltas(result) - - return result - - # there was nothing to do - return e - -
    -
    [docs]class PermutationOperator(Expr): - """ - Represents the index permutation operator P(ij). - - P(ij)*f(i)*g(j) = f(i)*g(j) - f(j)*g(i) - """ - is_commutative = True - - def __new__(cls, i, j): - i, j = list(map(sympify, (i, j))) - if (i > j): - obj = Basic.__new__(cls, j, i) - else: - obj = Basic.__new__(cls, i, j) - return obj - -
    [docs] def get_permuted(self, expr): - """ - Returns -expr with permuted indices. - - >>> from sympy import symbols, Function - >>> from sympy.physics.secondquant import PermutationOperator - >>> p,q = symbols('p,q') - >>> f = Function('f') - >>> PermutationOperator(p,q).get_permuted(f(p,q)) - -f(q, p) - - """ - i = self.args[0] - j = self.args[1] - if expr.has(i) and expr.has(j): - tmp = Dummy() - expr = expr.subs(i, tmp) - expr = expr.subs(j, i) - expr = expr.subs(tmp, j) - return S.NegativeOne*expr - else: - return expr -
    - def _latex(self, printer): - return "P(%s%s)" % self.args - -
    -
    [docs]def simplify_index_permutations(expr, permutation_operators): - """ - Performs simplification by introducing PermutationOperators where appropriate. - - Schematically: - [abij] - [abji] - [baij] + [baji] -> P(ab)*P(ij)*[abij] - - permutation_operators is a list of PermutationOperators to consider. - - If permutation_operators=[P(ab),P(ij)] we will try to introduce the - permutation operators P(ij) and P(ab) in the expression. If there are other - possible simplifications, we ignore them. - - >>> from sympy import symbols, Function - >>> from sympy.physics.secondquant import simplify_index_permutations - >>> from sympy.physics.secondquant import PermutationOperator - >>> p,q,r,s = symbols('p,q,r,s') - >>> f = Function('f') - >>> g = Function('g') - - >>> expr = f(p)*g(q) - f(q)*g(p); expr - f(p)*g(q) - f(q)*g(p) - >>> simplify_index_permutations(expr,[PermutationOperator(p,q)]) - f(p)*g(q)*PermutationOperator(p, q) - - >>> PermutList = [PermutationOperator(p,q),PermutationOperator(r,s)] - >>> expr = f(p,r)*g(q,s) - f(q,r)*g(p,s) + f(q,s)*g(p,r) - f(p,s)*g(q,r) - >>> simplify_index_permutations(expr,PermutList) - f(p, r)*g(q, s)*PermutationOperator(p, q)*PermutationOperator(r, s) - - """ - - def _get_indices(expr, ind): - """ - Collects indices recursively in predictable order. - """ - result = [] - for arg in expr.args: - if arg in ind: - result.append(arg) - else: - if arg.args: - result.extend(_get_indices(arg, ind)) - return result - - def _choose_one_to_keep(a, b, ind): - # we keep the one where indices in ind are in order ind[0] < ind[1] - if _get_indices(a, ind) < _get_indices(b, ind): - return a - else: - return b - - expr = expr.expand() - if isinstance(expr, Add): - terms = set(expr.args) - - for P in permutation_operators: - new_terms = set([]) - on_hold = set([]) - while terms: - term = terms.pop() - permuted = P.get_permuted(term) - if permuted in terms | on_hold: - try: - terms.remove(permuted) - except KeyError: - on_hold.remove(permuted) - keep = _choose_one_to_keep(term, permuted, P.args) - new_terms.add(P*keep) - else: - - # Some terms must get a second chance because the permuted - # term may already have canonical dummy ordering. Then - # substitute_dummies() does nothing. However, the other - # term, if it exists, will be able to match with us. - permuted1 = permuted - permuted = substitute_dummies(permuted) - if permuted1 == permuted: - on_hold.add(term) - elif permuted in terms | on_hold: - try: - terms.remove(permuted) - except KeyError: - on_hold.remove(permuted) - keep = _choose_one_to_keep(term, permuted, P.args) - new_terms.add(P*keep) - else: - new_terms.add(term) - terms = new_terms | on_hold - return Add(*terms) - return expr
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/sho.html b/dev-py3k/_modules/sympy/physics/sho.html deleted file mode 100644 index 35dda7b518f..00000000000 --- a/dev-py3k/_modules/sympy/physics/sho.html +++ /dev/null @@ -1,205 +0,0 @@ - - - - - - - - - - sympy.physics.sho — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.sho

    -from sympy.core import S, pi, Rational
    -from sympy.functions import assoc_laguerre, sqrt, exp, factorial, factorial2
    -
    -
    -
    [docs]def R_nl(n, l, nu, r): - """ - Returns the radial wavefunction R_{nl} for a 3d isotropic harmonic - oscillator. - - ``n`` - the "nodal" quantum number. Corresponds to the number of nodes in - the wavefunction. n >= 0 - ``l`` - the quantum number for orbital angular momentum - ``nu`` - mass-scaled frequency: nu = m*omega/(2*hbar) where `m' is the mass - and `omega` the frequency of the oscillator. - (in atomic units nu == omega/2) - ``r`` - Radial coordinate - - Examples - ======== - - >>> from sympy.physics.sho import R_nl - >>> from sympy import var - >>> var("r nu l") - (r, nu, l) - >>> R_nl(0, 0, 1, r) - 2*2**(3/4)*exp(-r**2)/pi**(1/4) - >>> R_nl(1, 0, 1, r) - 4*2**(1/4)*sqrt(3)*(-2*r**2 + 3/2)*exp(-r**2)/(3*pi**(1/4)) - - l, nu and r may be symbolic: - - >>> R_nl(0, 0, nu, r) - 2*2**(3/4)*sqrt(nu**(3/2))*exp(-nu*r**2)/pi**(1/4) - >>> R_nl(0, l, 1, r) - r**l*sqrt(2**(l + 3/2)*2**(l + 2)/(2*l + 1)!!)*exp(-r**2)/pi**(1/4) - - The normalization of the radial wavefunction is: - - >>> from sympy import Integral, oo - >>> Integral(R_nl(0, 0, 1, r)**2 * r**2, (r, 0, oo)).n() - 1.00000000000000 - >>> Integral(R_nl(1, 0, 1, r)**2 * r**2, (r, 0, oo)).n() - 1.00000000000000 - >>> Integral(R_nl(1, 1, 1, r)**2 * r**2, (r, 0, oo)).n() - 1.00000000000000 - - """ - n, l, nu, r = list(map(S, [n, l, nu, r])) - - # formula uses n >= 1 (instead of nodal n >= 0) - n = n + 1 - C = sqrt( - ((2*nu)**(l + Rational(3, 2))*2**(n + l + 1)*factorial(n - 1))/ - (sqrt(pi)*(factorial2(2*n + 2*l - 1))) - ) - return C*r**(l)*exp(-nu*r**2)*assoc_laguerre(n - 1, l + S(1)/2, 2*nu*r**2) - -
    -
    [docs]def E_nl(n, l, hw): - """ - Returns the Energy of an isotropic harmonic oscillator - - ``n`` - the "nodal" quantum number - ``l`` - the orbital angular momentum - ``hw`` - the harmonic oscillator parameter. - - The unit of the returned value matches the unit of hw, since the energy is - calculated as: - - E_nl = (2*n + l + 3/2)*hw - - Examples - ======== - - >>> from sympy.physics.sho import E_nl - >>> from sympy import symbols - >>> x, y, z = symbols('x, y, z') - >>> E_nl(x, y, z) - z*(2*x + y + 3/2) - """ - return (2*n + l + Rational(3, 2))*hw
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/units.html b/dev-py3k/_modules/sympy/physics/units.html deleted file mode 100644 index 404ba53acdf..00000000000 --- a/dev-py3k/_modules/sympy/physics/units.html +++ /dev/null @@ -1,424 +0,0 @@ - - - - - - - - - - sympy.physics.units — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.units

    -"""
    -Physical units and dimensions.
    -
    -The base class is Unit, where all here defined units (~200) inherit from.
    -
    -The find_unit function can help you find units for a given quantity:
    -
    -    >>> import sympy.physics.units as u
    -    >>> u.find_unit('coul')
    -    ['coulomb', 'coulombs']
    -    >>> u.find_unit(u.charge)
    -    ['C', 'charge', 'coulomb', 'coulombs']
    -    >>> u.coulomb
    -    A*s
    -
    -Units are always given in terms of base units that have a name and
    -an abbreviation:
    -
    -    >>> u.A.name
    -    'ampere'
    -    >>> u.ampere.abbrev
    -    'A'
    -
    -The generic name for a unit (like 'length', 'mass', etc...)
    -can help you find units:
    -
    -    >>> u.find_unit('magnet')
    -    ['magnetic_flux', 'magnetic_constant', 'magnetic_flux_density']
    -    >>> u.find_unit(u.magnetic_flux)
    -    ['Wb', 'wb', 'weber', 'webers', 'magnetic_flux']
    -
    -If, for a given session, you wish to add a unit you may do so:
    -
    -    >>> u.find_unit('gal')
    -    []
    -    >>> u.gal = 4*u.quart
    -    >>> u.gal/u.inch**3
    -    924
    -
    -To see a given quantity in terms of some other unit, divide by the desired
    -unit:
    -
    -    >>> mph = u.miles/u.hours
    -    >>> (u.m/u.s/mph).n(2)
    -    2.2
    -
    -The units are defined in terms of base units, so when you divide similar
    -units you will obtain a pure number. This means, for example, that if you
    -divide a real-world mass (like grams) by the atomic mass unit (amu) you
    -will obtain Avogadro's number. To obtain the answer in moles you
    -should divide by the unit ``avogadro``:
    -
    -    >>> u.grams/u.amu
    -    602214179000000000000000
    -    >>> _/u.avogadro
    -    mol
    -
    -For chemical calculations the unit ``mmu`` (molar mass unit) has been
    -defined so this conversion is handled automatically. For example, the
    -number of moles in 1 kg of water might be calculated as:
    -
    -    >>> u.kg/(18*u.mmu).n(3)
    -    55.5*mol
    -
    -If you need the number of atoms in a mol as a pure number you can use
    -``avogadro_number`` but if you need it as a dimensional quantity you should use
    -``avogadro_constant``. (``avogadro`` is a shorthand for the dimensional
    -quantity.)
    -
    -    >>> u.avogadro_number
    -    602214179000000000000000
    -    >>> u.avogadro_constant
    -    602214179000000000000000/mol
    -"""
    -
    -from sympy import Rational, pi
    -from sympy.core import AtomicExpr
    -
    -
    -
    [docs]class Unit(AtomicExpr): - """ - Base class for base unit of physical units. - - >>> from sympy.physics.units import Unit - >>> Unit("meter", "m") - m - - Other units are derived from base units: - - >>> import sympy.physics.units as u - >>> cm = u.m/100 - >>> 100*u.cm - m - - """ - is_positive = True # make sqrt(m**2) --> m - is_commutative = True - is_number = False - - __slots__ = ["name", "abbrev"] - - def __new__(cls, name, abbrev, **assumptions): - obj = AtomicExpr.__new__(cls, **assumptions) - assert isinstance(name, str), repr(type(name)) - assert isinstance(abbrev, str), repr(type(abbrev)) - obj.name = name - obj.abbrev = abbrev - return obj - - def __getnewargs__(self): - return (self.name, self.abbrev) - - def __eq__(self, other): - return isinstance(other, Unit) and self.name == other.name - - def __hash__(self): - return super(Unit, self).__hash__() - - def _hashable_content(self): - return (self.name, self.abbrev) - - @property - def free_symbols(self): - return set() - -# Dimensionless -
    -percent = percents = Rational(1, 100) -permille = permille = Rational(1, 1000) - -ten = Rational(10) - -yotta = ten**24 -zetta = ten**21 -exa = ten**18 -peta = ten**15 -tera = ten**12 -giga = ten**9 -mega = ten**6 -kilo = ten**3 -deca = ten**1 -deci = ten**-1 -centi = ten**-2 -milli = ten**-3 -micro = ten**-6 -nano = ten**-9 -pico = ten**-12 -femto = ten**-15 -atto = ten**-18 -zepto = ten**-21 -yocto = ten**-24 - -rad = radian = radians = 1 -deg = degree = degrees = pi/180 - - -# Base units - -length = m = meter = meters = Unit('meter', 'm') -mass = kg = kilogram = kilograms = Unit('kilogram', 'kg') -time = s = second = seconds = Unit('second', 's') -current = A = ampere = amperes = Unit('ampere', 'A') -temperature = K = kelvin = kelvins = Unit('kelvin', 'K') -amount = mol = mole = moles = Unit('mole', 'mol') -luminosity = cd = candela = candelas = Unit('candela', 'cd') - - -# Derived units -volume = meter**3 -frequency = Hz = hz = hertz = 1/s -force = N = newton = newtons = m*kg/s**2 -energy = J = joule = joules = N*m -power = W = watt = watts = J/s -pressure = Pa = pa = pascal = pascals = N/m**2 -charge = C = coulomb = coulombs = s*A -voltage = v = V = volt = volts = W/A -resistance = ohm = ohms = V/A -conductance = S = siemens = mho = mhos = A/V -capacitance = F = farad = farads = C/V -magnetic_flux = Wb = wb = weber = webers = J/A -magnetic_flux_density = T = tesla = teslas = V*s/m**2 -inductance = H = henry = henrys = V*s/A -speed = m/s -acceleration = m/s**2 -density = kg/m**3 - -# Common length units - -km = kilometer = kilometers = kilo*m -dm = decimeter = decimeters = deci*m -cm = centimeter = centimeters = centi*m -mm = millimeter = millimeters = milli*m -um = micrometer = micrometers = micron = microns = micro*m -nm = nanometer = nanometers = nano*m -pm = picometer = picometers = pico*m - -ft = foot = feet = Rational('0.3048')*m -inch = inches = Rational('25.4')*mm -yd = yard = yards = 3*ft -mi = mile = miles = 5280*ft - - -# Common volume and area units - -l = liter = liters = m**3 / 1000 -dl = deciliter = deciliters = deci*l -cl = centiliter = centiliters = centi*l -ml = milliliter = milliliters = milli*l - - -# Common time units - -ms = millisecond = milliseconds = milli*s -us = microsecond = microseconds = micro*s -ns = nanosecond = nanoseconds = nano*s -ps = picosecond = picoseconds = pico*s - -minute = minutes = 60*s -h = hour = hours = 60*minute -day = days = 24*hour - -sidereal_year = sidereal_years = Rational('31558149.540')*s -tropical_year = tropical_years = Rational('365.24219')*day -common_year = common_years = Rational('365')*day -julian_year = julian_years = Rational('365.25')*day - -year = years = tropical_year - - -# Common mass units - -g = gram = grams = kilogram / kilo -mg = milligram = milligrams = milli * g -ug = microgram = micrograms = micro * g - - -#---------------------------------------------------------------------------- -# Physical constants -# -c = speed_of_light = 299792458 * m/s -G = gravitational_constant = Rational('6.67428') * ten**-11 * m**3 / kg / s**2 -u0 = magnetic_constant = 4*pi * ten**-7 * N/A**2 -e0 = electric_constant = 1/(u0 * c**2) -Z0 = vacuum_impedance = u0 * c - -planck = Rational('6.62606896') * ten**-34 * J*s -hbar = planck / (2*pi) - -avogadro_number = Rational('6.02214179') * 10**23 -avogadro = avogadro_constant = avogadro_number / mol -boltzmann = Rational('1.3806505') * ten**-23 * J / K - -gee = gees = Rational('9.80665') * m/s**2 -atmosphere = atmospheres = atm = 101325 * pascal - -kPa = kilo*Pa -bar = bars = 100*kPa -pound = pounds = 0.45359237 * kg * gee # exact -psi = pound / inch ** 2 -dHg0 = 13.5951 # approx value at 0 C -mmHg = dHg0 * 9.80665 * Pa -amu = amus = gram / avogadro / mol -mmu = mmus = gram / mol -quart = quarts = 231 * inch**3 -eV = 1.602176487e-19 * J - -# Other convenient units and magnitudes - -ly = lightyear = lightyears = c*julian_year -au = astronomical_unit = astronomical_units = 149597870691*m - - -
    [docs]def find_unit(quantity): - """ - Return a list of matching units names. - if quantity is a string -- units containing the string `quantity` - if quantity is a unit -- units having matching base units - - Examples - ======== - - >>> from sympy.physics import units as u - >>> u.find_unit('charge') - ['charge'] - >>> u.find_unit(u.charge) - ['C', 'charge', 'coulomb', 'coulombs'] - >>> u.find_unit('volt') - ['volt', 'volts', 'voltage'] - >>> u.find_unit(u.inch**3)[:5] - ['l', 'cl', 'dl', 'ml', 'liter'] - """ - import sympy.physics.units as u - rv = [] - if isinstance(quantity, str): - rv = [i for i in dir(u) if quantity in i] - else: - units = quantity.as_coeff_Mul()[1] - for i in dir(u): - try: - if units == eval('u.' + i).as_coeff_Mul()[1]: - rv.append(str(i)) - except: - pass - return sorted(rv, key=len) - -# Delete this so it doesn't pollute the namespace
    -del Rational, pi -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/physics/wigner.html b/dev-py3k/_modules/sympy/physics/wigner.html deleted file mode 100644 index 1ffe0cc217c..00000000000 --- a/dev-py3k/_modules/sympy/physics/wigner.html +++ /dev/null @@ -1,808 +0,0 @@ - - - - - - - - - - sympy.physics.wigner — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.physics.wigner

    -r"""
    -Wigner, Clebsch-Gordan, Racah, and Gaunt coefficients
    -
    -Collection of functions for calculating Wigner 3j, 6j, 9j,
    -Clebsch-Gordan, Racah as well as Gaunt coefficients exactly, all
    -evaluating to a rational number times the square root of a rational
    -number [Rasch03]_.
    -
    -Please see the description of the individual functions for further
    -details and examples.
    -
    -REFERENCES:
    -
    -.. [Rasch03] J. Rasch and A. C. H. Yu, 'Efficient Storage Scheme for
    -  Pre-calculated Wigner 3j, 6j and Gaunt Coefficients', SIAM
    -  J. Sci. Comput. Volume 25, Issue 4, pp. 1416-1428 (2003)
    -
    -This code was taken from Sage with the permission of all authors:
    -
    -http://groups.google.com/group/sage-devel/browse_thread/thread/33835976efbb3b7f
    -
    -AUTHORS:
    -
    -- Jens Rasch (2009-03-24): initial version for Sage
    -
    -- Jens Rasch (2009-05-31): updated to sage-4.0
    -
    -Copyright (C) 2008 Jens Rasch <jyr2000@gmail.com>
    -"""
    -from sympy import Integer, pi, sqrt, sympify
    -#from sage.rings.complex_number import ComplexNumber
    -#from sage.rings.finite_rings.integer_mod import Mod
    -
    -# This list of precomputed factorials is needed to massively
    -# accelerate future calculations of the various coefficients
    -_Factlist = [1]
    -
    -
    -def _calc_factlist(nn):
    -    r"""
    -    Function calculates a list of precomputed factorials in order to
    -    massively accelerate future calculations of the various
    -    coefficients.
    -
    -    INPUT:
    -
    -    -  ``nn`` -  integer, highest factorial to be computed
    -
    -    OUTPUT:
    -
    -    list of integers -- the list of precomputed factorials
    -
    -    EXAMPLES:
    -
    -    Calculate list of factorials::
    -
    -        sage: from sage.functions.wigner import _calc_factlist
    -        sage: _calc_factlist(10)
    -        [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800]
    -    """
    -    if nn >= len(_Factlist):
    -        for ii in range(len(_Factlist), nn + 1):
    -            _Factlist.append(_Factlist[ii - 1] * ii)
    -    return _Factlist[:int(nn) + 1]
    -
    -
    -
    [docs]def wigner_3j(j_1, j_2, j_3, m_1, m_2, m_3, prec=None): - r""" - Calculate the Wigner 3j symbol `Wigner3j(j_1,j_2,j_3,m_1,m_2,m_3)`. - - INPUT: - - - ``j_1``, ``j_2``, ``j_3``, ``m_1``, ``m_2``, ``m_3`` - integer or half integer - - - ``prec`` - precision, default: ``None``. Providing a precision can - drastically speed up the calculation. - - OUTPUT: - - Rational number times the square root of a rational number - (if ``prec=None``), or real number if a precision is given. - - Examples - ======== - - >>> from sympy.physics.wigner import wigner_3j - >>> wigner_3j(2, 6, 4, 0, 0, 0) - sqrt(715)/143 - >>> wigner_3j(2, 6, 4, 0, 0, 1) - 0 - - It is an error to have arguments that are not integer or half - integer values:: - - sage: wigner_3j(2.1, 6, 4, 0, 0, 0) - Traceback (most recent call last): - ... - ValueError: j values must be integer or half integer - sage: wigner_3j(2, 6, 4, 1, 0, -1.1) - Traceback (most recent call last): - ... - ValueError: m values must be integer or half integer - - NOTES: - - The Wigner 3j symbol obeys the following symmetry rules: - - - invariant under any permutation of the columns (with the - exception of a sign change where `J:=j_1+j_2+j_3`): - - .. math:: - - Wigner3j(j_1,j_2,j_3,m_1,m_2,m_3) - =Wigner3j(j_3,j_1,j_2,m_3,m_1,m_2) - =Wigner3j(j_2,j_3,j_1,m_2,m_3,m_1) - =(-1)^J Wigner3j(j_3,j_2,j_1,m_3,m_2,m_1) - =(-1)^J Wigner3j(j_1,j_3,j_2,m_1,m_3,m_2) - =(-1)^J Wigner3j(j_2,j_1,j_3,m_2,m_1,m_3) - - - invariant under space inflection, i.e. - - .. math:: - - Wigner3j(j_1,j_2,j_3,m_1,m_2,m_3) - =(-1)^J Wigner3j(j_1,j_2,j_3,-m_1,-m_2,-m_3) - - - symmetric with respect to the 72 additional symmetries based on - the work by [Regge58]_ - - - zero for `j_1`, `j_2`, `j_3` not fulfilling triangle relation - - - zero for `m_1 + m_2 + m_3 \neq 0` - - - zero for violating any one of the conditions - `j_1 \ge |m_1|`, `j_2 \ge |m_2|`, `j_3 \ge |m_3|` - - ALGORITHM: - - This function uses the algorithm of [Edmonds74]_ to calculate the - value of the 3j symbol exactly. Note that the formula contains - alternating sums over large factorials and is therefore unsuitable - for finite precision arithmetic and only useful for a computer - algebra system [Rasch03]_. - - REFERENCES: - - .. [Regge58] 'Symmetry Properties of Clebsch-Gordan Coefficients', - T. Regge, Nuovo Cimento, Volume 10, pp. 544 (1958) - - .. [Edmonds74] 'Angular Momentum in Quantum Mechanics', - A. R. Edmonds, Princeton University Press (1974) - - AUTHORS: - - - Jens Rasch (2009-03-24): initial version - """ - if int(j_1 * 2) != j_1 * 2 or int(j_2 * 2) != j_2 * 2 or \ - int(j_3 * 2) != j_3 * 2: - raise ValueError("j values must be integer or half integer") - if int(m_1 * 2) != m_1 * 2 or int(m_2 * 2) != m_2 * 2 or \ - int(m_3 * 2) != m_3 * 2: - raise ValueError("m values must be integer or half integer") - if m_1 + m_2 + m_3 != 0: - return 0 - prefid = Integer((-1) ** int(j_1 - j_2 - m_3)) - m_3 = -m_3 - a1 = j_1 + j_2 - j_3 - if a1 < 0: - return 0 - a2 = j_1 - j_2 + j_3 - if a2 < 0: - return 0 - a3 = -j_1 + j_2 + j_3 - if a3 < 0: - return 0 - if (abs(m_1) > j_1) or (abs(m_2) > j_2) or (abs(m_3) > j_3): - return 0 - - maxfact = max(j_1 + j_2 + j_3 + 1, j_1 + abs(m_1), j_2 + abs(m_2), - j_3 + abs(m_3)) - _calc_factlist(int(maxfact)) - - argsqrt = Integer(_Factlist[int(j_1 + j_2 - j_3)] * - _Factlist[int(j_1 - j_2 + j_3)] * - _Factlist[int(-j_1 + j_2 + j_3)] * - _Factlist[int(j_1 - m_1)] * - _Factlist[int(j_1 + m_1)] * - _Factlist[int(j_2 - m_2)] * - _Factlist[int(j_2 + m_2)] * - _Factlist[int(j_3 - m_3)] * - _Factlist[int(j_3 + m_3)]) / \ - _Factlist[int(j_1 + j_2 + j_3 + 1)] - - ressqrt = sqrt(argsqrt) - if ressqrt.is_complex: - ressqrt = ressqrt.as_real_imag()[0] - - imin = max(-j_3 + j_1 + m_2, -j_3 + j_2 - m_1, 0) - imax = min(j_2 + m_2, j_1 - m_1, j_1 + j_2 - j_3) - sumres = 0 - for ii in range(int(imin), int(imax) + 1): - den = _Factlist[ii] * \ - _Factlist[int(ii + j_3 - j_1 - m_2)] * \ - _Factlist[int(j_2 + m_2 - ii)] * \ - _Factlist[int(j_1 - ii - m_1)] * \ - _Factlist[int(ii + j_3 - j_2 + m_1)] * \ - _Factlist[int(j_1 + j_2 - j_3 - ii)] - sumres = sumres + Integer((-1) ** ii) / den - - res = ressqrt * sumres * prefid - return res - -
    -
    [docs]def clebsch_gordan(j_1, j_2, j_3, m_1, m_2, m_3, prec=None): - r""" - Calculates the Clebsch-Gordan coefficient - `\langle j_1 m_1 \; j_2 m_2 | j_3 m_3 \rangle`. - - The reference for this function is [Edmonds74]_. - - INPUT: - - - ``j_1``, ``j_2``, ``j_3``, ``m_1``, ``m_2``, ``m_3`` - integer or half integer - - - ``prec`` - precision, default: ``None``. Providing a precision can - drastically speed up the calculation. - - OUTPUT: - - Rational number times the square root of a rational number - (if ``prec=None``), or real number if a precision is given. - - EXAMPLES:: - - >>> from sympy import S - >>> from sympy.physics.wigner import clebsch_gordan - >>> clebsch_gordan(S(3)/2, S(1)/2, 2, S(3)/2, S(1)/2, 2) - 1 - >>> clebsch_gordan(S(3)/2, S(1)/2, 1, S(3)/2, -S(1)/2, 1) - sqrt(3)/2 - >>> clebsch_gordan(S(3)/2, S(1)/2, 1, -S(1)/2, S(1)/2, 0) - -sqrt(2)/2 - - NOTES: - - The Clebsch-Gordan coefficient will be evaluated via its relation - to Wigner 3j symbols: - - .. math:: - - \langle j_1 m_1 \; j_2 m_2 | j_3 m_3 \rangle - =(-1)^{j_1-j_2+m_3} \sqrt{2j_3+1} \; - Wigner3j(j_1,j_2,j_3,m_1,m_2,-m_3) - - See also the documentation on Wigner 3j symbols which exhibit much - higher symmetry relations than the Clebsch-Gordan coefficient. - - AUTHORS: - - - Jens Rasch (2009-03-24): initial version - """ - res = (-1) ** sympify(j_1 - j_2 + m_3) * sqrt(2 * j_3 + 1) * \ - wigner_3j(j_1, j_2, j_3, m_1, m_2, -m_3, prec) - return res - -
    -def _big_delta_coeff(aa, bb, cc, prec=None): - r""" - Calculates the Delta coefficient of the 3 angular momenta for - Racah symbols. Also checks that the differences are of integer - value. - - INPUT: - - - ``aa`` - first angular momentum, integer or half integer - - - ``bb`` - second angular momentum, integer or half integer - - - ``cc`` - third angular momentum, integer or half integer - - - ``prec`` - precision of the ``sqrt()`` calculation - - OUTPUT: - - double - Value of the Delta coefficient - - EXAMPLES:: - - sage: from sage.functions.wigner import _big_delta_coeff - sage: _big_delta_coeff(1,1,1) - 1/2*sqrt(1/6) - """ - if int(aa + bb - cc) != (aa + bb - cc): - raise ValueError("j values must be integer or half integer and fulfill the triangle relation") - if int(aa + cc - bb) != (aa + cc - bb): - raise ValueError("j values must be integer or half integer and fulfill the triangle relation") - if int(bb + cc - aa) != (bb + cc - aa): - raise ValueError("j values must be integer or half integer and fulfill the triangle relation") - if (aa + bb - cc) < 0: - return 0 - if (aa + cc - bb) < 0: - return 0 - if (bb + cc - aa) < 0: - return 0 - - maxfact = max(aa + bb - cc, aa + cc - bb, bb + cc - aa, aa + bb + cc + 1) - _calc_factlist(maxfact) - - argsqrt = Integer(_Factlist[int(aa + bb - cc)] * - _Factlist[int(aa + cc - bb)] * - _Factlist[int(bb + cc - aa)]) / \ - Integer(_Factlist[int(aa + bb + cc + 1)]) - - ressqrt = sqrt(argsqrt) - if prec: - ressqrt = ressqrt.evalf(prec).as_real_imag()[0] - return ressqrt - - -
    [docs]def racah(aa, bb, cc, dd, ee, ff, prec=None): - r""" - Calculate the Racah symbol `W(a,b,c,d;e,f)`. - - INPUT: - - - ``a``, ..., ``f`` - integer or half integer - - - ``prec`` - precision, default: ``None``. Providing a precision can - drastically speed up the calculation. - - OUTPUT: - - Rational number times the square root of a rational number - (if ``prec=None``), or real number if a precision is given. - - Examples - ======== - - >>> from sympy.physics.wigner import racah - >>> racah(3,3,3,3,3,3) - -1/14 - - NOTES: - - The Racah symbol is related to the Wigner 6j symbol: - - .. math:: - - Wigner6j(j_1,j_2,j_3,j_4,j_5,j_6) - =(-1)^{j_1+j_2+j_4+j_5} W(j_1,j_2,j_5,j_4,j_3,j_6) - - Please see the 6j symbol for its much richer symmetries and for - additional properties. - - ALGORITHM: - - This function uses the algorithm of [Edmonds74]_ to calculate the - value of the 6j symbol exactly. Note that the formula contains - alternating sums over large factorials and is therefore unsuitable - for finite precision arithmetic and only useful for a computer - algebra system [Rasch03]_. - - AUTHORS: - - - Jens Rasch (2009-03-24): initial version - """ - prefac = _big_delta_coeff(aa, bb, ee, prec) * \ - _big_delta_coeff(cc, dd, ee, prec) * \ - _big_delta_coeff(aa, cc, ff, prec) * \ - _big_delta_coeff(bb, dd, ff, prec) - if prefac == 0: - return 0 - imin = max(aa + bb + ee, cc + dd + ee, aa + cc + ff, bb + dd + ff) - imax = min(aa + bb + cc + dd, aa + dd + ee + ff, bb + cc + ee + ff) - - maxfact = max(imax + 1, aa + bb + cc + dd, aa + dd + ee + ff, - bb + cc + ee + ff) - _calc_factlist(maxfact) - - sumres = 0 - for kk in range(imin, imax + 1): - den = _Factlist[int(kk - aa - bb - ee)] * \ - _Factlist[int(kk - cc - dd - ee)] * \ - _Factlist[int(kk - aa - cc - ff)] * \ - _Factlist[int(kk - bb - dd - ff)] * \ - _Factlist[int(aa + bb + cc + dd - kk)] * \ - _Factlist[int(aa + dd + ee + ff - kk)] * \ - _Factlist[int(bb + cc + ee + ff - kk)] - sumres = sumres + Integer((-1) ** kk * _Factlist[kk + 1]) / den - - res = prefac * sumres * (-1) ** int(aa + bb + cc + dd) - return res - -
    -
    [docs]def wigner_6j(j_1, j_2, j_3, j_4, j_5, j_6, prec=None): - r""" - Calculate the Wigner 6j symbol `Wigner6j(j_1,j_2,j_3,j_4,j_5,j_6)`. - - INPUT: - - - ``j_1``, ..., ``j_6`` - integer or half integer - - - ``prec`` - precision, default: ``None``. Providing a precision can - drastically speed up the calculation. - - OUTPUT: - - Rational number times the square root of a rational number - (if ``prec=None``), or real number if a precision is given. - - Examples - ======== - - >>> from sympy.physics.wigner import wigner_6j - >>> wigner_6j(3,3,3,3,3,3) - -1/14 - >>> wigner_6j(5,5,5,5,5,5) - 1/52 - - It is an error to have arguments that are not integer or half - integer values or do not fulfill the triangle relation:: - - sage: wigner_6j(2.5,2.5,2.5,2.5,2.5,2.5) - Traceback (most recent call last): - ... - ValueError: j values must be integer or half integer and fulfill the triangle relation - sage: wigner_6j(0.5,0.5,1.1,0.5,0.5,1.1) - Traceback (most recent call last): - ... - ValueError: j values must be integer or half integer and fulfill the triangle relation - - NOTES: - - The Wigner 6j symbol is related to the Racah symbol but exhibits - more symmetries as detailed below. - - .. math:: - - Wigner6j(j_1,j_2,j_3,j_4,j_5,j_6) - =(-1)^{j_1+j_2+j_4+j_5} W(j_1,j_2,j_5,j_4,j_3,j_6) - - The Wigner 6j symbol obeys the following symmetry rules: - - - Wigner 6j symbols are left invariant under any permutation of - the columns: - - .. math:: - - Wigner6j(j_1,j_2,j_3,j_4,j_5,j_6) - =Wigner6j(j_3,j_1,j_2,j_6,j_4,j_5) - =Wigner6j(j_2,j_3,j_1,j_5,j_6,j_4) - =Wigner6j(j_3,j_2,j_1,j_6,j_5,j_4) - =Wigner6j(j_1,j_3,j_2,j_4,j_6,j_5) - =Wigner6j(j_2,j_1,j_3,j_5,j_4,j_6) - - - They are invariant under the exchange of the upper and lower - arguments in each of any two columns, i.e. - - .. math:: - - Wigner6j(j_1,j_2,j_3,j_4,j_5,j_6) - =Wigner6j(j_1,j_5,j_6,j_4,j_2,j_3) - =Wigner6j(j_4,j_2,j_6,j_1,j_5,j_3) - =Wigner6j(j_4,j_5,j_3,j_1,j_2,j_6) - - - additional 6 symmetries [Regge59]_ giving rise to 144 symmetries - in total - - - only non-zero if any triple of `j`'s fulfill a triangle relation - - ALGORITHM: - - This function uses the algorithm of [Edmonds74]_ to calculate the - value of the 6j symbol exactly. Note that the formula contains - alternating sums over large factorials and is therefore unsuitable - for finite precision arithmetic and only useful for a computer - algebra system [Rasch03]_. - - REFERENCES: - - .. [Regge59] 'Symmetry Properties of Racah Coefficients', - T. Regge, Nuovo Cimento, Volume 11, pp. 116 (1959) - """ - res = (-1) ** int(j_1 + j_2 + j_4 + j_5) * \ - racah(j_1, j_2, j_5, j_4, j_3, j_6, prec) - return res - -
    -
    [docs]def wigner_9j(j_1, j_2, j_3, j_4, j_5, j_6, j_7, j_8, j_9, prec=None): - r""" - Calculate the Wigner 9j symbol - `Wigner9j(j_1,j_2,j_3,j_4,j_5,j_6,j_7,j_8,j_9)`. - - INPUT: - - - ``j_1``, ..., ``j_9`` - integer or half integer - - - ``prec`` - precision, default: ``None``. Providing a precision can - drastically speed up the calculation. - - OUTPUT: - - Rational number times the square root of a rational number - (if ``prec=None``), or real number if a precision is given. - - Examples - ======== - - >>> from sympy.physics.wigner import wigner_9j - >>> wigner_9j(1,1,1, 1,1,1, 1,1,0 ,prec=64) # ==1/18 - 0.05555555... - - It is an error to have arguments that are not integer or half - integer values or do not fulfill the triangle relation:: - - sage: wigner_9j(0.5,0.5,0.5, 0.5,0.5,0.5, 0.5,0.5,0.5,prec=64) - Traceback (most recent call last): - ... - ValueError: j values must be integer or half integer and fulfill the triangle relation - sage: wigner_9j(1,1,1, 0.5,1,1.5, 0.5,1,2.5,prec=64) - Traceback (most recent call last): - ... - ValueError: j values must be integer or half integer and fulfill the triangle relation - - ALGORITHM: - - This function uses the algorithm of [Edmonds74]_ to calculate the - value of the 3j symbol exactly. Note that the formula contains - alternating sums over large factorials and is therefore unsuitable - for finite precision arithmetic and only useful for a computer - algebra system [Rasch03]_. - """ - imin = 0 - imax = min(j_1 + j_9, j_2 + j_6, j_4 + j_8) - - sumres = 0 - for kk in range(imin, imax + 1): - sumres = sumres + (2 * kk + 1) * \ - racah(j_1, j_2, j_9, j_6, j_3, kk, prec) * \ - racah(j_4, j_6, j_8, j_2, j_5, kk, prec) * \ - racah(j_1, j_4, j_9, j_8, j_7, kk, prec) - return sumres - -
    -
    [docs]def gaunt(l_1, l_2, l_3, m_1, m_2, m_3, prec=None): - r""" - Calculate the Gaunt coefficient. - - The Gaunt coefficient is defined as the integral over three - spherical harmonics: - - .. math:: - - Y(j_1,j_2,j_3,m_1,m_2,m_3) - =\int Y_{l_1,m_1}(\Omega) - Y_{l_2,m_2}(\Omega) Y_{l_3,m_3}(\Omega) d\Omega - =\sqrt{(2l_1+1)(2l_2+1)(2l_3+1)/(4\pi)} - \; Y(j_1,j_2,j_3,0,0,0) \; Y(j_1,j_2,j_3,m_1,m_2,m_3) - - INPUT: - - - ``l_1``, ``l_2``, ``l_3``, ``m_1``, ``m_2``, ``m_3`` - integer - - - ``prec`` - precision, default: ``None``. Providing a precision can - drastically speed up the calculation. - - OUTPUT: - - Rational number times the square root of a rational number - (if ``prec=None``), or real number if a precision is given. - - Examples - ======== - - >>> from sympy.physics.wigner import gaunt - >>> gaunt(1,0,1,1,0,-1) - -1/(2*sqrt(pi)) - >>> gaunt(1000,1000,1200,9,3,-12).n(64) - 0.00689500421922113448... - - It is an error to use non-integer values for `l` and `m`:: - - sage: gaunt(1.2,0,1.2,0,0,0) - Traceback (most recent call last): - ... - ValueError: l values must be integer - sage: gaunt(1,0,1,1.1,0,-1.1) - Traceback (most recent call last): - ... - ValueError: m values must be integer - - NOTES: - - The Gaunt coefficient obeys the following symmetry rules: - - - invariant under any permutation of the columns - - .. math:: - Y(j_1,j_2,j_3,m_1,m_2,m_3) - =Y(j_3,j_1,j_2,m_3,m_1,m_2) - =Y(j_2,j_3,j_1,m_2,m_3,m_1) - =Y(j_3,j_2,j_1,m_3,m_2,m_1) - =Y(j_1,j_3,j_2,m_1,m_3,m_2) - =Y(j_2,j_1,j_3,m_2,m_1,m_3) - - - invariant under space inflection, i.e. - - .. math:: - Y(j_1,j_2,j_3,m_1,m_2,m_3) - =Y(j_1,j_2,j_3,-m_1,-m_2,-m_3) - - - symmetric with respect to the 72 Regge symmetries as inherited - for the `3j` symbols [Regge58]_ - - - zero for `l_1`, `l_2`, `l_3` not fulfilling triangle relation - - - zero for violating any one of the conditions: `l_1 \ge |m_1|`, - `l_2 \ge |m_2|`, `l_3 \ge |m_3|` - - - non-zero only for an even sum of the `l_i`, i.e. - `J = l_1 + l_2 + l_3 = 2n` for `n` in `\mathbb{N}` - - ALGORITHM: - - This function uses the algorithm of [Liberatodebrito82]_ to - calculate the value of the Gaunt coefficient exactly. Note that - the formula contains alternating sums over large factorials and is - therefore unsuitable for finite precision arithmetic and only - useful for a computer algebra system [Rasch03]_. - - REFERENCES: - - .. [Liberatodebrito82] 'FORTRAN program for the integral of three - spherical harmonics', A. Liberato de Brito, - Comput. Phys. Commun., Volume 25, pp. 81-85 (1982) - - AUTHORS: - - - Jens Rasch (2009-03-24): initial version for Sage - """ - if int(l_1) != l_1 or int(l_2) != l_2 or int(l_3) != l_3: - raise ValueError("l values must be integer") - if int(m_1) != m_1 or int(m_2) != m_2 or int(m_3) != m_3: - raise ValueError("m values must be integer") - - bigL = (l_1 + l_2 + l_3) // 2 - a1 = l_1 + l_2 - l_3 - if a1 < 0: - return 0 - a2 = l_1 - l_2 + l_3 - if a2 < 0: - return 0 - a3 = -l_1 + l_2 + l_3 - if a3 < 0: - return 0 - if (2 * bigL) % 2 != 0: - return 0 - if (m_1 + m_2 + m_3) != 0: - return 0 - if (abs(m_1) > l_1) or (abs(m_2) > l_2) or (abs(m_3) > l_3): - return 0 - - imin = max(-l_3 + l_1 + m_2, -l_3 + l_2 - m_1, 0) - imax = min(l_2 + m_2, l_1 - m_1, l_1 + l_2 - l_3) - - maxfact = max(l_1 + l_2 + l_3 + 1, imax + 1) - _calc_factlist(maxfact) - - argsqrt = (2 * l_1 + 1) * (2 * l_2 + 1) * (2 * l_3 + 1) * \ - _Factlist[l_1 - m_1] * _Factlist[l_1 + m_1] * _Factlist[l_2 - m_2] * \ - _Factlist[l_2 + m_2] * _Factlist[l_3 - m_3] * _Factlist[l_3 + m_3] / \ - (4*pi) - ressqrt = sqrt(argsqrt) - - prefac = Integer(_Factlist[bigL] * _Factlist[l_2 - l_1 + l_3] * - _Factlist[l_1 - l_2 + l_3] * _Factlist[l_1 + l_2 - l_3])/ \ - _Factlist[2 * bigL + 1]/ \ - (_Factlist[bigL - l_1] * - _Factlist[bigL - l_2] * _Factlist[bigL - l_3]) - - sumres = 0 - for ii in range(imin, imax + 1): - den = _Factlist[ii] * _Factlist[ii + l_3 - l_1 - m_2] * \ - _Factlist[l_2 + m_2 - ii] * _Factlist[l_1 - ii - m_1] * \ - _Factlist[ii + l_3 - l_2 + m_1] * _Factlist[l_1 + l_2 - l_3 - ii] - sumres = sumres + Integer((-1) ** ii) / den - - res = ressqrt * prefac * sumres * (-1) ** (bigL + l_3 + m_1 - m_2) - if prec is not None: - res = res.n(prec) - return res
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/plotting/plot.html b/dev-py3k/_modules/sympy/plotting/plot.html deleted file mode 100644 index 647c800a7ac..00000000000 --- a/dev-py3k/_modules/sympy/plotting/plot.html +++ /dev/null @@ -1,1781 +0,0 @@ - - - - - - - - - - sympy.plotting.plot — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.plotting.plot

    -"""Plotting module for Sympy.
    -
    -A plot is represented by the ``Plot`` class that contains a reference to the
    -backend and a list of the data series to be plotted. The data series are
    -instances of classes meant to simplify getting points and meshes from sympy
    -expressions. ``plot_backends`` is a dictionary with all the backends.
    -
    -This module gives only the essential. For all the fancy stuff use directly
    -the backend. You can get the backend wrapper for every plot from the
    -``_backend`` attribute. Moreover the data series classes have various useful
    -methods like ``get_points``, ``get_segments``, ``get_meshes``, etc, that may
    -be useful if you wish to use another plotting library.
    -
    -Especially if you need publication ready graphs and this module is not enough
    -for you - just get the ``_backend`` attribute and add whatever you want
    -directly to it. In the case of matplotlib (the common way to graph data in
    -python) just copy ``_backend.fig`` which is the figure and ``_backend.ax``
    -which is the axis and work on them as you would on any other matplotlib object.
    -
    -Simplicity of code takes much greater importance than performance. Don't use it
    -if you care at all about performance. A new backend instance is initialized
    -every time you call ``show()`` and the old one is left to the garbage collector.
    -"""
    -
    -from inspect import getargspec
    -from itertools import chain
    -from sympy import sympify, Expr, Tuple, Dummy
    -from sympy.external import import_module
    -from sympy.core.compatibility import set_union
    -import warnings
    -from .experimental_lambdify import (vectorized_lambdify, lambdify)
    -import collections
    -
    -#TODO probably all of the imports after this line can be put inside function to
    -# speed up the `from sympy import *` command.
    -np = import_module('numpy')
    -
    -# Backend specific imports - matplotlib
    -matplotlib = import_module('matplotlib',
    -    __import__kwargs={'fromlist': ['pyplot', 'cm', 'collections']},
    -    min_module_version='1.0.0', catch=(RuntimeError,))
    -if matplotlib:
    -    plt = matplotlib.pyplot
    -    cm = matplotlib.cm
    -    LineCollection = matplotlib.collections.LineCollection
    -    mpl_toolkits = import_module('mpl_toolkits',
    -            __import__kwargs={'fromlist': ['mplot3d']})
    -    Axes3D = mpl_toolkits.mplot3d.Axes3D
    -    art3d = mpl_toolkits.mplot3d.art3d
    -    ListedColormap = matplotlib.colors.ListedColormap
    -
    -# Backend specific imports - textplot
    -from sympy.plotting.textplot import textplot
    -
    -# Global variable
    -# Set to False when running tests / doctests so that the plots don't
    -# show.
    -_show = True
    -
    -
    -def unset_show():
    -    global _show
    -    _show = False
    -
    -##############################################################################
    -# The public interface
    -##############################################################################
    -
    -
    -
    [docs]class Plot(object): - """The central class of the plotting module. - - For interactive work the function ``plot`` is better suited. - - This class permits the plotting of sympy expressions using numerous - backends (matplotlib, textplot, the old pyglet module for sympy, Google - charts api, etc). - - The figure can contain an arbitrary number of plots of sympy expressions, - lists of coordinates of points, etc. Plot has a private attribute _series that - contains all data series to be plotted (expressions for lines or surfaces, - lists of points, etc (all subclasses of BaseSeries)). Those data series are - instances of classes not imported by ``from sympy import *``. - - The customization of the figure is on two levels. Global options that - concern the figure as a whole (eg title, xlabel, scale, etc) and - per-data series options (eg name) and aesthetics (eg. color, point shape, - line type, etc.). - - The difference between options and aesthetics is that an aesthetic can be - a function of the coordinates (or parameters in a parametric plot). The - supported values for an aesthetic are: - - None (the backend uses default values) - - a constant - - a function of one variable (the first coordinate or parameter) - - a function of two variables (the first and second coordinate or - parameters) - - a function of three variables (only in nonparametric 3D plots) - Their implementation depends on the backend so they may not work in some - backends. - - If the plot is parametric and the arity of the aesthetic function permits - it the aesthetic is calculated over parameters and not over coordinates. - If the arity does not permit calculation over parameters the calculation is - done over coordinates. - - Only cartesian coordinates are supported for the moment, but you can use - the parametric plots to plot in polar, spherical and cylindrical - coordinates. - - The arguments for the constructor Plot must be subclasses of BaseSeries. - - Any global option can be specified as a keyword argument. - - The global options for a figure are: - - - title : str - - xlabel : str - - ylabel : str - - legend : bool - - xscale : {'linear', 'log'} - - yscale : {'linear', 'log'} - - axis : bool - - axis_center : tuple of two floats or {'center', 'auto'} - - xlim : tuple of two floats - - ylim : tuple of two floats - - aspect_ratio : tuple of two floats or {'auto'} - - autoscale : bool - - margin : float in [0, 1] - - The per data series options and aesthetics are: - There are none in the base series. See below for options for subclasses. - - Some data series support additional aesthetics or options: - - ListSeries, LineOver1DRangeSeries, Parametric2DLineSeries, - Parametric3DLineSeries support the following: - - Aesthetics: - - - line_color : function which returns a float. - - options: - - - label : str - - steps : bool - - integers_only : bool - - SurfaceOver2DRangeSeries, ParametricSurfaceSeries support the following: - - aesthetics: - - - surface_color : function which returns a float. - """ - - def __init__(self, *args, **kwargs): - super(Plot, self).__init__() - - # Options for the graph as a whole. - # The possible values for each option are described in the docstring of - # Plot. They are based purely on convention, no checking is done. - self.title = None - self.xlabel = None - self.ylabel = None - self.aspect_ratio = 'auto' - self.xlim = None - self.ylim = None - self.axis_center = 'auto' - self.axis = True - self.xscale = 'linear' - self.yscale = 'linear' - self.legend = False - self.autoscale = True - self.margin = 0 - - # Contains the data objects to be plotted. The backend should be smart - # enough to iterate over this list. - self._series = [] - self._series.extend(args) - - # The backend type. On every show() a new backend instance is created - # in self._backend which is tightly coupled to the Plot instance - # (thanks to the parent attribute of the backend). - self.backend = DefaultBackend - - # The keyword arguments should only contain options for the plot. - for key, val in kwargs.items(): - if hasattr(self, key): - setattr(self, key, val) - - def show(self): - # TODO move this to the backend (also for save) - if hasattr(self, '_backend'): - self._backend.close() - self._backend = self.backend(self) - self._backend.show() - - def save(self, path): - if hasattr(self, '_backend'): - self._backend.close() - self._backend = self.backend(self) - self._backend.save(path) - - def __str__(self): - series_strs = [('[%d]: ' % i) + str(s) - for i, s in enumerate(self._series)] - return 'Plot object containing:\n' + '\n'.join(series_strs) - - def __getitem__(self, index): - return self._series[index] - - def __setitem__(self, index, *args): - if len(args) == 1 and isinstance(args[0], BaseSeries): - self._series[index] = args - - def __delitem__(self, index): - del self._series[index] - -
    [docs] def append(self, *args): - """Adds one more graph to the figure.""" - if len(args) == 1 and isinstance(args[0], BaseSeries): - self._series.append(*args) - else: - self._series.append(Series(*args)) -
    -
    [docs] def extend(self, arg): - """Adds the series from another plot or a list of series.""" - if isinstance(arg, Plot): - self._series.extend(arg._series) - else: - self._series.extend(arg) - - -############################################################################## -# Data Series -############################################################################## -#TODO more general way to calculate aesthetics (see get_color_array) - -### The base class for all series
    -
    [docs]class BaseSeries(object): - """Base class for the data objects containing stuff to be plotted. - - The backend should check if it supports the data series that it's given. - (eg TextBackend supports only LineOver1DRange). - It's the backend responsibility to know how to use the class of - data series that it's given. - - Some data series classes are grouped (using a class attribute like is_2Dline) - according to the api they present (based only on convention). The backend is - not obliged to use that api (eg. The LineOver1DRange belongs to the - is_2Dline group and presents the get_points method, but the - TextBackend does not use the get_points method). - """ - - # Some flags follow. The rationale for using flags instead of checking base - # classes is that setting multiple flags is simpler than multiple - # inheritance. - - is_2Dline = False - # Some of the backends expect: - # - get_points returning 1D np.arrays list_x, list_y - # - get_segments returning np.array (done in Line2DBaseSeries) - # - get_color_array returning 1D np.array (done in Line2DBaseSeries) - # with the colors calculated at the points from get_points - - is_3Dline = False - # Some of the backends expect: - # - get_points returning 1D np.arrays list_x, list_y, list_y - # - get_segments returning np.array (done in Line2DBaseSeries) - # - get_color_array returning 1D np.array (done in Line2DBaseSeries) - # with the colors calculated at the points from get_points - - is_3Dsurface = False - # Some of the backends expect: - # - get_meshes returning mesh_x, mesh_y, mesh_z (2D np.arrays) - # - get_points an alias for get_meshes - - is_contour = False - # Some of the backends expect: - # - get_meshes returning mesh_x, mesh_y, mesh_z (2D np.arrays) - # - get_points an alias for get_meshes - - is_implicit = False - # Some of the backends expect: - # - get_meshes returning mesh_x (1D array), mesh_y(1D array, - # mesh_z (2D np.arrays) - # - get_points an alias for get_meshes - #Different from is_contour as the colormap in backend will be - #different - - is_parametric = False - # The calculation of aesthetics expects: - # - get_parameter_points returning one or two np.arrays (1D or 2D) - # used for calculation aesthetics - - def __init__(self): - super(BaseSeries, self).__init__() - - @property - def is_3D(self): - flags3D = [ - self.is_3Dline, - self.is_3Dsurface - ] - return any(flags3D) - - @property - def is_line(self): - flagslines = [ - self.is_2Dline, - self.is_3Dline - ] - return any(flagslines) - - -### 2D lines
    -
    [docs]class Line2DBaseSeries(BaseSeries): - """A base class for 2D lines. - - - adding the label, steps and only_integers options - - making is_2Dline true - - defining get_segments and get_color_array - """ - - is_2Dline = True - - _dim = 2 - - def __init__(self): - super(Line2DBaseSeries, self).__init__() - self.label = None - self.steps = False - self.only_integers = False - self.line_color = None - - def get_segments(self): - points = self.get_points() - if self.steps is True: - x = np.array((points[0], points[0])).T.flatten()[1:] - y = np.array((points[1], points[1])).T.flatten()[:-1] - points = (x, y) - points = np.ma.array(points).T.reshape(-1, 1, self._dim) - return np.ma.concatenate([points[:-1], points[1:]], axis=1) - - def get_color_array(self): - c = self.line_color - if hasattr(c, '__call__'): - f = np.vectorize(c) - arity = len(getargspec(c)[0]) - if arity == 1 and self.is_parametric: - x = self.get_parameter_points() - return f(centers_of_segments(x)) - else: - variables = list(map(centers_of_segments, self.get_points())) - if arity == 1: - return f(variables[0]) - elif arity == 2: - return f(*variables[:2]) - else: # only if the line is 3D (otherwise raises an error) - return f(*variables) - else: - return c*np.ones(self.nb_of_points) - -
    -class List2DSeries(Line2DBaseSeries): - """Representation for a line consisting of list of points.""" - - def __init__(self, list_x, list_y): - super(List2DSeries, self).__init__() - self.list_x = np.array(list_x) - self.list_y = np.array(list_y) - self.label = 'list' - - def __str__(self): - return 'list plot' - - def get_points(self): - return (self.list_x, self.list_y) - - -
    [docs]class LineOver1DRangeSeries(Line2DBaseSeries): - """Representation for a line consisting of a sympy expression over a range.""" - - def __init__(self, expr, var_start_end, **kwargs): - super(LineOver1DRangeSeries, self).__init__() - self.expr = sympify(expr) - self.label = str(self.expr) - self.var = sympify(var_start_end[0]) - self.start = float(var_start_end[1]) - self.end = float(var_start_end[2]) - self.nb_of_points = kwargs.get('nb_of_points', 300) - self.adaptive = kwargs.get('adaptive', True) - self.depth = kwargs.get('depth', 12) - self.line_color = kwargs.get('line_color', None) - - def __str__(self): - return 'cartesian line: %s for %s over %s' % ( - str(self.expr), str(self.var), str((self.start, self.end))) - -
    [docs] def get_segments(self): - """ - Adaptively gets segments for plotting. - - The adaptive sampling is done by recursively checking if three - points are almost collinear. If they are not collinear, then more - points are added between those points. - - References - ========== - [1] Adaptive polygonal approximation of parametric curves, - Luiz Henrique de Figueiredo. - - """ - if self.only_integers or not self.adaptive: - return super(LineOver1DRangeSeries, self).get_segments() - else: - f = lambdify([self.var], self.expr) - list_segments = [] - - def sample(p, q, depth): - """ Samples recursively if three points are almost collinear. - For depth < 6, points are added irrespective of whether they - satisfy the collinearity condition or not. The maximum depth - allowed is 12. - """ - #Randomly sample to avoid aliasing. - random = 0.45 + np.random.rand() * 0.1 - xnew = p[0] + random * (q[0] - p[0]) - ynew = f(xnew) - new_point = np.array([xnew, ynew]) - - #Maximum depth - if depth > self.depth: - list_segments.append([p, q]) - - #Sample irrespective of whether the line is flat till the - #depth of 6. We are not using linspace to avoid aliasing. - elif depth < 6: - sample(p, new_point, depth + 1) - sample(new_point, q, depth + 1) - - #Sample ten points if complex values are encountered - #at both ends. If there is a real value in between, then - #sample those points further. - elif p[1] is None and q[1] is None: - xarray = np.linspace(p[0], q[0], 10) - yarray = list(map(f, xarray)) - if any(y is not None for y in yarray): - for i in len(yarray) - 1: - if yarray[i] is not None or yarray[i + 1] is not None: - sample([xarray[i], yarray[i]], - [xarray[i + 1], yarray[i + 1]], depth + 1) - - #Sample further if one of the end points in None( i.e. a complex - #value) or the three points are not almost collinear. - elif (p[1] is None or q[1] is None or new_point[1] is None - or not flat(p, new_point, q)): - sample(p, new_point, depth + 1) - sample(new_point, q, depth + 1) - else: - list_segments.append([p, q]) - - f_start = f(self.start) - f_end = f(self.end) - sample([self.start, f_start], [self.end, f_end], 0) - return list_segments -
    - def get_points(self): - if self.only_integers is True: - list_x = np.linspace(int(self.start), int(self.end), - num=int(self.end) - int(self.start) + 1) - else: - list_x = np.linspace(self.start, self.end, num=self.nb_of_points) - f = vectorized_lambdify([self.var], self.expr) - list_y = f(list_x) - return (list_x, list_y) - -
    -
    [docs]class Parametric2DLineSeries(Line2DBaseSeries): - """Representation for a line consisting of two parametric sympy expressions - over a range.""" - - is_parametric = True - - def __init__(self, expr_x, expr_y, var_start_end, **kwargs): - super(Parametric2DLineSeries, self).__init__() - self.expr_x = sympify(expr_x) - self.expr_y = sympify(expr_y) - self.label = "(%s, %s)" % (str(self.expr_x), str(self.expr_y)) - self.var = sympify(var_start_end[0]) - self.start = float(var_start_end[1]) - self.end = float(var_start_end[2]) - self.nb_of_points = kwargs.get('nb_of_points', 300) - self.adaptive = kwargs.get('adaptive', True) - self.depth = kwargs.get('depth', 12) - self.line_color = kwargs.get('line_color', None) - - def __str__(self): - return 'parametric cartesian line: (%s, %s) for %s over %s' % ( - str(self.expr_x), str(self.expr_y), str(self.var), - str((self.start, self.end))) - - def get_parameter_points(self): - return np.linspace(self.start, self.end, num=self.nb_of_points) - - def get_points(self): - param = self.get_parameter_points() - fx = vectorized_lambdify([self.var], self.expr_x) - fy = vectorized_lambdify([self.var], self.expr_y) - list_x = fx(param) - list_y = fy(param) - return (list_x, list_y) - -
    [docs] def get_segments(self): - """ - Adaptively gets segments for plotting. - - The adaptive sampling is done by recursively checking if three - points are almost collinear. If they are not collinear, then more - points are added between those points. - - References - ========== - [1] Adaptive polygonal approximation of parametric curves, - Luiz Henrique de Figueiredo. - - """ - if not self.adaptive: - return super(Parametric2DLineSeries, self).get_segments() - - f_x = lambdify([self.var], self.expr_x) - f_y = lambdify([self.var], self.expr_y) - list_segments = [] - - def sample(param_p, param_q, p, q, depth): - """ Samples recursively if three points are almost collinear. - For depth < 6, points are added irrespective of whether they - satisfy the collinearity condition or not. The maximum depth - allowed is 12. - """ - #Randomly sample to avoid aliasing. - random = 0.45 + np.random.rand() * 0.1 - param_new = param_p + random * (param_q - param_p) - xnew = f_x(param_new) - ynew = f_y(param_new) - new_point = np.array([xnew, ynew]) - - #Maximum depth - if depth > self.depth: - list_segments.append([p, q]) - - #Sample irrespective of whether the line is flat till the - #depth of 6. We are not using linspace to avoid aliasing. - elif depth < 6: - sample(param_p, param_new, p, new_point, depth + 1) - sample(param_new, param_q, new_point, q, depth + 1) - - #Sample ten points if complex values are encountered - #at both ends. If there is a real value in between, then - #sample those points further. - elif ((p[0] is None and q[1] is None) or - (p[1] is None and q[1] is None)): - param_array = np.linspace(param_p, param_q, 10) - x_array = list(map(f_x, param_array)) - y_array = list(map(f_y, param_array)) - if any(x is not None and y is not None - for x, y in zip(x_array, y_array)): - for i in len(y_array) - 1: - if ((x_array[i] is not None and y_array[i] is not None) or - (x_array[i + 1] is not None and y_array[i + 1] is not None)): - point_a = [x_array[i], y_array[i]] - point_b = [x_array[i + 1], y_array[i + 1]] - sample(param_array[i], param_array[i], point_a, - point_b, depth + 1) - - #Sample further if one of the end points in None( ie a complex - #value) or the three points are not almost collinear. - elif (p[0] is None or p[1] is None - or q[1] is None or q[0] is None - or not flat(p, new_point, q)): - sample(param_p, param_new, p, new_point, depth + 1) - sample(param_new, param_q, new_point, q, depth + 1) - else: - list_segments.append([p, q]) - - f_start_x = f_x(self.start) - f_start_y = f_y(self.start) - start = [f_start_x, f_start_y] - f_end_x = f_x(self.end) - f_end_y = f_y(self.end) - end = [f_end_x, f_end_y] - sample(self.start, self.end, start, end, 0) - return list_segments - - -### 3D lines
    -
    [docs]class Line3DBaseSeries(Line2DBaseSeries): - """A base class for 3D lines. - - Most of the stuff is derived from Line2DBaseSeries.""" - - is_2Dline = False - is_3Dline = True - _dim = 3 - - def __init__(self): - super(Line3DBaseSeries, self).__init__() - -
    -
    [docs]class Parametric3DLineSeries(Line3DBaseSeries): - """Representation for a 3D line consisting of two parametric sympy - expressions and a range.""" - - def __init__(self, expr_x, expr_y, expr_z, var_start_end, **kwargs): - super(Parametric3DLineSeries, self).__init__() - self.expr_x = sympify(expr_x) - self.expr_y = sympify(expr_y) - self.expr_z = sympify(expr_z) - self.label = "(%s, %s)" % (str(self.expr_x), str(self.expr_y)) - self.var = sympify(var_start_end[0]) - self.start = float(var_start_end[1]) - self.end = float(var_start_end[2]) - self.nb_of_points = kwargs.get('nb_of_points', 300) - self.line_color = kwargs.get('line_color', None) - - def __str__(self): - return '3D parametric cartesian line: (%s, %s, %s) for %s over %s' % ( - str(self.expr_x), str(self.expr_y), str(self.expr_z), - str(self.var), str((self.start, self.end))) - - def get_parameter_points(self): - return np.linspace(self.start, self.end, num=self.nb_of_points) - - def get_points(self): - param = self.get_parameter_points() - fx = vectorized_lambdify([self.var], self.expr_x) - fy = vectorized_lambdify([self.var], self.expr_y) - fz = vectorized_lambdify([self.var], self.expr_z) - list_x = fx(param) - list_y = fy(param) - list_z = fz(param) - return (list_x, list_y, list_z) - - -### Surfaces
    -
    [docs]class SurfaceBaseSeries(BaseSeries): - """A base class for 3D surfaces.""" - - is_3Dsurface = True - - def __init__(self): - super(SurfaceBaseSeries, self).__init__() - self.surface_color = None - - def get_color_array(self): - c = self.surface_color - if isinstance(c, collections.Callable): - f = np.vectorize(c) - arity = len(getargspec(c)[0]) - if self.is_parametric: - variables = list(map(centers_of_faces, self.get_parameter_meshes())) - if arity == 1: - return f(variables[0]) - elif arity == 2: - return f(*variables) - variables = list(map(centers_of_faces, self.get_meshes())) - if arity == 1: - return f(variables[0]) - elif arity == 2: - return f(*variables[:2]) - else: - return f(*variables) - else: - return c*np.ones(self.nb_of_points) - -
    -
    [docs]class SurfaceOver2DRangeSeries(SurfaceBaseSeries): - """Representation for a 3D surface consisting of a sympy expression and 2D - range.""" - def __init__(self, expr, var_start_end_x, var_start_end_y, **kwargs): - super(SurfaceOver2DRangeSeries, self).__init__() - self.expr = sympify(expr) - self.var_x = sympify(var_start_end_x[0]) - self.start_x = float(var_start_end_x[1]) - self.end_x = float(var_start_end_x[2]) - self.var_y = sympify(var_start_end_y[0]) - self.start_y = float(var_start_end_y[1]) - self.end_y = float(var_start_end_y[2]) - self.nb_of_points_x = kwargs.get('nb_of_points_x', 50) - self.nb_of_points_y = kwargs.get('nb_of_points_y', 50) - self.surface_color = kwargs.get('surface_color', None) - - def __str__(self): - return ('cartesian surface: %s for' - ' %s over %s and %s over %s') % ( - str(self.expr), - str(self.var_x), - str((self.start_x, self.end_x)), - str(self.var_y), - str((self.start_y, self.end_y))) - - def get_meshes(self): - mesh_x, mesh_y = np.meshgrid(np.linspace(self.start_x, self.end_x, - num=self.nb_of_points_x), - np.linspace(self.start_y, self.end_y, - num=self.nb_of_points_y)) - f = vectorized_lambdify((self.var_x, self.var_y), self.expr) - return (mesh_x, mesh_y, f(mesh_x, mesh_y)) - -
    -
    [docs]class ParametricSurfaceSeries(SurfaceBaseSeries): - """Representation for a 3D surface consisting of three parametric sympy - expressions and a range.""" - - is_parametric = True - - def __init__( - self, expr_x, expr_y, expr_z, var_start_end_u, var_start_end_v, - **kwargs): - super(ParametricSurfaceSeries, self).__init__() - self.expr_x = sympify(expr_x) - self.expr_y = sympify(expr_y) - self.expr_z = sympify(expr_z) - self.var_u = sympify(var_start_end_u[0]) - self.start_u = float(var_start_end_u[1]) - self.end_u = float(var_start_end_u[2]) - self.var_v = sympify(var_start_end_v[0]) - self.start_v = float(var_start_end_v[1]) - self.end_v = float(var_start_end_v[2]) - self.nb_of_points_u = kwargs.get('nb_of_points_u', 50) - self.nb_of_points_v = kwargs.get('nb_of_points_v', 50) - self.surface_color = kwargs.get('surface_color', None) - - def __str__(self): - return ('parametric cartesian surface: (%s, %s, %s) for' - ' %s over %s and %s over %s') % ( - str(self.expr_x), - str(self.expr_y), - str(self.expr_z), - str(self.var_u), - str((self.start_u, self.end_u)), - str(self.var_v), - str((self.start_v, self.end_v))) - - def get_parameter_meshes(self): - return np.meshgrid(np.linspace(self.start_u, self.end_u, - num=self.nb_of_points_u), - np.linspace(self.start_v, self.end_v, - num=self.nb_of_points_v)) - - def get_meshes(self): - mesh_u, mesh_v = self.get_parameter_meshes() - fx = vectorized_lambdify((self.var_u, self.var_v), self.expr_x) - fy = vectorized_lambdify((self.var_u, self.var_v), self.expr_y) - fz = vectorized_lambdify((self.var_u, self.var_v), self.expr_z) - return (fx(mesh_u, mesh_v), fy(mesh_u, mesh_v), fz(mesh_u, mesh_v)) - - -### Contours
    -class ContourSeries(BaseSeries): - """Representation for a contour plot.""" - #The code is mostly repetition of SurfaceOver2DRange. - #XXX: Presently not used in any of those functions. - #XXX: Add contour plot and use this seties. - - is_contour = True - - def __init__(self, expr, var_start_end_x, var_start_end_y): - super(ContourSeries, self).__init__() - self.nb_of_points_x = 50 - self.nb_of_points_y = 50 - self.expr = sympify(expr) - self.var_x = sympify(var_start_end_x[0]) - self.start_x = float(var_start_end_x[1]) - self.end_x = float(var_start_end_x[2]) - self.var_y = sympify(var_start_end_y[0]) - self.start_y = float(var_start_end_y[1]) - self.end_y = float(var_start_end_y[2]) - - self.get_points = self.get_meshes - - def __str__(self): - return ('contour: %s for ' - '%s over %s and %s over %s') % ( - str(self.expr), - str(self.var_x), - str((self.start_x, self.end_x)), - str(self.var_y), - str((self.start_y, self.end_y))) - - def get_meshes(self): - mesh_x, mesh_y = np.meshgrid(np.linspace(self.start_x, self.end_x, - num=self.nb_of_points_x), - np.linspace(self.start_y, self.end_y, - num=self.nb_of_points_y)) - f = vectorized_lambdify((self.var_x, self.var_y), self.expr) - return (mesh_x, mesh_y, f(mesh_x, mesh_y)) - - -############################################################################## -# Backends -############################################################################## - -class BaseBackend(object): - def __init__(self, parent): - super(BaseBackend, self).__init__() - self.parent = parent - - -class MatplotlibBackend(BaseBackend): - def __init__(self, parent): - super(MatplotlibBackend, self).__init__(parent) - are_3D = [s.is_3D for s in self.parent._series] - if any(are_3D) and not all(are_3D): - raise ValueError('The matplotlib backend can not mix 2D and 3D.') - elif not any(are_3D): - self.fig = plt.figure() - self.ax = self.fig.add_subplot(111) - self.ax.spines['left'].set_position('zero') - self.ax.spines['right'].set_color('none') - self.ax.spines['bottom'].set_position('zero') - self.ax.spines['top'].set_color('none') - self.ax.spines['left'].set_smart_bounds(True) - self.ax.spines['bottom'].set_smart_bounds(True) - self.ax.xaxis.set_ticks_position('bottom') - self.ax.yaxis.set_ticks_position('left') - elif all(are_3D): - self.fig = plt.figure() - self.ax = self.fig.add_subplot(111, projection='3d') - - def process_series(self): - parent = self.parent - - for s in self.parent._series: - # Create the collections - if s.is_2Dline: - collection = LineCollection(s.get_segments()) - self.ax.add_collection(collection) - elif s.is_contour: - self.ax.contour(*s.get_meshes()) - elif s.is_3Dline: - # TODO too complicated, I blame matplotlib - collection = art3d.Line3DCollection(s.get_segments()) - self.ax.add_collection(collection) - x, y, z = s.get_points() - self.ax.set_xlim((min(x), max(x))) - self.ax.set_ylim((min(y), max(y))) - self.ax.set_zlim((min(z), max(z))) - elif s.is_3Dsurface: - x, y, z = s.get_meshes() - collection = self.ax.plot_surface(x, y, z, cmap=cm.jet, - rstride=1, cstride=1, - linewidth=0.1) - elif s.is_implicit: - #Smart bounds have to be set to False for implicit plots. - self.ax.spines['left'].set_smart_bounds(False) - self.ax.spines['bottom'].set_smart_bounds(False) - points = s.get_raster() - if len(points) == 2: - #interval math plotting - x, y = _matplotlib_list(points[0]) - self.ax.fill(x, y, facecolor='b', edgecolor='None' ) - else: - # use contourf or contour depending on whether it is - # an inequality or equality. - #XXX: ``contour`` plots multiple lines. Should be fixed. - colormap = ListedColormap(["white", "blue"]) - xarray, yarray, zarray, plot_type = points - if plot_type == 'contour': - self.ax.contour(xarray, yarray, zarray, - contours=(0, 0), fill=False, cmap=colormap) - else: - self.ax.contourf(xarray, yarray, zarray, cmap=colormap) - else: - raise ValueError('The matplotlib backend supports only ' - 'is_2Dline, is_3Dline, is_3Dsurface and ' - 'is_contour objects.') - - # Customise the collections with the corresponding per-series - # options. - if hasattr(s, 'label'): - collection.set_label(s.label) - if s.is_line and s.line_color: - if isinstance(s.line_color, (float, int)) or isinstance(s.line_color, collections.Callable): - color_array = s.get_color_array() - collection.set_array(color_array) - else: - collection.set_color(s.line_color) - if s.is_3Dsurface and s.surface_color: - if matplotlib.__version__ < "1.2.0": # TODO in the distant future remove this check - warnings.warn('The version of matplotlib is too old to use surface coloring.') - elif isinstance(s.surface_color, (float, int)) or isinstance(s.surface_color, collections.Callable): - color_array = s.get_color_array() - color_array = color_array.reshape(color_array.size) - collection.set_array(color_array) - else: - collection.set_color(s.surface_color) - - # Set global options. - # TODO The 3D stuff - # XXX The order of those is important. - if parent.xscale and not isinstance(self.ax, Axes3D): - self.ax.set_xscale(parent.xscale) - if parent.yscale and not isinstance(self.ax, Axes3D): - self.ax.set_yscale(parent.yscale) - if parent.xlim: - self.ax.set_xlim(parent.xlim) - if parent.ylim: - self.ax.set_ylim(parent.ylim) - if not isinstance(self.ax, Axes3D) or matplotlib.__version__ >= '1.2.0': # XXX in the distant future remove this check - self.ax.set_autoscale_on(parent.autoscale) - if parent.axis_center: - val = parent.axis_center - if isinstance(self.ax, Axes3D): - pass - elif val == 'center': - self.ax.spines['left'].set_position('center') - self.ax.spines['bottom'].set_position('center') - elif val == 'auto': - xl, xh = self.ax.get_xlim() - yl, yh = self.ax.get_ylim() - pos_left = ('data', 0) if xl*xh <= 0 else 'center' - pos_bottom = ('data', 0) if yl*yh <= 0 else 'center' - self.ax.spines['left'].set_position(pos_left) - self.ax.spines['bottom'].set_position(pos_bottom) - else: - self.ax.spines['left'].set_position(('data', val[0])) - self.ax.spines['bottom'].set_position(('data', val[1])) - if not parent.axis: - self.ax.set_axis_off() - if parent.legend: - self.ax.legend() - self.ax.legend_.set_visible(parent.legend) - if parent.margin: - self.ax.set_xmargin(parent.margin) - self.ax.set_ymargin(parent.margin) - if parent.title: - self.ax.set_title(parent.title) - if parent.xlabel: - self.ax.set_xlabel(parent.xlabel, position=(1, 0)) - if parent.ylabel: - self.ax.set_ylabel(parent.ylabel, position=(0, 1)) - - def show(self): - self.process_series() - #TODO after fixing https://github.com/ipython/ipython/issues/1255 - # you can uncomment the next line and remove the pyplot.show() call - #self.fig.show() - if _show: - plt.show() - - def save(self, path): - self.process_series() - self.fig.savefig(path) - - def close(self): - plt.close(self.fig) - - -class TextBackend(BaseBackend): - def __init__(self, parent): - super(TextBackend, self).__init__(parent) - - def show(self): - if len(self.parent._series) != 1: - raise ValueError( - 'The TextBackend supports only one graph per Plot.') - elif not isinstance(self.parent._series[0], LineOver1DRangeSeries): - raise ValueError( - 'The TextBackend supports only expressions over a 1D range') - else: - ser = self.parent._series[0] - textplot(ser.expr, ser.start, ser.end) - - def close(self): - pass - - -class DefaultBackend(BaseBackend): - def __new__(cls, parent): - if matplotlib: - return MatplotlibBackend(parent) - else: - return TextBackend(parent) - - -plot_backends = { - 'matplotlib': MatplotlibBackend, - 'text': TextBackend, - 'default': DefaultBackend -} - - -############################################################################## -# Finding the centers of line segments or mesh faces -############################################################################## - -def centers_of_segments(array): - return np.average(np.vstack((array[:-1], array[1:])), 0) - - -def centers_of_faces(array): - return np.average(np.dstack((array[:-1, :-1], - array[1:, :-1], - array[:-1, 1: ], - array[:-1, :-1], - )), 2) - - -def flat(x, y, z, eps=1e-3): - """Checks whether three points are almost collinear""" - vector_a = x - y - vector_b = z - y - dot_product = np.dot(vector_a, vector_b) - vector_a_norm = np.linalg.norm(vector_a) - vector_b_norm = np.linalg.norm(vector_b) - cos_theta = dot_product / (vector_a_norm * vector_b_norm) - return abs(cos_theta + 1) < eps - - -def _matplotlib_list(interval_list): - """ - Returns lists for matplotlib ``fill`` command from a list of bounding - rectangular intervals - """ - xlist = [] - ylist = [] - if len(interval_list): - for intervals in interval_list: - intervalx = intervals[0] - intervaly = intervals[1] - xlist.extend([intervalx.start, intervalx.start, - intervalx.end, intervalx.end, None]) - ylist.extend([intervaly.start, intervaly.end, - intervaly.end, intervaly.start, None]) - else: - #XXX Ugly hack. Matplotlib does not accept empty lists for ``fill`` - xlist.extend([None, None, None, None]) - ylist.extend([None, None, None, None]) - return xlist, ylist - - -####New API for plotting module #### - -# TODO: Add color arrays for plots. -# TODO: Add more plotting options for 3d plots. -# TODO: Adaptive sampling for 3D plots. - -
    [docs]def plot(*args, **kwargs): - """ - Plots a function of a single variable. - - The plotting uses an adaptive algorithm which samples recursively to - accurately plot the plot. The adaptive algorithm uses a random point near - the midpoint of two points that has to be further sampled. Hence the same - plots can appear slightly different. - - Usage - ===== - - Single Plot - - ``plot(expr, range, **kwargs)`` - - If the range is not specified, then a default range of (-10, 10) is used. - - Multiple plots with same range. - - ``plot(expr1, expr2, ..., range, **kwargs)`` - - If the range is not specified, then a default range of (-10, 10) is used. - - Multiple plots with different ranges. - - ``plot((expr1, range), (expr2, range), ..., **kwargs)`` - - Range has to be specified for every expression. - - Default range may change in the future if a more advanced default range - detection algorithm is implemented. - - Arguments - ========= - - ``expr`` : Expression representing the function of single variable - - ``range``: (x, 0, 5), A 3-tuple denoting the range of the free variable. - - Keyword Arguments - ================= - - Arguments for ``LineOver1DRangeSeries`` class: - - ``adaptive``: Boolean. The default value is set to True. Set adaptive to False and - specify ``nb_of_points`` if uniform sampling is required. - - ``depth``: int Recursion depth of the adaptive algorithm. A depth of value ``n`` - samples a maximum of `2^{n}` points. - - ``nb_of_points``: int. Used when the ``adaptive`` is set to False. The function - is uniformly sampled at ``nb_of_points`` number of points. - - Aesthetics options: - - ``line_color``: float. Specifies the color for the plot. - See ``Plot`` to see how to set color for the plots. - - If there are multiple plots, then the same series series are applied to - all the plots. If you want to set these options separately, you can index - the ``Plot`` object returned and set it. - - Arguments for ``Plot`` class: - - ``title`` : str. Title of the plot. It is set to the latex representation of - the expression, if the plot has only one expression. - - ``xlabel`` : str. Label for the x - axis. - - ``ylabel`` : str. Label for the y - axis. - - ``xscale``: {'linear', 'log'} Sets the scaling of the x - axis. - - ``yscale``: {'linear', 'log'} Sets the scaling if the y - axis. - - ``axis_center``: tuple of two floats denoting the coordinates of the center or - {'center', 'auto'} - - ``xlim`` : tuple of two floats, denoting the x - axis limits. - - ``ylim`` : tuple of two floats, denoting the y - axis limits. - - Examples - ======== - - >>> from sympy import symbols - >>> from sympy.plotting import plot - >>> x = symbols('x') - - Single Plot - - >>> plot(x**2, (x, -5, 5))# doctest: +SKIP - - Multiple plots with single range. - - >>> plot(x, x**2, x**3, (x, -5, 5))# doctest: +SKIP - - Multiple plots with different ranges. - - >>> plot((x**2, (x, -6, 6)), (x, (x, -5, 5)))# doctest: +SKIP - - No adaptive sampling. - - >>> plot(x**2, adaptive=False, nb_of_points=400)# doctest: +SKIP - - See Also - ======== - - Plot, LineOver1DRangeSeries. - - """ - args = list(map(sympify, args)) - show = kwargs.pop('show', True) - series = [] - plot_expr = check_arguments(args, 1, 1) - series = [LineOver1DRangeSeries(*arg, **kwargs) for arg in plot_expr] - - plots = Plot(*series, **kwargs) - if show: - plots.show() - return plots - -
    -
    [docs]def plot_parametric(*args, **kwargs): - """ - Plots a 2D parametric plot. - - The plotting uses an adaptive algorithm which samples recursively to - accurately plot the plot. The adaptive algorithm uses a random point near - the midpoint of two points that has to be further sampled. Hence the same - plots can appear slightly different. - - Usage - ===== - - Single plot. - - ``plot_parametric(expr_x, expr_y, range, **kwargs)`` - - If the range is not specified, then a default range of (-10, 10) is used. - - Multiple plots with same range. - - ``plot_parametric((expr1_x, expr1_y), (expr2_x, expr2_y), range, **kwargs)`` - - If the range is not specified, then a default range of (-10, 10) is used. - - Multiple plots with different ranges. - - ``plot_parametric((expr_x, expr_y, range), ..., **kwargs)`` - - Range has to be specified for every expression. - - Default range may change in the future if a more advanced default range - detection algorithm is implemented. - - Arguments - ========= - - ``expr_x`` : Expression representing the function along x. - - ``expr_y`` : Expression representing the function along y. - - ``range``: (u, 0, 5), A 3-tuple denoting the range of the parameter - variable. - - Keyword Arguments - ================= - - Arguments for ``Parametric2DLineSeries`` class: - - ``adaptive``: Boolean. The default value is set to True. Set adaptive to - False and specify ``nb_of_points`` if uniform sampling is required. - - ``depth``: int Recursion depth of the adaptive algorithm. A depth of - value ``n`` samples a maximum of `2^{n}` points. - - ``nb_of_points``: int. Used when the ``adaptive`` is set to False. The - function is uniformly sampled at ``nb_of_points`` number of points. - - Aesthetics - ---------- - - ``line_color``: function which returns a float. Specifies the color for the - plot. See ``sympy.plotting.Plot`` for more details. - - If there are multiple plots, then the same Series arguments are applied to - all the plots. If you want to set these options separately, you can index - the returned ``Plot`` object and set it. - - Arguments for ``Plot`` class: - - ``xlabel`` : str. Label for the x - axis. - - ``ylabel`` : str. Label for the y - axis. - - ``xscale``: {'linear', 'log'} Sets the scaling of the x - axis. - - ``yscale``: {'linear', 'log'} Sets the scaling if the y - axis. - - ``axis_center``: tuple of two floats denoting the coordinates of the center - or {'center', 'auto'} - - ``xlim`` : tuple of two floats, denoting the x - axis limits. - - ``ylim`` : tuple of two floats, denoting the y - axis limits. - - Examples - ======== - - >>> from sympy import symbols, cos, sin - >>> from sympy.plotting import plot_parametric - >>> u = symbols('u') - - Single Parametric plot - - >>> plot_parametric(cos(u), sin(u), (u, -5, 5))# doctest: +SKIP - - Multiple parametric plot with single range. - - >>> plot_parametric((cos(u), sin(u)), (u, cos(u))) # doctest: +SKIP - - Multiple parametric plots. - - >>> plot_parametric((cos(u), sin(u), (u, -5, 5)), - ... (cos(u), u, (u, -5, 5))) # doctest: +SKIP - - See Also - ======== - Plot, Parametric2DLineSeries - - """ - args = list(map(sympify, args)) - show = kwargs.pop('show', True) - series = [] - plot_expr = check_arguments(args, 2, 1) - series = [Parametric2DLineSeries(*arg) for arg in plot_expr] - plots = Plot(*series, **kwargs) - if show: - plots.show() - return plots - -
    -
    [docs]def plot3d_parametric_line(*args, **kwargs): - """ - Plots a 3D parametric line plot. - - Usage - ===== - - Single plot: - - ``plot3d_parametric_line(expr_x, expr_y, expr_z, range, **kwargs)`` - - If the range is not specified, then a default range of (-10, 10) is used. - - Multiple plots. - - ``plot3d_parametric_line((expr_x, expr_y, expr_z, range), ..., **kwargs)`` - - Ranges have to be specified for every expression. - - Default range may change in the future if a more advanced default range - detection algorithm is implemented. - - Arguments - ========= - - ``expr_x`` : Expression representing the function along x. - - ``expr_y`` : Expression representing the function along y. - - ``expr_z`` : Expression representing the function along z. - - ``range``: ``(u, 0, 5)``, A 3-tuple denoting the range of the parameter - variable. - - Keyword Arguments - ================= - - Arguments for ``Parametric3DLineSeries`` class. - - ``nb_of_points``: The range is uniformly sampled at ``nb_of_points`` - number of points. - - Aesthetics: - - ``line_color``: function which returns a float. Specifies the color for the - plot. See ``sympy.plotting.Plot`` for more details. - - If there are multiple plots, then the same series arguments are applied to - all the plots. If you want to set these options separately, you can index - the returned ``Plot`` object and set it. - - Arguments for ``Plot`` class. - - ``title`` : str. Title of the plot. - - Examples - ======== - - >>> from sympy import symbols, cos, sin - >>> from sympy.plotting import plot3d_parametric_line - >>> u = symbols('u') - - Single plot. - - >>> plot3d_parametric_line(cos(u), sin(u), u, (u, -5, 5)) # doctest: +SKIP - - Multiple plots. - - >>> plot3d_parametric_line((cos(u), sin(u), u, (u, -5, 5)), - ... (sin(u), u**2, u, (u, -5, 5))) # doctest: +SKIP - - See Also - ======== - - Plot, Parametric3DLineSeries - - """ - args = list(map(sympify, args)) - show = kwargs.pop('show', True) - series = [] - plot_expr = check_arguments(args, 3, 1) - series = [Parametric3DLineSeries(*arg) for arg in plot_expr] - plots = Plot(*series, **kwargs) - if show: - plots.show() - return plots - -
    -
    [docs]def plot3d(*args, **kwargs): - """ - Plots a 3D surface plot. - - Usage - ===== - - Single plot - - ``plot3d(expr, range_x, range_y, **kwargs)`` - - If the ranges are not specified, then a default range of (-10, 10) is used. - - Multiple plot with the same range. - - ``plot3d(expr1, expr2, range_x, range_y, **kwargs)`` - - If the ranges are not specified, then a default range of (-10, 10) is used. - - Multiple plots with different ranges. - - ``plot3d((expr1, range_x, range_y), (expr2, range_x, range_y), ..., **kwargs)`` - - Ranges have to be specified for every expression. - - Default range may change in the future if a more advanced default range - detection algorithm is implemented. - - Arguments - ========= - - ``expr`` : Expression representing the function along x. - - ``range_x``: (x, 0, 5), A 3-tuple denoting the range of the x - variable. - - ``range_y``: (y, 0, 5), A 3-tuple denoting the range of the y - variable. - - Keyword Arguments - ================= - - Arguments for ``SurfaceOver2DRangeSeries`` class: - - ``nb_of_points_x``: int. The x range is sampled uniformly at - ``nb_of_points_x`` of points. - - ``nb_of_points_y``: int. The y range is sampled uniformly at - ``nb_of_points_y`` of points. - - Aesthetics: - - ``surface_color``: Function which returns a float. Specifies the color for - the surface of the plot. See ``sympy.plotting.Plot`` for more details. - - If there are multiple plots, then the same series arguments are applied to - all the plots. If you want to set these options separately, you can index - the returned ``Plot`` object and set it. - - Arguments for ``Plot`` class: - - ``title`` : str. Title of the plot. - - Examples - ======== - - >>> from sympy import symbols - >>> from sympy.plotting import plot3d - >>> x, y = symbols('x y') - - Single plot - - >>> plot3d(x*y, (x, -5, 5), (y, -5, 5)) # doctest: +SKIP - - Multiple plots with same range - - >>> plot3d(x*y, -x*y, (x, -5, 5), (y, -5, 5)) # doctest: +SKIP - - Multiple plots with different ranges. - - >>> plot3d((x**2 + y**2, (x, -5, 5), (y, -5, 5)), - ... (x*y, (x, -3, 3), (y, -3, 3))) # doctest: +SKIP - - See Also - ======== - Plot, SurfaceOver2DRangeSeries - - """ - - args = list(map(sympify, args)) - show = kwargs.pop('show', True) - series = [] - plot_expr = check_arguments(args, 1, 2) - series = [SurfaceOver2DRangeSeries(*arg) for arg in plot_expr] - plots = Plot(*series, **kwargs) - if show: - plots.show() - return plots - -
    -
    [docs]def plot3d_parametric_surface(*args, **kwargs): - """ - Plots a 3D parametric surface plot. - - Usage - ===== - - Single plot. - - ``plot3d_parametric_surface(expr_x, expr_y, expr_z, range_u, range_v, **kwargs)`` - - If the ranges is not specified, then a default range of (-10, 10) is used. - - Multiple plots. - - ``plot3d_parametric_surface((expr_x, expr_y, expr_z, range_u, range_v), ..., **kwargs)`` - - Ranges have to be specified for every expression. - - Default range may change in the future if a more advanced default range - detection algorithm is implemented. - - Arguments - ========= - - ``expr_x``: Expression representing the function along ``x``. - - ``expr_y``: Expression representing the function along ``y``. - - ``expr_z``: Expression representing the function along ``z``. - - ``range_u``: ``(u, 0, 5)``, A 3-tuple denoting the range of the ``u`` - variable. - - ``range_v``: ``(v, 0, 5)``, A 3-tuple denoting the range of the v - variable. - - Keyword Arguments - ================= - - Arguments for ``ParametricSurfaceSeries`` class: - - ``nb_of_points_u``: int. The ``u`` range is sampled uniformly at - ``nb_of_points_v`` of points - - ``nb_of_points_y``: int. The ``v`` range is sampled uniformly at - ``nb_of_points_y`` of points - - Aesthetics: - - ``surface_color``: Function which returns a float. Specifies the color for - the surface of the plot. See ``sympy.plotting.Plot`` for more details. - - If there are multiple plots, then the same series arguments are applied for - all the plots. If you want to set these options separately, you can index - the returned ``Plot`` object and set it. - - - Arguments for ``Plot`` class: - - ``title`` : str. Title of the plot. - - Examples - ======== - - >>> from sympy import symbols, cos, sin - >>> from sympy.plotting import plot3d_parametric_surface - >>> u, v = symbols('u v') - - Single plot. - - >>> plot3d_parametric_surface(cos(u + v), sin(u - v), u - v, - ... (u, -5, 5), (v, -5, 5)) # doctest: +SKIP - - See Also - ======== - Plot, ParametricSurfaceSeries - - """ - - args = list(map(sympify, args)) - show = kwargs.pop('show', True) - series = [] - plot_expr = check_arguments(args, 3, 2) - series = [ParametricSurfaceSeries(*arg) for arg in plot_expr] - plots = Plot(*series, **kwargs) - if show: - plots.show() - return plots - -
    -def check_arguments(args, expr_len, nb_of_free_symbols): - """ - Checks the arguments and converts into tuples of the - form (exprs, ranges) - - >>> from sympy import plot, cos, sin, symbols - >>> from sympy.plotting.plot import check_arguments - >>> x,y,u,v = symbols('x y u v') - >>> check_arguments([cos(x), sin(x)], 2, 1) - [(cos(x), sin(x), (x, -10, 10))] - - >>> check_arguments([x, x**2], 1, 1) - [(x, (x, -10, 10)), (x**2, (x, -10, 10))] - """ - if expr_len > 1 and isinstance(args[0], Expr): - # Multiple expressions same range. - # The arguments are tuples when the expression length is - # greater than 1. - assert len(args) >= expr_len - for i in range(len(args)): - if isinstance(args[i], Tuple): - break - else: - i = len(args) + 1 - - exprs = Tuple(*args[:i]) - free_symbols = list(set_union(*[e.free_symbols for e in exprs])) - if len(args) == expr_len + nb_of_free_symbols: - #Ranges given - plots = [exprs + Tuple(*args[expr_len:])] - else: - default_range = Tuple(-10, 10) - ranges = [] - for symbol in free_symbols: - ranges.append(Tuple(symbol) + default_range) - - for i in range(len(free_symbols) - nb_of_free_symbols): - ranges.append(Tuple(Dummy()) + default_range) - plots = [exprs + Tuple(*ranges)] - return plots - - if isinstance(args[0], Expr) or (isinstance(args[0], Tuple) and - len(args[0]) == expr_len and - expr_len != 3): - # Cannot handle expressions with number of expression = 3. It is - # not possible to differentiate between expressions and ranges. - #Series of plots with same range - for i in range(len(args)): - if isinstance(args[i], Tuple) and len(args[i]) != expr_len: - break - if not isinstance(args[i], Tuple): - args[i] = Tuple(args[i]) - else: - i = len(args) + 1 - - exprs = args[:i] - assert all(isinstance(e, Expr) for expr in exprs for e in expr) - free_symbols = list(set_union(*[e.free_symbols for expr in exprs - for e in expr])) - - if len(free_symbols) > nb_of_free_symbols: - raise ValueError("The number of free_symbols in the expression" - "is greater than %d" % nb_of_free_symbols) - if len(args) == i + nb_of_free_symbols and isinstance(args[i], Tuple): - ranges = Tuple(*[range_expr for range_expr in args[ - i:i + nb_of_free_symbols]]) - plots = [expr + ranges for expr in exprs] - return plots - else: - #Use default ranges. - default_range = Tuple(-10, 10) - ranges = [] - for symbol in free_symbols: - ranges.append(Tuple(symbol) + default_range) - - for i in range(len(free_symbols) - nb_of_free_symbols): - ranges.append(Tuple(Dummy()) + default_range) - ranges = Tuple(*ranges) - plots = [expr + ranges for expr in exprs] - return plots - - elif isinstance(args[0], Tuple) and len(args[0]) == expr_len + nb_of_free_symbols: - #Multiple plots with different ranges. - for arg in args: - for i in range(expr_len): - if not isinstance(arg[i], Expr): - raise ValueError("Expected an expression, given %s" % - str(arg[i])) - for i in range(nb_of_free_symbols): - if not len(arg[i + expr_len]) == 3: - raise ValueError("The ranges should be a tuple of" - "length 3, got %s" % str(arg[i + expr_len])) - return args -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/plotting/plot_implicit.html b/dev-py3k/_modules/sympy/plotting/plot_implicit.html deleted file mode 100644 index dd325a62112..00000000000 --- a/dev-py3k/_modules/sympy/plotting/plot_implicit.html +++ /dev/null @@ -1,470 +0,0 @@ - - - - - - - - - - sympy.plotting.plot_implicit — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.plotting.plot_implicit

    -"""Implicit plotting module for SymPy
    -
    -The module implements a data series called ImplicitSeries which is used by
    -``Plot`` class to plot implicit plots for different backends. The module,
    -by default, implements plotting using interval arithmetic. It switches to a
    -fall back algorithm if the expression cannot be plotted used interval
    -interval arithmetic. It is also possible to specify to use the fall back
    -algorithm for all plots.
    -
    -Boolean combinations of expressions cannot be plotted by the fall back
    -algorithm.
    -
    -See Also
    -========
    -sympy.plotting.plot
    -
    -References
    -==========
    -- Jeffrey Allen Tupper. Reliable Two-Dimensional Graphing Methods for
    -Mathematical Formulae with Two Free Variables.
    -
    -- Jeffrey Allen Tupper. Graphing Equations with Generalized Interval
    -Arithmetic. Master's thesis. University of Toronto, 1996
    -
    -"""
    -
    -from .plot import BaseSeries, Plot
    -from .experimental_lambdify import experimental_lambdify, vectorized_lambdify
    -from .intervalmath import interval
    -from sympy.core.relational import (Equality, GreaterThan, LessThan,
    -                Relational, StrictLessThan, StrictGreaterThan)
    -from sympy import Eq, Tuple, sympify, Expr, Dummy
    -from sympy.external import import_module
    -from sympy.core.compatibility import set_union
    -from sympy.logic.boolalg import BooleanFunction
    -import warnings
    -
    -np = import_module('numpy')
    -
    -
    -
    [docs]class ImplicitSeries(BaseSeries): - """ Representation for Implicit plot """ - is_implicit = True - - def __init__(self, expr, var_start_end_x, var_start_end_y, - has_equality, use_interval_math, depth, nb_of_points): - super(ImplicitSeries, self).__init__() - self.expr = sympify(expr) - self.var_x = sympify(var_start_end_x[0]) - self.start_x = float(var_start_end_x[1]) - self.end_x = float(var_start_end_x[2]) - self.var_y = sympify(var_start_end_y[0]) - self.start_y = float(var_start_end_y[1]) - self.end_y = float(var_start_end_y[2]) - self.get_points = self.get_raster - self.has_equality = has_equality # If the expression has equality, i.e. - #Eq, Greaterthan, LessThan. - self.nb_of_points = nb_of_points - self.use_interval_math = use_interval_math - self.depth = 4 + depth - - def __str__(self): - return ('Implicit equation: %s for ' - '%s over %s and %s over %s') % ( - str(self.expr), - str(self.var_x), - str((self.start_x, self.end_x)), - str(self.var_y), - str((self.start_y, self.end_y))) - - def get_raster(self): - func = experimental_lambdify((self.var_x, self.var_y), self.expr, - use_interval=True) - xinterval = interval(self.start_x, self.end_x) - yinterval = interval(self.start_y, self.end_y) - try: - temp = func(xinterval, yinterval) - except AttributeError: - if self.use_interval_math: - warnings.warn("Adaptive meshing could not be applied to the" - " expression. Using uniform meshing.") - self.use_interval_math = False - - if self.use_interval_math: - return self._get_raster_interval(func) - else: - return self._get_meshes_grid() - - def _get_raster_interval(self, func): - """ Uses interval math to adaptively mesh and obtain the plot""" - k = self.depth - interval_list = [] - #Create initial 32 divisions - xsample = np.linspace(self.start_x, self.end_x, 33) - ysample = np.linspace(self.start_y, self.end_y, 33) - - #Add a small jitter so that there are no false positives for equality. - # Ex: y==x becomes True for x interval(1, 2) and y interval(1, 2) - #which will draw a rectangle. - jitterx = (np.random.rand( - len(xsample)) * 2 - 1) * (self.end_x - self.start_x) / 2**20 - jittery = (np.random.rand( - len(ysample)) * 2 - 1) * (self.end_y - self.start_y) / 2**20 - xsample += jitterx - ysample += jittery - - xinter = [interval(x1, x2) for x1, x2 in zip(xsample[:-1], - xsample[1:])] - yinter = [interval(y1, y2) for y1, y2 in zip(ysample[:-1], - ysample[1:])] - interval_list = [[x, y] for x in xinter for y in yinter] - plot_list = [] - - #recursive call refinepixels which subdivides the intervals which are - #neither True nor False according to the expression. - def refine_pixels(interval_list): - """ Evaluates the intervals and subdivides the interval if the - expression is partially satisfied.""" - temp_interval_list = [] - plot_list = [] - for intervals in interval_list: - - #Convert the array indices to x and y values - intervalx = intervals[0] - intervaly = intervals[1] - func_eval = func(intervalx, intervaly) - #The expression is valid in the interval. Change the contour - #array values to 1. - if func_eval[1] is False or func_eval[0] is False: - pass - elif func_eval == (True, True): - plot_list.append([intervalx, intervaly]) - elif func_eval[1] is None or func_eval[0] is None: - #Subdivide - avgx = intervalx.mid - avgy = intervaly.mid - a = interval(intervalx.start, avgx) - b = interval(avgx, intervalx.end) - c = interval(intervaly.start, avgy) - d = interval(avgy, intervaly.end) - temp_interval_list.append([a, c]) - temp_interval_list.append([a, d]) - temp_interval_list.append([b, c]) - temp_interval_list.append([b, d]) - return temp_interval_list, plot_list - - while k >= 0 and len(interval_list): - interval_list, plot_list_temp = refine_pixels(interval_list) - plot_list.extend(plot_list_temp) - k = k - 1 - #Check whether the expression represents an equality - #If it represents an equality, then none of the intervals - #would have satisfied the expression due to floating point - #differences. Add all the undecided values to the plot. - if self.has_equality: - for intervals in interval_list: - intervalx = intervals[0] - intervaly = intervals[1] - func_eval = func(intervalx, intervaly) - if func_eval[1] and func_eval[0] is not False: - plot_list.append([intervalx, intervaly]) - return plot_list, 'fill' - - def _get_meshes_grid(self): - """Generates the mesh for generating a contour. - - In the case of equality, ``contour`` function of matplotlib can - be used. In other cases, matplotlib's ``contourf`` is used. - """ - equal = False - if isinstance(self.expr, Equality): - expr = self.expr.lhs - self.expr.rhs - equal = True - - elif isinstance(self.expr, (GreaterThan, StrictGreaterThan)): - expr = self.expr.lhs - self.expr.rhs - - elif isinstance(self.expr, (LessThan, StrictLessThan)): - expr = self.expr.rhs - self.expr.lhs - else: - raise NotImplementedError("The expression is not supported for" - "plotting in uniform meshed plot.") - xarray = np.linspace(self.start_x, self.end_x, self.nb_of_points) - yarray = np.linspace(self.start_y, self.end_y, self.nb_of_points) - x_grid, y_grid = np.meshgrid(xarray, yarray) - - func = vectorized_lambdify((self.var_x, self.var_y), expr) - z_grid = func(x_grid, y_grid) - z_grid[np.ma.where(z_grid < 0)] = -1 - z_grid[np.ma.where(z_grid > 0)] = 1 - if equal: - return xarray, yarray, z_grid, 'contour' - else: - return xarray, yarray, z_grid, 'contourf' - -
    -
    [docs]def plot_implicit(expr, *args, **kwargs): - """A plot function to plot implicit equations / inequalities. - - Arguments - ========= - - - ``expr`` : The equation / inequality that is to be plotted. - - ``(x, xmin, xmax)`` optional, 3-tuple denoting the range of symbol - ``x`` - - ``(y, ymin, ymax)`` optional, 3-tuple denoting the range of symbol - ``y`` - - The following arguments can be passed as named parameters. - - - ``adaptive``. Boolean. The default value is set to True. It has to be - set to False if you want to use a mesh grid. - - - ``depth`` integer. The depth of recursion for adaptive mesh grid. - Default value is 0. Takes value in the range (0, 4). - - - ``points`` integer. The number of points if adaptive mesh grid is not - used. Default value is 200. - - - ``title`` string .The title for the plot. - - - ``xlabel`` string. The label for the x - axis - - - ``ylabel`` string. The label for the y - axis - - plot_implicit, by default, uses interval arithmetic to plot functions. If - the expression cannot be plotted using interval arithmetic, it defaults to - a generating a contour using a mesh grid of fixed number of points. By - setting adaptive to False, you can force plot_implicit to use the mesh - grid. The mesh grid method can be effective when adaptive plotting using - interval arithmetic, fails to plot with small line width. - - Examples: - ========= - - Plot expressions: - - >>> from sympy import plot_implicit, cos, sin, symbols, Eq - >>> x, y = symbols('x y') - - Without any ranges for the symbols in the expression - - >>> p1 = plot_implicit(Eq(x**2 + y**2, 5)) # doctest: +SKIP - - With the range for the symbols - - >>> p2 = plot_implicit(Eq(x**2 + y**2, 3), - ... (x, -3, 3), (y, -3, 3)) # doctest: +SKIP - - With depth of recursion as argument. - - >>> p3 = plot_implicit(Eq(x**2 + y**2, 5), - ... (x, -4, 4), (y, -4, 4), depth = 2) # doctest: +SKIP - - Using mesh grid and not using adaptive meshing. - - >>> p4 = plot_implicit(Eq(x**2 + y**2, 5), - ... (x, -5, 5), (y, -2, 2), adaptive=False) # doctest: +SKIP - - Using mesh grid with number of points as input. - - >>> p5 = plot_implicit(Eq(x**2 + y**2, 5), - ... (x, -5, 5), (y, -2, 2), - ... adaptive=False, points=400) # doctest: +SKIP - - Plotting regions. - - >>> p6 = plot_implicit(y > x**2) # doctest: +SKIP - - Plotting Using boolean conjunctions. - - >>> p7 = plot_implicit(And(y > x, y > -x)) # doctest: +SKIP - """ - has_equality = False # Represents whether the expression contains an Equality, - #GreaterThan or LessThan - - def arg_expand(bool_expr): - """ - Recursively expands the arguments of an Boolean Function - """ - for arg in bool_expr.args: - if isinstance(arg, BooleanFunction): - arg_expand(arg) - elif isinstance(arg, Relational): - arg_list.append(arg) - - arg_list = [] - if isinstance(expr, BooleanFunction): - arg_expand(expr) - - #Check whether there is an equality in the expression provided. - if any(isinstance(e, (Equality, GreaterThan, LessThan)) - for e in arg_list): - has_equality = True - - elif not isinstance(expr, Relational): - expr = Eq(expr, 0) - has_equality = True - elif isinstance(expr, (Equality, GreaterThan, LessThan)): - has_equality = True - - free_symbols = set(expr.free_symbols) - range_symbols = set([t[0] for t in args]) - symbols = set_union(free_symbols, range_symbols) - if len(symbols) > 2: - raise NotImplementedError("Implicit plotting is not implemented for " - "more than 2 variables") - - #Create default ranges if the range is not provided. - default_range = Tuple(-5, 5) - if len(args) == 2: - var_start_end_x = args[0] - var_start_end_y = args[1] - elif len(args) == 1: - if len(free_symbols) == 2: - var_start_end_x = args[0] - var_start_end_y, = (Tuple(e) + default_range - for e in (free_symbols - range_symbols)) - else: - var_start_end_x, = (Tuple(e) + default_range for e in free_symbols) - #Create a random symbol - var_start_end_y = Tuple(Dummy()) + default_range - - elif len(args) == 0: - if len(free_symbols) == 1: - var_start_end_x, = (Tuple(e) + default_range for e in free_symbols) - #create a random symbol - var_start_end_y = Tuple(Dummy()) + default_range - else: - var_start_end_x, var_start_end_y = (Tuple(e) + default_range - for e in free_symbols) - - use_interval = kwargs.pop('adaptive', True) - nb_of_points = kwargs.pop('points', 300) - depth = kwargs.pop('depth', 0) - #Check whether the depth is greater than 4 or less than 0. - if depth > 4: - depth = 4 - elif depth < 0: - depth = 0 - - series_argument = ImplicitSeries(expr, var_start_end_x, var_start_end_y, - has_equality, use_interval, depth, - nb_of_points) - show = kwargs.pop('show', True) - - #set the x and y limits - kwargs['xlim'] = tuple(float(x) for x in var_start_end_x[1:]) - kwargs['ylim'] = tuple(float(y) for y in var_start_end_y[1:]) - p = Plot(series_argument, **kwargs) - if show: - p.show() - return p
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/polys/agca/homomorphisms.html b/dev-py3k/_modules/sympy/polys/agca/homomorphisms.html deleted file mode 100644 index 7887eaf6ce0..00000000000 --- a/dev-py3k/_modules/sympy/polys/agca/homomorphisms.html +++ /dev/null @@ -1,735 +0,0 @@ - - - - - - - - - - sympy.polys.agca.homomorphisms — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.polys.agca.homomorphisms

    -"""
    -Computations with homomorphisms of modules and rings.
    -
    -This module implements classes for representing homomorphisms of rings and
    -their modules. Instead of instantiating the classes directly, you should use
    -the function ``homomorphism(from, to, matrix)`` to create homomorphism objects.
    -"""
    -
    -from sympy.polys.agca.modules import (Module, FreeModule, QuotientModule,
    -    SubModule, SubQuotientModule)
    -from sympy.polys.polyerrors import CoercionFailed
    -
    -# The main computational task for module homomorphisms is kernels.
    -# For this reason, the concrete classes are organised by domain module type.
    -
    -
    -
    [docs]class ModuleHomomorphism(object): - """ - Abstract base class for module homomoprhisms. Do not instantiate. - - Instead, use the ``homomorphism`` function: - - >>> from sympy import QQ, homomorphism - >>> from sympy.abc import x - >>> F = QQ[x].free_module(2) - >>> homomorphism(F, F, [[1, 0], [0, 1]]) - [1, 0] - [0, 1] : QQ[x]**2 -> QQ[x]**2 - - Attributes: - - - ring - the ring over which we are considering modules - - domain - the domain module - - codomain - the codomain module - - _ker - cached kernel - - _img - cachd image - - Non-implemented methods: - - - _kernel - - _image - - _restrict_domain - - _restrict_codomain - - _quotient_domain - - _quotient_codomain - - _apply - - _mul_scalar - - _compose - - _add - """ - - def __init__(self, domain, codomain): - if not isinstance(domain, Module): - raise TypeError('Source must be a module, got %s' % domain) - if not isinstance(codomain, Module): - raise TypeError('Target must be a module, got %s' % codomain) - if domain.ring != codomain.ring: - raise ValueError('Source and codomain must be over same ring, ' - 'got %s != %s' % (domain, codomain)) - self.domain = domain - self.codomain = codomain - self.ring = domain.ring - self._ker = None - self._img = None - -
    [docs] def kernel(self): - r""" - Compute the kernel of ``self``. - - That is, if ``self`` is the homomorphism `\phi: M \to N`, then compute - `ker(\phi) = \{x \in M | \phi(x) = 0\}`. This is a submodule of `M`. - - >>> from sympy import homomorphism, QQ - >>> from sympy.abc import x - >>> F = QQ[x].free_module(2) - >>> homomorphism(F, F, [[1, 0], [x, 0]]).kernel() - <[x, -1]> - """ - if self._ker is None: - self._ker = self._kernel() - return self._ker -
    -
    [docs] def image(self): - r""" - Compute the image of ``self``. - - That is, if ``self`` is the homomorphism `\phi: M \to N`, then compute - `im(\phi) = \{\phi(x) | x \in M \}`. This is a submodule of `N`. - - >>> from sympy import homomorphism, QQ - >>> from sympy.abc import x - >>> F = QQ[x].free_module(2) - >>> homomorphism(F, F, [[1, 0], [x, 0]]).image() == F.submodule([1, 0]) - True - """ - if self._img is None: - self._img = self._image() - return self._img -
    - def _kernel(self): - """Compute the kernel of ``self``.""" - raise NotImplementedError - - def _image(self): - """Compute the image of ``self``.""" - raise NotImplementedError - - def _restrict_domain(self, sm): - """Implementation of domain restriction.""" - raise NotImplementedError - - def _restrict_codomain(self, sm): - """Implementation of codomain restriction.""" - raise NotImplementedError - - def _quotient_domain(self, sm): - """Implementation of domain quotienting.""" - raise NotImplementedError - - def _quotient_codomain(self, sm): - """Implementation of codomain quotienting.""" - raise NotImplementedError - -
    [docs] def restrict_domain(self, sm): - """ - Return ``self``, with the domain restricted to ``sm``. - - Here ``sm`` has to be a submodule of ``self.domain``. - - >>> from sympy import homomorphism, QQ - >>> from sympy.abc import x - >>> F = QQ[x].free_module(2) - >>> h = homomorphism(F, F, [[1, 0], [x, 0]]) - >>> h - [1, x] - [0, 0] : QQ[x]**2 -> QQ[x]**2 - >>> h.restrict_domain(F.submodule([1, 0])) - [1, x] - [0, 0] : <[1, 0]> -> QQ[x]**2 - - This is the same as just composing on the right with the submodule - inclusion: - - >>> h * F.submodule([1, 0]).inclusion_hom() - [1, x] - [0, 0] : <[1, 0]> -> QQ[x]**2 - """ - if not self.domain.is_submodule(sm): - raise ValueError('sm must be a submodule of %s, got %s' - % (self.domain, sm)) - if sm == self.domain: - return self - return self._restrict_domain(sm) -
    -
    [docs] def restrict_codomain(self, sm): - """ - Return ``self``, with codomain restricted to to ``sm``. - - Here ``sm`` has to be a submodule of ``self.codomain`` containing the - image. - - >>> from sympy import homomorphism, QQ - >>> from sympy.abc import x - >>> F = QQ[x].free_module(2) - >>> h = homomorphism(F, F, [[1, 0], [x, 0]]) - >>> h - [1, x] - [0, 0] : QQ[x]**2 -> QQ[x]**2 - >>> h.restrict_codomain(F.submodule([1, 0])) - [1, x] - [0, 0] : QQ[x]**2 -> <[1, 0]> - """ - if not sm.is_submodule(self.image()): - raise ValueError('the image %s must contain sm, got %s' - % (self.image(), sm)) - if sm == self.codomain: - return self - return self._restrict_codomain(sm) -
    -
    [docs] def quotient_domain(self, sm): - """ - Return ``self`` with domain replaced by ``domain/sm``. - - Here ``sm`` must be a submodule of ``self.kernel()``. - - >>> from sympy import homomorphism, QQ - >>> from sympy.abc import x - >>> F = QQ[x].free_module(2) - >>> h = homomorphism(F, F, [[1, 0], [x, 0]]) - >>> h - [1, x] - [0, 0] : QQ[x]**2 -> QQ[x]**2 - >>> h.quotient_domain(F.submodule([-x, 1])) - [1, x] - [0, 0] : QQ[x]**2/<[-x, 1]> -> QQ[x]**2 - """ - if not self.kernel().is_submodule(sm): - raise ValueError('kernel %s must contain sm, got %s' % - (self.kernel(), sm)) - if sm.is_zero(): - return self - return self._quotient_domain(sm) -
    -
    [docs] def quotient_codomain(self, sm): - """ - Return ``self`` with codomain replaced by ``codomain/sm``. - - Here ``sm`` must be a submodule of ``self.codomain``. - - >>> from sympy import homomorphism, QQ - >>> from sympy.abc import x - >>> F = QQ[x].free_module(2) - >>> h = homomorphism(F, F, [[1, 0], [x, 0]]) - >>> h - [1, x] - [0, 0] : QQ[x]**2 -> QQ[x]**2 - >>> h.quotient_codomain(F.submodule([1, 1])) - [1, x] - [0, 0] : QQ[x]**2 -> QQ[x]**2/<[1, 1]> - - This is the same as composing with the quotient map on the left: - - >>> (F/[(1, 1)]).quotient_hom() * h - [1, x] - [0, 0] : QQ[x]**2 -> QQ[x]**2/<[1, 1]> - """ - if not self.codomain.is_submodule(sm): - raise ValueError('sm must be a submodule of codomain %s, got %s' - % (self.codomain, sm)) - if sm.is_zero(): - return self - return self._quotient_codomain(sm) -
    - def _apply(self, elem): - """Apply ``self`` to ``elem``.""" - raise NotImplementedError - - def __call__(self, elem): - return self.codomain.convert(self._apply(self.domain.convert(elem))) - - def _compose(self, oth): - """ - Compose ``self`` with ``oth``, that is, return the homomorphism - obtained by first applying then ``self``, then ``oth``. - - (This method is private since in this syntax, it is non-obvious which - homomorphism is executed first.) - """ - raise NotImplementedError - - def _mul_scalar(self, c): - """Scalar multiplication. ``c`` is guaranteed in self.ring.""" - raise NotImplementedError - - def _add(self, oth): - """ - Homomorphism addition. - ``oth`` is guaranteed to be a homomorphism with same domain/codomain. - """ - raise NotImplementedError - - def _check_hom(self, oth): - """Helper to check that oth is a homomorphism with same domain/codomain.""" - if not isinstance(oth, ModuleHomomorphism): - return False - return oth.domain == self.domain and oth.codomain == self.codomain - - def __mul__(self, oth): - if isinstance(oth, ModuleHomomorphism) and self.domain == oth.codomain: - return oth._compose(self) - try: - return self._mul_scalar(self.ring.convert(oth)) - except CoercionFailed: - return NotImplemented - - # NOTE: _compose will never be called from rmul - __rmul__ = __mul__ - - def __div__(self, oth): - try: - return self._mul_scalar(1/self.ring.convert(oth)) - except CoercionFailed: - return NotImplemented - - __truediv__ = __div__ - - def __add__(self, oth): - if self._check_hom(oth): - return self._add(oth) - return NotImplemented - - def __sub__(self, oth): - if self._check_hom(oth): - return self._add(oth._mul_scalar(self.ring.convert(-1))) - return NotImplemented - -
    [docs] def is_injective(self): - """ - Return True if ``self`` is injective. - - That is, check if the elements of the domain are mapped to the same - codomain element. - - >>> from sympy import homomorphism, QQ - >>> from sympy.abc import x - >>> F = QQ[x].free_module(2) - >>> h = homomorphism(F, F, [[1, 0], [x, 0]]) - >>> h.is_injective() - False - >>> h.quotient_domain(h.kernel()).is_injective() - True - """ - return self.kernel().is_zero() -
    -
    [docs] def is_surjective(self): - """ - Return True if ``self`` is surjective. - - That is, check if every element of the codomain has at least one - preimage. - - >>> from sympy import homomorphism, QQ - >>> from sympy.abc import x - >>> F = QQ[x].free_module(2) - >>> h = homomorphism(F, F, [[1, 0], [x, 0]]) - >>> h.is_surjective() - False - >>> h.restrict_codomain(h.image()).is_surjective() - True - """ - return self.image() == self.codomain -
    -
    [docs] def is_isomorphism(self): - """ - Return True if ``self`` is an isomorphism. - - That is, check if every element of the codomain has precisely one - preimage. Equivalently, ``self`` is both injective and surjective. - - >>> from sympy import homomorphism, QQ - >>> from sympy.abc import x - >>> F = QQ[x].free_module(2) - >>> h = homomorphism(F, F, [[1, 0], [x, 0]]) - >>> h = h.restrict_codomain(h.image()) - >>> h.is_isomorphism() - False - >>> h.quotient_domain(h.kernel()).is_isomorphism() - True - """ - return self.is_injective() and self.is_surjective() -
    -
    [docs] def is_zero(self): - """ - Return True if ``self`` is a zero morphism. - - That is, check if every element of the domain is mapped to zero - under self. - - >>> from sympy import homomorphism, QQ - >>> from sympy.abc import x - >>> F = QQ[x].free_module(2) - >>> h = homomorphism(F, F, [[1, 0], [x, 0]]) - >>> h.is_zero() - False - >>> h.restrict_domain(F.submodule()).is_zero() - True - >>> h.quotient_codomain(h.image()).is_zero() - True - """ - return self.image().is_zero() -
    - def __eq__(self, oth): - try: - return (self - oth).is_zero() - except TypeError: - return False - - def __ne__(self, oth): - return not (self == oth) - -
    -class MatrixHomomorphism(ModuleHomomorphism): - """ - Helper class for all homomoprhisms which are expressed via a matrix. - - That is, for such homomorphisms ``domain`` is contained in a module - generated by finitely many elements `e_1, \dots, e_n`, so that the - homomorphism is determined uniquely by its action on the `e_i`. It - can thus be represented as a vector of elements of the codomain module, - or potentially a supermodule of the codomain module - (and hence conventionally as a matrix, if there is a similar interpretation - for elements of the codomain module). - - Note that this class does *not* assume that the `e_i` freely generate a - submodule, nor that ``domain`` is even all of this submodule. It exists - only to unify the interface. - - Do not instantiate. - - Attributes: - - - matrix - the list of images determining the homomorphism. - NOTE: the elements of matrix belong to either self.codomain or - self.codomain.container - - Still non-implemented methods: - - - kernel - - _apply - """ - - def __init__(self, domain, codomain, matrix): - ModuleHomomorphism.__init__(self, domain, codomain) - if len(matrix) != domain.rank: - raise ValueError('Need to provide %s elements, got %s' - % (domain.rank, len(matrix))) - - converter = self.codomain.convert - if isinstance(self.codomain, (SubModule, SubQuotientModule)): - converter = self.codomain.container.convert - self.matrix = tuple(converter(x) for x in matrix) - - def _sympy_matrix(self): - """Helper function which returns a sympy matrix ``self.matrix``.""" - from sympy.matrices import Matrix - c = lambda x: x - if isinstance(self.codomain, (QuotientModule, SubQuotientModule)): - c = lambda x: x.data - return Matrix([[self.ring.to_sympy(y) for y in c(x)] for x in self.matrix]).T - - def __repr__(self): - lines = repr(self._sympy_matrix()).split('\n') - t = " : %s -> %s" % (self.domain, self.codomain) - s = ' '*len(t) - n = len(lines) - for i in range(n // 2): - lines[i] += s - lines[n // 2] += t - for i in range(n//2 + 1, n): - lines[i] += s - return '\n'.join(lines) - - def _restrict_domain(self, sm): - """Implementation of domain restriction.""" - return SubModuleHomomorphism(sm, self.codomain, self.matrix) - - def _restrict_codomain(self, sm): - """Implementation of codomain restriction.""" - return self.__class__(self.domain, sm, self.matrix) - - def _quotient_domain(self, sm): - """Implementation of domain quotienting.""" - return self.__class__(self.domain/sm, self.codomain, self.matrix) - - def _quotient_codomain(self, sm): - """Implementation of codomain quotienting.""" - Q = self.codomain/sm - converter = Q.convert - if isinstance(self.codomain, SubModule): - converter = Q.container.convert - return self.__class__(self.domain, self.codomain/sm, - [converter(x) for x in self.matrix]) - - def _add(self, oth): - return self.__class__(self.domain, self.codomain, - [x + y for x, y in zip(self.matrix, oth.matrix)]) - - def _mul_scalar(self, c): - return self.__class__(self.domain, self.codomain, [c*x for x in self.matrix]) - - def _compose(self, oth): - return self.__class__(self.domain, oth.codomain, [oth(x) for x in self.matrix]) - - -class FreeModuleHomomorphism(MatrixHomomorphism): - """ - Concrete class for homomorphisms with domain a free module or a quotient - thereof. - - Do not instantiate; the constructor does not check that your data is well - defined. Use the ``homomorphism`` function instead: - - >>> from sympy import QQ, homomorphism - >>> from sympy.abc import x - >>> F = QQ[x].free_module(2) - >>> homomorphism(F, F, [[1, 0], [0, 1]]) - [1, 0] - [0, 1] : QQ[x]**2 -> QQ[x]**2 - """ - - def _apply(self, elem): - if isinstance(self.domain, QuotientModule): - elem = elem.data - return sum(x * e for x, e in zip(elem, self.matrix)) - - def _image(self): - return self.codomain.submodule(*self.matrix) - - def _kernel(self): - # The domain is either a free module or a quotient thereof. - # It does not matter if it is a quotient, because that won't increase - # the kernel. - # Our generators {e_i} are sent to the matrix entries {b_i}. - # The kernel is essentially the syzygy module of these {b_i}. - syz = self.image().syzygy_module() - return self.domain.submodule(*syz.gens) - - -class SubModuleHomomorphism(MatrixHomomorphism): - """ - Concrete class for homomorphism with domain a submodule of a free module - or a quotient thereof. - - Do not instantiate; the constructor does not check that your data is well - defined. Use the ``homomorphism`` function instead: - - >>> from sympy import QQ, homomorphism - >>> from sympy.abc import x - >>> M = QQ[x].free_module(2)*x - >>> homomorphism(M, M, [[1, 0], [0, 1]]) - [1, 0] - [0, 1] : <[x, 0], [0, x]> -> <[x, 0], [0, x]> - """ - - def _apply(self, elem): - if isinstance(self.domain, SubQuotientModule): - elem = elem.data - return sum(x * e for x, e in zip(elem, self.matrix)) - - def _image(self): - return self.codomain.submodule(*[self(x) for x in self.domain.gens]) - - def _kernel(self): - syz = self.image().syzygy_module() - return self.domain.submodule( - *[sum(xi*gi for xi, gi in zip(s, self.domain.gens)) - for s in syz.gens]) - - -
    [docs]def homomorphism(domain, codomain, matrix): - r""" - Create a homomorphism object. - - This function tries to build a homomorphism from ``domain`` to ``codomain`` - via the matrix ``matrix``. - - Examples - ======== - - >>> from sympy import QQ, homomorphism - >>> from sympy.abc import x - >>> R = QQ[x] - >>> T = R.free_module(2) - - If ``domain`` is a free module generated by `e_1, \dots, e_n`, then - ``matrix`` should be an n-element iterable `(b_1, \dots, b_n)` where - the `b_i` are elements of ``codomain``. The constructed homomorphism is the - unique homomorphism sending `e_i` to `b_i`. - - >>> F = R.free_module(2) - >>> h = homomorphism(F, T, [[1, x], [x**2, 0]]) - >>> h - [1, x**2] - [x, 0] : QQ[x]**2 -> QQ[x]**2 - >>> h([1, 0]) - [1, x] - >>> h([0, 1]) - [x**2, 0] - >>> h([1, 1]) - [x**2 + 1, x] - - If ``domain`` is a submodule of a free module, them ``matrix`` determines - a homomoprhism from the containing free module to ``codomain``, and the - homomorphism returned is obtained by restriction to ``domain``. - - >>> S = F.submodule([1, 0], [0, x]) - >>> homomorphism(S, T, [[1, x], [x**2, 0]]) - [1, x**2] - [x, 0] : <[1, 0], [0, x]> -> QQ[x]**2 - - If ``domain`` is a (sub)quotient `N/K`, then ``matrix`` determines a - homomorphism from `N` to ``codomain``. If the kernel contains `K`, this - homomorphism descends to ``domain`` and is returned; otherwise an exception - is raised. - - >>> homomorphism(S/[(1, 0)], T, [0, [x**2, 0]]) - [0, x**2] - [0, 0] : <[1, 0] + <[1, 0]>, [0, x] + <[1, 0]>, [1, 0] + <[1, 0]>> -> QQ[x]**2 - >>> homomorphism(S/[(0, x)], T, [0, [x**2, 0]]) - Traceback (most recent call last): - ... - ValueError: kernel <[1, 0], [0, 0]> must contain sm, got <[0,x]> - - """ - def freepres(module): - """ - Return a tuple ``(F, S, Q, c)`` where ``F`` is a free module, ``S`` is a - submodule of ``F``, and ``Q`` a submodule of ``S``, such that - ``module = S/Q``, and ``c`` is a conversion function. - """ - if isinstance(module, FreeModule): - return module, module, module.submodule(), lambda x: module.convert(x) - if isinstance(module, QuotientModule): - return (module.base, module.base, module.killed_module, - lambda x: module.convert(x).data) - if isinstance(module, SubQuotientModule): - return (module.base.container, module.base, module.killed_module, - lambda x: module.container.convert(x).data) - # an ordinary submodule - return (module.container, module, module.submodule(), - lambda x: module.container.convert(x)) - - SF, SS, SQ, _ = freepres(domain) - TF, TS, TQ, c = freepres(codomain) - # NOTE this is probably a bit inefficient (redundant checks) - return FreeModuleHomomorphism(SF, TF, [c(x) for x in matrix] - ).restrict_domain(SS).restrict_codomain(TS - ).quotient_codomain(TQ).quotient_domain(SQ)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/polys/agca/ideals.html b/dev-py3k/_modules/sympy/polys/agca/ideals.html deleted file mode 100644 index 4bf6e555a45..00000000000 --- a/dev-py3k/_modules/sympy/polys/agca/ideals.html +++ /dev/null @@ -1,479 +0,0 @@ - - - - - - - - - - sympy.polys.agca.ideals — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.polys.agca.ideals

    -"""Computations with ideals of polynomial rings."""
    -
    -from sympy.polys.polyerrors import CoercionFailed
    -from functools import reduce
    -
    -
    -
    [docs]class Ideal(object): - """ - Abstract base class for ideals. - - Do not instantiate - use explicit constructors in the ring class instead: - - >>> from sympy import QQ - >>> from sympy.abc import x - >>> QQ[x].ideal(x+1) - <x + 1> - - Attributes - - - ring - the ring this ideal belongs to - - Non-implemented methods: - - - _contains_elem - - _contains_ideal - - _quotient - - _intersect - - _union - - _product - - is_whole_ring - - is_zero - - is_prime, is_maximal, is_primary, is_radical - - is_principal - - height, depth - - radical - - Methods that likely should be overridden in subclasses: - - - reduce_element - """ - - def _contains_elem(self, x): - """Implementation of element containment.""" - raise NotImplementedError - - def _contains_ideal(self, I): - """Implementation of ideal containment.""" - raise NotImplementedError - - def _quotient(self, J): - """Implementation of ideal quotient.""" - raise NotImplementedError - - def _intersect(self, J): - """Implementation of ideal intersection.""" - raise NotImplementedError - -
    [docs] def is_whole_ring(self): - """Return True if ``self`` is the whole ring.""" - raise NotImplementedError -
    -
    [docs] def is_zero(self): - """Return True if ``self`` is the zero ideal.""" - raise NotImplementedError -
    - def _equals(self, J): - """Implementation of ideal equality.""" - return self._contains_ideal(J) and J._contains_ideal(self) - -
    [docs] def is_prime(self): - """Return True if ``self`` is a prime ideal.""" - raise NotImplementedError -
    -
    [docs] def is_maximal(self): - """Return True if ``self`` is a maximal ideal.""" - raise NotImplementedError -
    -
    [docs] def is_radical(self): - """Return True if ``self`` is a radical ideal.""" - raise NotImplementedError -
    -
    [docs] def is_primary(self): - """Return True if ``self`` is a primary ideal.""" - raise NotImplementedError -
    -
    [docs] def is_principal(self): - """Return True if ``self`` is a principal ideal.""" - raise NotImplementedError -
    -
    [docs] def radical(self): - """Compute the radical of ``self``.""" - raise NotImplementedError -
    -
    [docs] def depth(self): - """Compute the depth of ``self``.""" - raise NotImplementedError -
    -
    [docs] def height(self): - """Compute the height of ``self``.""" - raise NotImplementedError - - # TODO more - - # non-implemented methods end here -
    - def __init__(self, ring): - self.ring = ring - - def _check_ideal(self, J): - """Helper to check ``J`` is an ideal of our ring.""" - if not isinstance(J, Ideal) or J.ring != self.ring: - raise ValueError( - 'J must be an ideal of %s, got %s' % (self.ring, J)) - -
    [docs] def contains(self, elem): - """ - Return True if ``elem`` is an element of this ideal. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> QQ[x].ideal(x+1, x-1).contains(3) - True - >>> QQ[x].ideal(x**2, x**3).contains(x) - False - """ - return self._contains_elem(self.ring.convert(elem)) -
    -
    [docs] def subset(self, other): - """ - Returns True if ``other`` is is a subset of ``self``. - - Here ``other`` may be an ideal. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> I = QQ[x].ideal(x+1) - >>> I.subset([x**2 - 1, x**2 + 2*x + 1]) - True - >>> I.subset([x**2 + 1, x + 1]) - False - >>> I.subset(QQ[x].ideal(x**2 - 1)) - True - """ - if isinstance(other, Ideal): - return self._contains_ideal(other) - return all(self._contains_elem(x) for x in other) -
    -
    [docs] def quotient(self, J, **opts): - r""" - Compute the ideal quotient of ``self`` by ``J``. - - That is, if ``self`` is the ideal `I`, compute the set - `I : J = \{x \in R | xJ \subset I \}`. - - >>> from sympy.abc import x, y - >>> from sympy import QQ - >>> R = QQ[x, y] - >>> R.ideal(x*y).quotient(R.ideal(x)) - <y> - """ - self._check_ideal(J) - return self._quotient(J, **opts) -
    -
    [docs] def intersect(self, J): - """ - Compute the intersection of self with ideal J. - - >>> from sympy.abc import x, y - >>> from sympy import QQ - >>> R = QQ[x, y] - >>> R.ideal(x).intersect(R.ideal(y)) - <x*y> - """ - self._check_ideal(J) - return self._intersect(J) -
    -
    [docs] def saturate(self, J): - r""" - Compute the ideal saturation of ``self`` by ``J``. - - That is, if ``self`` is the ideal `I`, compute the set - `I : J^\infty = \{x \in R | xJ^n \subset I \text{ for some } n\}`. - """ - raise NotImplementedError - # Note this can be implemented using repeated quotient -
    -
    [docs] def union(self, J): - """ - Compute the ideal generated by the union of ``self`` and ``J``. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> QQ[x].ideal(x**2 - 1).union(QQ[x].ideal((x+1)**2)) == QQ[x].ideal(x+1) - True - """ - self._check_ideal(J) - return self._union(J) -
    -
    [docs] def product(self, J): - """ - Compute the ideal product of ``self`` and ``J``. - - That is, compute the ideal generated by products `xy`, for `x` an element - of ``self`` and `y \in J`. - - >>> from sympy.abc import x, y - >>> from sympy import QQ - >>> QQ[x, y].ideal(x).product(QQ[x, y].ideal(y)) - <x*y> - """ - self._check_ideal(J) - return self._product(J) -
    -
    [docs] def reduce_element(self, x): - """ - Reduce the element ``x`` of our ring modulo the ideal ``self``. - - Here "reduce" has no specific meaning: it could return a unique normal - form, simplify the expression a bit, or just do nothing. - """ - return x -
    - def __add__(self, e): - if not isinstance(e, Ideal): - R = self.ring.quotient_ring(self) - if isinstance(e, R.dtype): - return e - if isinstance(e, R.ring.dtype): - return R(e) - return R.convert(e) - self._check_ideal(e) - return self.union(e) - - __radd__ = __add__ - - def __mul__(self, e): - if not isinstance(e, Ideal): - try: - e = self.ring.ideal(e) - except CoercionFailed: - return NotImplemented - self._check_ideal(e) - return self.product(e) - - __rmul__ = __mul__ - - def __pow__(self, exp): - if exp < 0: - raise NotImplementedError - # TODO exponentiate by squaring - return reduce(lambda x, y: x*y, [self]*exp, self.ring.ideal(1)) - - def __eq__(self, e): - if not isinstance(e, Ideal) or e.ring != self.ring: - return False - return self._equals(e) - - def __ne__(self, e): - return not (self == e) - -
    -class ModuleImplementedIdeal(Ideal): - """ - Ideal implementation relying on the modules code. - - Attributes: - - - _module - the underlying module - """ - - def __init__(self, ring, module): - Ideal.__init__(self, ring) - self._module = module - - def _contains_elem(self, x): - return self._module.contains([x]) - - def _contains_ideal(self, J): - if not isinstance(J, ModuleImplementedIdeal): - raise NotImplementedError - return self._module.is_submodule(J._module) - - def _intersect(self, J): - if not isinstance(J, ModuleImplementedIdeal): - raise NotImplementedError - return self.__class__(self.ring, self._module.intersect(J._module)) - - def _quotient(self, J, **opts): - if not isinstance(J, ModuleImplementedIdeal): - raise NotImplementedError - return self._module.module_quotient(J._module, **opts) - - def _union(self, J): - if not isinstance(J, ModuleImplementedIdeal): - raise NotImplementedError - return self.__class__(self.ring, self._module.union(J._module)) - - @property - def gens(self): - """ - Return generators for ``self``. - - >>> from sympy import QQ - >>> from sympy.abc import x, y - >>> list(QQ[x, y].ideal(x, y, x**2 + y).gens) - [x, y, x**2 + y] - """ - return (x[0] for x in self._module.gens) - - def is_zero(self): - """ - Return True if ``self`` is the zero ideal. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> QQ[x].ideal(x).is_zero() - False - >>> QQ[x].ideal().is_zero() - True - """ - return self._module.is_zero() - - def is_whole_ring(self): - """ - Return True if ``self`` is the whole ring, i.e. one generator is a unit. - - >>> from sympy.abc import x - >>> from sympy import QQ, ilex - >>> QQ[x].ideal(x).is_whole_ring() - False - >>> QQ[x].ideal(3).is_whole_ring() - True - >>> QQ.poly_ring(x, order=ilex).ideal(2 + x).is_whole_ring() - True - """ - return self._module.is_full_module() - - def __repr__(self): - from sympy import sstr - return '<' + ','.join(sstr(x) for [x] in self._module.gens) + '>' - - # NOTE this is the only method using the fact that the module is a SubModule - def _product(self, J): - if not isinstance(J, ModuleImplementedIdeal): - raise NotImplementedError - return self.__class__(self.ring, self._module.submodule( - *[[x*y] for [x] in self._module.gens for [y] in J._module.gens])) - - def in_terms_of_generators(self, e): - """ - Express ``e`` in terms of the generators of ``self``. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> I = QQ[x].ideal(x**2 + 1, x) - >>> I.in_terms_of_generators(1) - [1, -x] - """ - return self._module.in_terms_of_generators([e]) - - def reduce_element(self, x, **options): - return self._module.reduce_element([x], **options)[0] -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/polys/agca/modules.html b/dev-py3k/_modules/sympy/polys/agca/modules.html deleted file mode 100644 index 5f88db8fb48..00000000000 --- a/dev-py3k/_modules/sympy/polys/agca/modules.html +++ /dev/null @@ -1,1493 +0,0 @@ - - - - - - - - - - sympy.polys.agca.modules — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.polys.agca.modules

    -"""
    -Computations with modules over polynomial rings.
    -
    -This module implements various classes that encapsulate groebner basis
    -computations for modules. Most of them should not be instantiated by hand.
    -Instead, use the constructing routines on objects you already have.
    -
    -For example, to construct a free module over ``QQ[x, y]``, call
    -``QQ[x, y].free_module(rank)`` instead of the ``FreeModule`` constructor.
    -In fact ``FreeModule`` is an abstract base class that should not be
    -instantiated, the ``free_module`` method instead returns the implementing class
    -``FreeModulePolyRing``.
    -
    -In general, the abstract base classes implement most functionality in terms of
    -a few non-implemented methods. The concrete base classes supply only these
    -non-implemented methods. They may also supply new implementations of the
    -convenience methods, for example if there are faster algorithms available.
    -"""
    -
    -from copy import copy
    -
    -from sympy.polys.polyerrors import CoercionFailed
    -from sympy.polys.monomialtools import ProductOrder, monomial_key
    -from sympy.polys.domains import Field
    -from sympy.polys.agca.ideals import Ideal
    -
    -from sympy.core.compatibility import iterable
    -from functools import reduce
    -
    -# TODO
    -# - module saturation
    -# - module quotient/intersection for quotient rings
    -# - free resoltutions / syzygies
    -# - finding small/minimal generating sets
    -# - ...
    -
    -##########################################################################
    -## Abstract base classes #################################################
    -##########################################################################
    -
    -
    -
    [docs]class Module(object): - """ - Abstract base class for modules. - - Do not instantiate - use ring explicit constructors instead: - - >>> from sympy import QQ - >>> from sympy.abc import x - >>> QQ[x].free_module(2) - QQ[x]**2 - - Attributes: - - - dtype - type of elements - - ring - containing ring - - Non-implemented methods: - - - submodule - - quotient_module - - is_zero - - is_submodule - - multiply_ideal - - The method convert likely needs to be changed in subclasses. - """ - - def __init__(self, ring): - self.ring = ring - -
    [docs] def convert(self, elem, M=None): - """ - Convert ``elem`` into internal representation of this module. - - If ``M`` is not None, it should be a module containing it. - """ - if not isinstance(elem, self.dtype): - raise CoercionFailed - return elem -
    -
    [docs] def submodule(self, *gens): - """Generate a submodule.""" - raise NotImplementedError -
    -
    [docs] def quotient_module(self, other): - """Generate a quotient module.""" - raise NotImplementedError -
    - def __div__(self, e): - if not isinstance(e, Module): - e = self.submodule(*e) - return self.quotient_module(e) - - __truediv__ = __div__ - -
    [docs] def contains(self, elem): - """Return True if ``elem`` is an element of this module.""" - try: - self.convert(elem) - return True - except CoercionFailed: - return False -
    - def __contains__(self, elem): - return self.contains(elem) - -
    [docs] def subset(self, other): - """ - Returns True if ``other`` is is a subset of ``self``. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> F = QQ[x].free_module(2) - >>> F.subset([(1, x), (x, 2)]) - True - >>> F.subset([(1/x, x), (x, 2)]) - False - """ - return all(self.contains(x) for x in other) -
    - def __eq__(self, other): - return self.is_submodule(other) and other.is_submodule(self) - - def __ne__(self, other): - return not (self == other) - -
    [docs] def is_zero(self): - """Returns True if ``self`` is a zero module.""" - raise NotImplementedError -
    -
    [docs] def is_submodule(self, other): - """Returns True if ``other`` is a submodule of ``self``.""" - raise NotImplementedError -
    -
    [docs] def multiply_ideal(self, other): - """ - Multiply ``self`` by the ideal ``other``. - """ - raise NotImplementedError -
    - def __mul__(self, e): - if not isinstance(e, Ideal): - try: - e = self.ring.ideal(e) - except (CoercionFailed, NotImplementedError): - return NotImplemented - return self.multiply_ideal(e) - - __rmul__ = __mul__ - -
    [docs] def identity_hom(self): - """Return the identity homomorphism on ``self``.""" - raise NotImplementedError - -
    -class ModuleElement(object): - """ - Base class for module element wrappers. - - Use this class to wrap primitive data types as module elements. It stores - a reference to the containing module, and implements all the arithmetic - operators. - - Attributes: - - - module - containing module - - data - internal data - - Methods that likely need change in subclasses: - - - add - - mul - - div - - eq - """ - - def __init__(self, module, data): - self.module = module - self.data = data - - def add(self, d1, d2): - """Add data ``d1`` and ``d2``.""" - return d1 + d2 - - def mul(self, m, d): - """Multiply module data ``m`` by coefficient d.""" - return m * d - - def div(self, m, d): - """Divide module data ``m`` by coefficient d.""" - return m / d - - def eq(self, d1, d2): - """Return true if d1 and d2 represent the same element.""" - return d1 == d2 - - def __add__(self, om): - if not isinstance(om, self.__class__) or om.module != self.module: - try: - om = self.module.convert(om) - except CoercionFailed: - return NotImplemented - return self.__class__(self.module, self.add(self.data, om.data)) - - __radd__ = __add__ - - def __neg__(self): - return self.__class__(self.module, self.mul(self.data, - self.module.ring.convert(-1))) - - def __sub__(self, om): - if not isinstance(om, self.__class__) or om.module != self.module: - try: - om = self.module.convert(om) - except CoercionFailed: - return NotImplemented - return self.__add__(-om) - - def __rsub__(self, om): - return (-self).__add__(om) - - def __mul__(self, o): - if not isinstance(o, self.module.ring.dtype): - try: - o = self.module.ring.convert(o) - except CoercionFailed: - return NotImplemented - return self.__class__(self.module, self.mul(self.data, o)) - - __rmul__ = __mul__ - - def __div__(self, o): - if not isinstance(o, self.module.ring.dtype): - try: - o = self.module.ring.convert(o) - except CoercionFailed: - return NotImplemented - return self.__class__(self.module, self.div(self.data, o)) - - __truediv__ = __div__ - - def __eq__(self, om): - if not isinstance(om, self.__class__) or om.module != self.module: - try: - om = self.module.convert(om) - except CoercionFailed: - return False - return self.eq(self.data, om.data) - - def __ne__(self, om): - return not self.__eq__(om) - -########################################################################## -## Free Modules ########################################################## -########################################################################## - - -class FreeModuleElement(ModuleElement): - """Element of a free module. Data stored as a tuple.""" - - def add(self, d1, d2): - return tuple(x + y for x, y in zip(d1, d2)) - - def mul(self, d, p): - return tuple(x * p for x in d) - - def div(self, d, p): - return tuple(x / p for x in d) - - def __repr__(self): - from sympy import sstr - return '[' + ', '.join(sstr(x) for x in self.data) + ']' - - def __iter__(self): - return self.data.__iter__() - - def __getitem__(self, idx): - return self.data[idx] - - -
    [docs]class FreeModule(Module): - """ - Abstract base class for free modules. - - Additional attributes: - - - rank - rank of the free module - - Non-implemented methods: - - - submodule - """ - - dtype = FreeModuleElement - - def __init__(self, ring, rank): - Module.__init__(self, ring) - self.rank = rank - - def __repr__(self): - return repr(self.ring) + "**" + repr(self.rank) - -
    [docs] def is_submodule(self, other): - """ - Returns True if ``other`` is a submodule of ``self``. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> F = QQ[x].free_module(2) - >>> M = F.submodule([2, x]) - >>> F.is_submodule(F) - True - >>> F.is_submodule(M) - True - >>> M.is_submodule(F) - False - """ - if isinstance(other, SubModule): - return other.container == self - if isinstance(other, FreeModule): - return other.ring == self.ring and other.rank == self.rank - return False -
    -
    [docs] def convert(self, elem, M=None): - """ - Convert ``elem`` into the internal representation. - - This method is called implicitly whenever computations involve elements - not in the internal representation. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> F = QQ[x].free_module(2) - >>> F.convert([1, 0]) - [1, 0] - """ - if isinstance(elem, FreeModuleElement): - if elem.module is self: - return elem - if elem.module.rank != self.rank: - raise CoercionFailed - return FreeModuleElement(self, - tuple(self.ring.convert(x, elem.module.ring) for x in elem.data)) - elif iterable(elem): - tpl = tuple(self.ring.convert(x) for x in elem) - if len(tpl) != self.rank: - raise CoercionFailed - return FreeModuleElement(self, tpl) - elif elem is 0: - return FreeModuleElement(self, (self.ring.convert(0),)*self.rank) - else: - raise CoercionFailed -
    -
    [docs] def is_zero(self): - """ - Returns True if ``self`` is a zero module. - - (If, as this implementation assumes, the coefficient ring is not the - zero ring, then this is equivalent to the rank being zero.) - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> QQ[x].free_module(0).is_zero() - True - >>> QQ[x].free_module(1).is_zero() - False - """ - return self.rank == 0 -
    -
    [docs] def basis(self): - """ - Return a set of basis elements. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> QQ[x].free_module(3).basis() - ([1, 0, 0], [0, 1, 0], [0, 0, 1]) - """ - from sympy.matrices import eye - M = eye(self.rank) - return tuple(self.convert(M.row(i)) for i in range(self.rank)) -
    -
    [docs] def quotient_module(self, submodule): - """ - Return a quotient module. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> M = QQ[x].free_module(2) - >>> M.quotient_module(M.submodule([1, x], [x, 2])) - QQ[x]**2/<[1, x], [x, 2]> - - Or more conicisely, using the overloaded division operator: - - >>> QQ[x].free_module(2) / [[1, x], [x, 2]] - QQ[x]**2/<[1, x], [x, 2]> - """ - return QuotientModule(self.ring, self, submodule) -
    -
    [docs] def multiply_ideal(self, other): - """ - Multiply ``self`` by the ideal ``other``. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> I = QQ[x].ideal(x) - >>> F = QQ[x].free_module(2) - >>> F.multiply_ideal(I) - <[x, 0], [0, x]> - """ - return self.submodule(*self.basis()).multiply_ideal(other) -
    -
    [docs] def identity_hom(self): - """ - Return the identity homomorphism on ``self``. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> QQ[x].free_module(2).identity_hom() - [1, 0] - [0, 1] : QQ[x]**2 -> QQ[x]**2 - """ - from sympy.polys.agca.homomorphisms import homomorphism - return homomorphism(self, self, self.basis()) - -
    -class FreeModulePolyRing(FreeModule): - """ - Free module over a generalized polynomial ring. - - Do not instantiate this, use the constructor method of the ring instead: - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> F = QQ[x].free_module(3) - >>> F - QQ[x]**3 - >>> F.contains([x, 1, 0]) - True - >>> F.contains([1/x, 0, 1]) - False - """ - - def __init__(self, ring, rank): - from sympy.polys.domains.polynomialring import PolynomialRingBase - FreeModule.__init__(self, ring, rank) - if not isinstance(ring, PolynomialRingBase): - raise NotImplementedError('This implementation only works over ' - + 'polynomial rings, got %s' % ring) - if not isinstance(ring.dom, Field): - raise NotImplementedError('Ground domain must be a field, ' - + 'got %s' % ring.dom) - - def submodule(self, *gens, **opts): - """ - Generate a submodule. - - >>> from sympy.abc import x, y - >>> from sympy import QQ - >>> M = QQ[x, y].free_module(2).submodule([x, x + y]) - >>> M - <[x, x + y]> - >>> M.contains([2*x, 2*x + 2*y]) - True - >>> M.contains([x, y]) - False - """ - return SubModulePolyRing(gens, self, **opts) - - -class FreeModuleQuotientRing(FreeModule): - """ - Free module over a quotient ring. - - Do not instantiate this, use the constructor method of the ring instead: - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> F = (QQ[x]/[x**2 + 1]).free_module(3) - >>> F - (QQ[x]/<x**2 + 1>)**3 - - Attributes - - - quot - the quotient module `R^n / IR^n`, where `R/I` is our ring - """ - - def __init__(self, ring, rank): - from sympy.polys.domains import QuotientRing - FreeModule.__init__(self, ring, rank) - if not isinstance(ring, QuotientRing): - raise NotImplementedError('This implementation only works over ' - + 'quotient rings, got %s' % ring) - F = self.ring.ring.free_module(self.rank) - self.quot = F / (self.ring.base_ideal*F) - - def __repr__(self): - return "(" + repr(self.ring) + ")" + "**" + repr(self.rank) - - def submodule(self, *gens, **opts): - """ - Generate a submodule. - - >>> from sympy.abc import x, y - >>> from sympy import QQ - >>> M = (QQ[x, y]/[x**2 - y**2]).free_module(2).submodule([x, x + y]) - >>> M - <[x + <x**2 - y**2>, x + y + <x**2 - y**2>]> - >>> M.contains([y**2, x**2 + x*y]) - True - >>> M.contains([x, y]) - False - """ - return SubModuleQuotientRing(gens, self, **opts) - - def lift(self, elem): - """ - Lift the element ``elem`` of self to the module self.quot. - - Note that self.quot is the same set as self, just as an R-module - and not as an R/I-module, so this makes sense. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> F = (QQ[x]/[x**2 + 1]).free_module(2) - >>> e = F.convert([1, 0]) - >>> e - [1 + <x**2 + 1>, 0 + <x**2 + 1>] - >>> L = F.quot - >>> l = F.lift(e) - >>> l - [1, 0] + <[x**2 + 1, 0], [0, x**2 + 1]> - >>> L.contains(l) - True - """ - return self.quot.convert([x.data for x in elem]) - - def unlift(self, elem): - """ - Push down an element of self.quot to self. - - This undoes ``lift``. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> F = (QQ[x]/[x**2 + 1]).free_module(2) - >>> e = F.convert([1, 0]) - >>> l = F.lift(e) - >>> e == l - False - >>> e == F.unlift(l) - True - """ - return self.convert(elem.data) - -########################################################################## -## Submodules and subquotients ########################################### -########################################################################## - - -
    [docs]class SubModule(Module): - """ - Base class for submodules. - - Attributes: - - - container - containing module - - gens - generators (subset of containing module) - - rank - rank of containing module - - Non-implemented methods: - - - _contains - - _syzygies - - _in_terms_of_generators - - _intersect - - _module_quotient - - Methods that likely need change in subclasses: - - - reduce_element - """ - - def __init__(self, gens, container): - Module.__init__(self, container.ring) - self.gens = tuple(container.convert(x) for x in gens) - self.container = container - self.rank = container.rank - self.ring = container.ring - self.dtype = container.dtype - - def __repr__(self): - return "<" + ", ".join(repr(x) for x in self.gens) + ">" - - def _contains(self, other): - """Implementation of containment. - Other is guaranteed to be FreeModuleElement.""" - raise NotImplementedError - - def _syzygies(self): - """Implementation of syzygy computation wrt self generators.""" - raise NotImplementedError - - def _in_terms_of_generators(self, e): - """Implementation of expression in terms of generators.""" - raise NotImplementedError - -
    [docs] def convert(self, elem, M=None): - """ - Convert ``elem`` into the internal represantition. - - Mostly called implicitly. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> M = QQ[x].free_module(2).submodule([1, x]) - >>> M.convert([2, 2*x]) - [2, 2*x] - """ - if isinstance(elem, self.container.dtype) and elem.module is self: - return elem - r = copy(self.container.convert(elem, M)) - r.module = self - if not self._contains(r): - raise CoercionFailed - return r -
    - def _intersect(self, other): - """Implementation of intersection. - Other is guaranteed to be a submodule of same free module.""" - raise NotImplementedError - - def _module_quotient(self, other): - """Implementation of quotient. - Other is guaranteed to be a submodule of same free module.""" - raise NotImplementedError - -
    [docs] def intersect(self, other, **options): - """ - Returns the intersection of ``self`` with submodule ``other``. - - >>> from sympy.abc import x, y - >>> from sympy import QQ - >>> F = QQ[x, y].free_module(2) - >>> F.submodule([x, x]).intersect(F.submodule([y, y])) - <[x*y, x*y]> - - Some implementation allow further options to be passed. Currently, to - only one implemented is ``relations=True``, in which case the function - will return a triple ``(res, rela, relb)``, where ``res`` is the - intersection module, and ``rela`` and ``relb`` are lists of coefficient - vectors, expressing the generators of ``res`` in terms of the - generators of ``self`` (``rela``) and ``other`` (``relb``). - - >>> F.submodule([x, x]).intersect(F.submodule([y, y]), relations=True) - (<[x*y, x*y]>, [(y,)], [(x,)]) - - The above result says: the intersection module is generated by the - single element `(-xy, -xy) = -y (x, x) = -x (y, y)`, where - `(x, x)` and `(y, y)` respectively are the unique generators of - the two modules being intersected. - """ - if not isinstance(other, SubModule): - raise TypeError('%s is not a SubModule' % other) - if other.container != self.container: - raise ValueError( - '%s is contained in a different free module' % other) - return self._intersect(other, **options) -
    -
    [docs] def module_quotient(self, other, **options): - r""" - Returns the module quotient of ``self`` by submodule ``other``. - - That is, if ``self`` is the module `M` and ``other`` is `N`, then - return the ideal `\{f \in R | fN \subset M\}`. - - >>> from sympy import QQ - >>> from sympy.abc import x, y - >>> F = QQ[x, y].free_module(2) - >>> S = F.submodule([x*y, x*y]) - >>> T = F.submodule([x, x]) - >>> S.module_quotient(T) - <y> - - Some implementations allow further options to be passed. Currently, the - only one implemented is ``relations=True``, which may only be passed - if ``other`` is prinicipal. In this case the function - will return a pair ``(res, rel)`` where ``res`` is the ideal, and - ``rel`` is a list of coefficient vectors, expressing the generators of - the ideal, multiplied by the generator of ``other`` in terms of - generators of ``self``. - - >>> S.module_quotient(T, relations=True) - (<y>, [[1]]) - - This means that the quotient ideal is generated by the single element - `y`, and that `y (x, x) = 1 (xy, xy)`, `(x, x)` and `(xy, xy)` being - the generators of `T` and `S`, respectively. - """ - if not isinstance(other, SubModule): - raise TypeError('%s is not a SubModule' % other) - if other.container != self.container: - raise ValueError( - '%s is contained in a different free module' % other) - return self._module_quotient(other, **options) -
    -
    [docs] def union(self, other): - """ - Returns the module generated by the union of ``self`` and ``other``. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> F = QQ[x].free_module(1) - >>> M = F.submodule([x**2 + x]) # <x(x+1)> - >>> N = F.submodule([x**2 - 1]) # <(x-1)(x+1)> - >>> M.union(N) == F.submodule([x+1]) - True - """ - if not isinstance(other, SubModule): - raise TypeError('%s is not a SubModule' % other) - if other.container != self.container: - raise ValueError( - '%s is contained in a different free module' % other) - return self.__class__(self.gens + other.gens, self.container) -
    -
    [docs] def is_zero(self): - """ - Return True if ``self`` is a zero module. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> F = QQ[x].free_module(2) - >>> F.submodule([x, 1]).is_zero() - False - >>> F.submodule([0, 0]).is_zero() - True - """ - return all(x == 0 for x in self.gens) -
    -
    [docs] def submodule(self, *gens): - """ - Generate a submodule. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> M = QQ[x].free_module(2).submodule([x, 1]) - >>> M.submodule([x**2, x]) - <[x**2, x]> - """ - if not self.subset(gens): - raise ValueError('%s not a subset of %s' % (gens, self)) - return self.__class__(gens, self.container) -
    -
    [docs] def is_full_module(self): - """ - Return True if ``self`` is the entire free module. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> F = QQ[x].free_module(2) - >>> F.submodule([x, 1]).is_full_module() - False - >>> F.submodule([1, 1], [1, 2]).is_full_module() - True - """ - return all(self.contains(x) for x in self.container.basis()) -
    -
    [docs] def is_submodule(self, other): - """ - Returns True if ``other`` is a submodule of ``self``. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> F = QQ[x].free_module(2) - >>> M = F.submodule([2, x]) - >>> N = M.submodule([2*x, x**2]) - >>> M.is_submodule(M) - True - >>> M.is_submodule(N) - True - >>> N.is_submodule(M) - False - """ - if isinstance(other, SubModule): - return self.container == other.container and \ - all(self.contains(x) for x in other.gens) - if isinstance(other, (FreeModule, QuotientModule)): - return self.container == other and self.is_full_module() - return False -
    -
    [docs] def syzygy_module(self, **opts): - r""" - Compute the syzygy module of the generators of ``self``. - - Suppose `M` is generated by `f_1, \dots, f_n` over the ring - `R`. Consider the homomorphism `\phi: R^n \to M`, given by - sending `(r_1, \dots, r_n) \to r_1 f_1 + \dots + r_n f_n`. - The syzygy module is defined to be the kernel of `\phi`. - - The syzygy module is zero iff the generators generate freely a free - submodule: - - >>> from sympy.abc import x, y - >>> from sympy import QQ - >>> QQ[x].free_module(2).submodule([1, 0], [1, 1]).syzygy_module().is_zero() - True - - A slightly more interesting example: - - >>> M = QQ[x, y].free_module(2).submodule([x, 2*x], [y, 2*y]) - >>> S = QQ[x, y].free_module(2).submodule([y, -x]) - >>> M.syzygy_module() == S - True - """ - F = self.ring.free_module(len(self.gens)) - # NOTE we filter out zero syzygies. This is for convenience of the - # _syzygies function and not meant to replace any real "generating set - # reduction" algorithm - return F.submodule(*[x for x in self._syzygies() if F.convert(x) != 0], - **opts) -
    -
    [docs] def in_terms_of_generators(self, e): - """ - Express element ``e`` of ``self`` in terms of the generators. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> F = QQ[x].free_module(2) - >>> M = F.submodule([1, 0], [1, 1]) - >>> M.in_terms_of_generators([x, x**2]) - [-x**2 + x, x**2] - """ - try: - e = self.convert(e) - except CoercionFailed: - raise ValueError('%s is not an element of %s' % (e, self)) - return self._in_terms_of_generators(e) -
    -
    [docs] def reduce_element(self, x): - """ - Reduce the element ``x`` of our ring modulo the ideal ``self``. - - Here "reduce" has no specific meaning, it could return a unique normal - form, simplify the expression a bit, or just do nothing. - """ - return x -
    -
    [docs] def quotient_module(self, other, **opts): - """ - Return a quotient module. - - This is the same as taking a submodule of a quotient of the containing - module. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> F = QQ[x].free_module(2) - >>> S1 = F.submodule([x, 1]) - >>> S2 = F.submodule([x**2, x]) - >>> S1.quotient_module(S2) - <[x, 1] + <[x**2, x]>> - - Or more coincisely, using the overloaded division operator: - - >>> F.submodule([x, 1]) / [(x**2, x)] - <[x, 1] + <[x**2, x]>> - """ - if not self.is_submodule(other): - raise ValueError('%s not a submodule of %s' % (other, self)) - return SubQuotientModule(self.gens, - self.container.quotient_module(other), **opts) -
    - def __add__(self, oth): - return self.container.quotient_module(self).convert(oth) - - __radd__ = __add__ - -
    [docs] def multiply_ideal(self, I): - """ - Multiply ``self`` by the ideal ``I``. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> I = QQ[x].ideal(x**2) - >>> M = QQ[x].free_module(2).submodule([1, 1]) - >>> I*M - <[x**2, x**2]> - """ - return self.submodule(*[x*g for [x] in I._module.gens for g in self.gens]) -
    -
    [docs] def inclusion_hom(self): - """ - Return a homomorphism representing the inclusion map of ``self``. - - That is, the natural map from ``self`` to ``self.container``. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> QQ[x].free_module(2).submodule([x, x]).inclusion_hom() - [1, 0] - [0, 1] : <[x, x]> -> QQ[x]**2 - """ - return self.container.identity_hom().restrict_domain(self) -
    -
    [docs] def identity_hom(self): - """ - Return the identity homomorphism on ``self``. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> QQ[x].free_module(2).submodule([x, x]).identity_hom() - [1, 0] - [0, 1] : <[x, x]> -> <[x, x]> - """ - return self.container.identity_hom().restrict_domain( - self).restrict_codomain(self) - -
    -
    [docs]class SubQuotientModule(SubModule): - """ - Submodule of a quotient module. - - Equivalently, quotient module of a submodule. - - Do not instantiate this, instead use the submodule or quotient_module - constructing methods: - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> F = QQ[x].free_module(2) - >>> S = F.submodule([1, 0], [1, x]) - >>> Q = F/[(1, 0)] - >>> S/[(1, 0)] == Q.submodule([5, x]) - True - - Attributes: - - - base - base module we are quotient of - - killed_module - submodule we are quotienting by - """ - def __init__(self, gens, container, **opts): - SubModule.__init__(self, gens, container) - self.killed_module = self.container.killed_module - # XXX it is important for some code below that the generators of base - # are in this particular order! - self.base = self.container.base.submodule( - *[x.data for x in self.gens], **opts).union(self.killed_module) - - def _contains(self, elem): - return self.base.contains(elem.data) - - def _syzygies(self): - # let N = self.killed_module be generated by e_1, ..., e_r - # let F = self.base be generated by f_1, ..., f_s and e_1, ..., e_r - # Then self = F/N. - # Let phi: R**s --> self be the evident surjection. - # Similarly psi: R**(s + r) --> F. - # We need to find generators for ker(phi). Let chi: R**s --> F be the - # evident lift of phi. For X in R**s, phi(X) = 0 iff chi(X) is - # contained in N, iff there exists Y in R**r such that - # psi(X, Y) = 0. - # Hence if alpha: R**(s + r) --> R**s is the projection map, then - # ker(phi) = alpha ker(psi). - return [X[:len(self.gens)] for X in self.base._syzygies()] - - def _in_terms_of_generators(self, e): - return self.base._in_terms_of_generators(e.data)[:len(self.gens)] - -
    [docs] def is_full_module(self): - """ - Return True if ``self`` is the entire free module. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> F = QQ[x].free_module(2) - >>> F.submodule([x, 1]).is_full_module() - False - >>> F.submodule([1, 1], [1, 2]).is_full_module() - True - """ - return self.base.is_full_module() -
    -
    [docs] def quotient_hom(self): - """ - Return the quotient homomorphism to self. - - That is, return the natural map from ``self.base`` to ``self``. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> M = (QQ[x].free_module(2) / [(1, x)]).submodule([1, 0]) - >>> M.quotient_hom() - [1, 0] - [0, 1] : <[1, 0], [1, x]> -> <[1, 0] + <[1, x]>, [1, x] + <[1, x]>> - """ - return self.base.identity_hom().quotient_codomain(self.killed_module) - -
    -_subs0 = lambda x: x[0] -_subs1 = lambda x: x[1:] - - -class ModuleOrder(ProductOrder): - """A product monomial order with a zeroth term as module index.""" - - def __init__(self, o1, o2, TOP): - if TOP: - ProductOrder.__init__(self, (o2, _subs1), (o1, _subs0)) - else: - ProductOrder.__init__(self, (o1, _subs0), (o2, _subs1)) - - -class SubModulePolyRing(SubModule): - """ - Submodule of a free module over a generalized polynomial ring. - - Do not instantiate this, use the constructor method of FreeModule instead: - - >>> from sympy.abc import x, y - >>> from sympy import QQ - >>> F = QQ[x, y].free_module(2) - >>> F.submodule([x, y], [1, 0]) - <[x, y], [1, 0]> - - Attributes: - - - order - monomial order used - """ - - #self._gb - cached groebner basis - #self._gbe - cached groebner basis relations - - def __init__(self, gens, container, order="lex", TOP=True): - SubModule.__init__(self, gens, container) - if not isinstance(container, FreeModulePolyRing): - raise NotImplementedError('This implementation is for submodules of ' - + 'FreeModulePolyRing, got %s' % container) - self.order = ModuleOrder(monomial_key(order), self.ring.order, TOP) - self._gb = None - self._gbe = None - - def __eq__(self, other): - if isinstance(other, SubModulePolyRing) and self.order != other.order: - return False - return SubModule.__eq__(self, other) - - def _groebner(self, extended=False): - """Returns a standard basis in sdm form.""" - from sympy.polys.distributedmodules import sdm_groebner, sdm_nf_mora - if self._gbe is None and extended: - gb, gbe = sdm_groebner( - [self.ring._vector_to_sdm(x, self.order) for x in self.gens], - sdm_nf_mora, self.order, self.ring.dom, extended=True) - self._gb, self._gbe = tuple(gb), tuple(gbe) - if self._gb is None: - self._gb = tuple(sdm_groebner( - [self.ring._vector_to_sdm(x, self.order) for x in self.gens], - sdm_nf_mora, self.order, self.ring.dom)) - if extended: - return self._gb, self._gbe - else: - return self._gb - - def _groebner_vec(self, extended=False): - """Returns a standard basis in element form.""" - if not extended: - return [self.convert(self.ring._sdm_to_vector(x, self.rank)) - for x in self._groebner()] - gb, gbe = self._groebner(extended=True) - return ([self.convert(self.ring._sdm_to_vector(x, self.rank)) - for x in gb], - [self.ring._sdm_to_vector(x, len(self.gens)) for x in gbe]) - - def _contains(self, x): - from sympy.polys.distributedmodules import sdm_zero, sdm_nf_mora - return sdm_nf_mora(self.ring._vector_to_sdm(x, self.order), - self._groebner(), self.order, self.ring.dom) == \ - sdm_zero() - - def _syzygies(self): - """Compute syzygies. See [SCA, algorithm 2.5.4].""" - # NOTE if self.gens is a standard basis, this can be done more - # efficiently using Schreyer's theorem - from sympy.matrices import eye - - # First bullet point - k = len(self.gens) - r = self.rank - im = eye(k) - Rkr = self.ring.free_module(r + k) - newgens = [] - for j, f in enumerate(self.gens): - m = [0]*(r + k) - for i, v in enumerate(f): - m[i] = f[i] - for i in range(k): - m[r + i] = im[j, i] - newgens.append(Rkr.convert(m)) - # Note: we need *descending* order on module index, and TOP=False to - # get an eliminetaion order - F = Rkr.submodule(*newgens, **{'order': 'ilex', 'TOP': False}) - - # Second bullet point: standard basis of F - G = F._groebner_vec() - - # Third bullet point: G0 = G intersect the new k components - G0 = [x[r:] for x in G if all(y == self.ring.convert(0) - for y in x[:r])] - - # Fourth and fifth bullet points: we are done - return G0 - - def _in_terms_of_generators(self, e): - """Expression in terms of generators. See [SCA, 2.8.1].""" - # NOTE: if gens is a standard basis, this can be done more efficiently - M = self.ring.free_module(self.rank).submodule(*((e,) + self.gens)) - S = M.syzygy_module( - order="ilex", TOP=False) # We want decreasing order! - G = S._groebner_vec() - # This list cannot not be empty since e is an element - e = list([x for x in G if self.ring.is_unit(x[0])])[0] - return [-x/e[0] for x in e[1:]] - - def reduce_element(self, x, NF=None): - """ - Reduce the element ``x`` of our container modulo ``self``. - - This applies the normal form ``NF`` to ``x``. If ``NF`` is passed - as none, the default Mora normal form is used (which is not unique!). - """ - from sympy.polys.distributedmodules import sdm_nf_mora - if NF is None: - NF = sdm_nf_mora - return self.container.convert(self.ring._sdm_to_vector(NF( - self.ring._vector_to_sdm(x, self.order), self._groebner(), - self.order, self.ring.dom), - self.rank)) - - def _intersect(self, other, relations=False): - # See: [SCA, section 2.8.2] - fi = self.gens - hi = other.gens - r = self.rank - ci = [[0]*(2*r) for _ in range(r)] - for k in range(r): - ci[k][k] = 1 - ci[k][r + k] = 1 - di = [list(f) + [0]*r for f in fi] - ei = [[0]*r + list(h) for h in hi] - syz = self.ring.free_module(2*r).submodule(*(ci + di + ei))._syzygies() - nonzero = [x for x in syz if any(y != self.ring.zero for y in x[:r])] - res = self.container.submodule(*([-y for y in x[:r]] for x in nonzero)) - reln1 = [x[r:r + len(fi)] for x in nonzero] - reln2 = [x[r + len(fi):] for x in nonzero] - if relations: - return res, reln1, reln2 - return res - - def _module_quotient(self, other, relations=False): - # See: [SCA, section 2.8.4] - if relations and len(other.gens) != 1: - raise NotImplementedError - if len(other.gens) == 0: - return self.ring.ideal(1) - elif len(other.gens) == 1: - # We do some trickery. Let f be the (vector!) generating ``other`` - # and f1, .., fn be the (vectors) generating self. - # Consider the submodule of R^{r+1} generated by (f, 1) and - # {(fi, 0) | i}. Then the intersection with the last module - # component yields the quotient. - g1 = list(other.gens[0]) + [1] - gi = [list(x) + [0] for x in self.gens] - # NOTE: We *need* to use an elimination order - M = self.ring.free_module(self.rank + 1).submodule(*([g1] + gi), - **{'order': 'ilex', 'TOP': False}) - if not relations: - return self.ring.ideal(*[x[-1] for x in M._groebner_vec() if - all(y == self.ring.zero for y in x[:-1])]) - else: - G, R = M._groebner_vec(extended=True) - indices = [i for i, x in enumerate(G) if - all(y == self.ring.zero for y in x[:-1])] - return (self.ring.ideal(*[G[i][-1] for i in indices]), - [[-x for x in R[i][1:]] for i in indices]) - # For more generators, we use I : <h1, .., hn> = intersection of - # {I : <hi> | i} - # TODO this can be done more efficiently - return reduce(lambda x, y: x.intersect(y), - (self._module_quotient(self.container.submodule(x)) for x in other.gens)) - - -class SubModuleQuotientRing(SubModule): - """ - Class for submodules of free modules over quotient rings. - - Do not instantiate this. Instead use the submodule methods. - - >>> from sympy.abc import x, y - >>> from sympy import QQ - >>> M = (QQ[x, y]/[x**2 - y**2]).free_module(2).submodule([x, x + y]) - >>> M - <[x + <x**2 - y**2>, x + y + <x**2 - y**2>]> - >>> M.contains([y**2, x**2 + x*y]) - True - >>> M.contains([x, y]) - False - - Attributes: - - - quot - the subquotient of `R^n/IR^n` generated by lifts of our generators - """ - - def __init__(self, gens, container): - SubModule.__init__(self, gens, container) - self.quot = self.container.quot.submodule( - *[self.container.lift(x) for x in self.gens]) - - def _contains(self, elem): - return self.quot._contains(self.container.lift(elem)) - - def _syzygies(self): - return [tuple(self.ring.convert(y, self.quot.ring) for y in x) - for x in self.quot._syzygies()] - - def _in_terms_of_generators(self, elem): - return [self.ring.convert(x, self.quot.ring) for x in - self.quot._in_terms_of_generators(self.container.lift(elem))] - -########################################################################## -## Quotient Modules ###################################################### -########################################################################## - - -class QuotientModuleElement(ModuleElement): - """Element of a quotient module.""" - - def eq(self, d1, d2): - """Equality comparison.""" - return self.module.killed_module.contains(d1 - d2) - - def __repr__(self): - from sympy import sstr - return repr(self.data) + " + " + repr(self.module.killed_module) - - -
    [docs]class QuotientModule(Module): - """ - Class for quotient modules. - - Do not instantiate this directly. For subquotients, see the - SubQuotientModule class. - - Attributes: - - - base - the base module we are a quotient of - - killed_module - the submodule we are quotienting by - - rank of the base - """ - - dtype = QuotientModuleElement - - def __init__(self, ring, base, submodule): - Module.__init__(self, ring) - if not base.is_submodule(submodule): - raise ValueError('%s is not a submodule of %s' % (submodule, base)) - self.base = base - self.killed_module = submodule - self.rank = base.rank - - def __repr__(self): - return repr(self.base) + "/" + repr(self.killed_module) - -
    [docs] def is_zero(self): - """ - Return True if ``self`` is a zero module. - - This happens if and only if the base module is the same as the - submodule being killed. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> F = QQ[x].free_module(2) - >>> (F/[(1, 0)]).is_zero() - False - >>> (F/[(1, 0), (0, 1)]).is_zero() - True - """ - return self.base == self.killed_module -
    -
    [docs] def is_submodule(self, other): - """ - Return True if ``other`` is a submodule of ``self``. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> Q = QQ[x].free_module(2) / [(x, x)] - >>> S = Q.submodule([1, 0]) - >>> Q.is_submodule(S) - True - >>> S.is_submodule(Q) - False - """ - if isinstance(other, QuotientModule): - return self.killed_module == other.killed_module and \ - self.base.is_submodule(other.base) - if isinstance(other, SubQuotientModule): - return other.container == self - return False -
    -
    [docs] def submodule(self, *gens, **opts): - """ - Generate a submodule. - - This is the same as taking a quotient of a submodule of the base - module. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> Q = QQ[x].free_module(2) / [(x, x)] - >>> Q.submodule([x, 0]) - <[x, 0] + <[x, x]>> - """ - return SubQuotientModule(gens, self, **opts) -
    -
    [docs] def convert(self, elem, M=None): - """ - Convert ``elem`` into the internal representation. - - This method is called implicitly whenever computations involve elements - not in the internal representation. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> F = QQ[x].free_module(2) / [(1, 2), (1, x)] - >>> F.convert([1, 0]) - [1, 0] + <[1, 2], [1, x]> - """ - if isinstance(elem, QuotientModuleElement): - if elem.module is self: - return elem - if self.killed_module.is_submodule(elem.module.killed_module): - return QuotientModuleElement(self, self.base.convert(elem.data)) - raise CoercionFailed - return QuotientModuleElement(self, self.base.convert(elem)) -
    -
    [docs] def identity_hom(self): - """ - Return the identity homomorphism on ``self``. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> M = QQ[x].free_module(2) / [(1, 2), (1, x)] - >>> M.identity_hom() - [1, 0] - [0, 1] : QQ[x]**2/<[1, 2], [1, x]> -> QQ[x]**2/<[1, 2], [1, x]> - """ - return self.base.identity_hom().quotient_codomain( - self.killed_module).quotient_domain(self.killed_module) -
    -
    [docs] def quotient_hom(self): - """ - Return the quotient homomorphism to ``self``. - - That is, return a homomorphism representing the natural map from - ``self.base`` to ``self``. - - >>> from sympy.abc import x - >>> from sympy import QQ - >>> M = QQ[x].free_module(2) / [(1, 2), (1, x)] - >>> M.quotient_hom() - [1, 0] - [0, 1] : QQ[x]**2 -> QQ[x]**2/<[1, 2], [1, x]> - """ - return self.base.identity_hom().quotient_codomain( - self.killed_module)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/polys/constructor.html b/dev-py3k/_modules/sympy/polys/constructor.html deleted file mode 100644 index 02630d0ab41..00000000000 --- a/dev-py3k/_modules/sympy/polys/constructor.html +++ /dev/null @@ -1,358 +0,0 @@ - - - - - - - - - - sympy.polys.constructor — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.polys.constructor

    -"""Tools for constructing domains for expressions. """
    -
    -from sympy.polys.polyutils import parallel_dict_from_basic
    -from sympy.polys.polyoptions import build_options
    -from sympy.polys.polyerrors import GeneratorsNeeded
    -from sympy.polys.domains import ZZ, QQ, RR, EX
    -from sympy.assumptions import ask, Q
    -from sympy.core import sympify
    -
    -
    -def _construct_simple(coeffs, opt):
    -    """Handle simple domains, e.g.: ZZ, QQ, RR and algebraic domains. """
    -    result, rationals, reals, algebraics = {}, False, False, False
    -
    -    if opt.extension is True:
    -        is_algebraic = lambda coeff: ask(Q.algebraic(coeff))
    -    else:
    -        is_algebraic = lambda coeff: False
    -
    -    # XXX: add support for a + b*I coefficients
    -    for coeff in coeffs:
    -        if coeff.is_Rational:
    -            if not coeff.is_Integer:
    -                rationals = True
    -        elif coeff.is_Float:
    -            if not algebraics:
    -                reals = True
    -            else:
    -                # there are both reals and algebraics -> EX
    -                return False
    -        elif is_algebraic(coeff):
    -            if not reals:
    -                algebraics = True
    -            else:
    -                # there are both algebraics and reals -> EX
    -                return False
    -        else:
    -            # this is a composite domain, e.g. ZZ[X], EX
    -            return None
    -
    -    if algebraics:
    -        domain, result = _construct_algebraic(coeffs, opt)
    -    else:
    -        if reals:
    -            domain = RR
    -        else:
    -            if opt.field or rationals:
    -                domain = QQ
    -            else:
    -                domain = ZZ
    -
    -        result = []
    -
    -        for coeff in coeffs:
    -            result.append(domain.from_sympy(coeff))
    -
    -    return domain, result
    -
    -
    -def _construct_algebraic(coeffs, opt):
    -    """We know that coefficients are algebraic so construct the extension. """
    -    from sympy.polys.numberfields import primitive_element
    -
    -    result, exts = [], set([])
    -
    -    for coeff in coeffs:
    -        if coeff.is_Rational:
    -            coeff = (None, 0, QQ.from_sympy(coeff))
    -        else:
    -            a = coeff.as_coeff_add()[0]
    -            coeff -= a
    -
    -            b = coeff.as_coeff_mul()[0]
    -            coeff /= b
    -
    -            exts.add(coeff)
    -
    -            a = QQ.from_sympy(a)
    -            b = QQ.from_sympy(b)
    -
    -            coeff = (coeff, b, a)
    -
    -        result.append(coeff)
    -
    -    exts = list(exts)
    -
    -    g, span, H = primitive_element(exts, ex=True, polys=True)
    -    root = sum([ s*ext for s, ext in zip(span, exts) ])
    -
    -    domain, g = QQ.algebraic_field((g, root)), g.rep.rep
    -
    -    for i, (coeff, a, b) in enumerate(result):
    -        if coeff is not None:
    -            coeff = a*domain.dtype.from_list(H[exts.index(coeff)], g, QQ) + b
    -        else:
    -            coeff = domain.dtype.from_list([b], g, QQ)
    -
    -        result[i] = coeff
    -
    -    return domain, result
    -
    -
    -def _construct_composite(coeffs, opt):
    -    """Handle composite domains, e.g.: ZZ[X], QQ[X], ZZ(X), QQ(X). """
    -    numers, denoms = [], []
    -
    -    for coeff in coeffs:
    -        numer, denom = coeff.as_numer_denom()
    -
    -        numers.append(numer)
    -        denoms.append(denom)
    -
    -    try:
    -        polys, gens = parallel_dict_from_basic(numers + denoms)  # XXX: sorting
    -    except GeneratorsNeeded:
    -        return None
    -
    -    if any(gen.is_number for gen in gens):
    -        return None  # generators are number-like so lets better use EX
    -
    -    n = len(gens)
    -    k = len(polys)//2
    -
    -    numers = polys[:k]
    -    denoms = polys[k:]
    -
    -    if opt.field:
    -        fractions = True
    -    else:
    -        fractions, zeros = False, (0,)*n
    -
    -        for denom in denoms:
    -            if len(denom) > 1 or zeros not in denom:
    -                fractions = True
    -                break
    -
    -    coeffs = set([])
    -
    -    if not fractions:
    -        for numer, denom in zip(numers, denoms):
    -            denom = denom[zeros]
    -
    -            for monom, coeff in numer.items():
    -                coeff /= denom
    -                coeffs.add(coeff)
    -                numer[monom] = coeff
    -    else:
    -        for numer, denom in zip(numers, denoms):
    -            coeffs.update(list(numer.values()))
    -            coeffs.update(list(denom.values()))
    -
    -    rationals, reals = False, False
    -
    -    for coeff in coeffs:
    -        if coeff.is_Rational:
    -            if not coeff.is_Integer:
    -                rationals = True
    -        elif coeff.is_Float:
    -            reals = True
    -            break
    -
    -    if reals:
    -        ground = RR
    -    elif rationals:
    -        ground = QQ
    -    else:
    -        ground = ZZ
    -
    -    result = []
    -
    -    if not fractions:
    -        domain = ground.poly_ring(*gens)
    -
    -        for numer in numers:
    -            for monom, coeff in numer.items():
    -                numer[monom] = ground.from_sympy(coeff)
    -
    -            result.append(domain(numer))
    -    else:
    -        domain = ground.frac_field(*gens)
    -
    -        for numer, denom in zip(numers, denoms):
    -            for monom, coeff in numer.items():
    -                numer[monom] = ground.from_sympy(coeff)
    -
    -            for monom, coeff in denom.items():
    -                denom[monom] = ground.from_sympy(coeff)
    -
    -            result.append(domain((numer, denom)))
    -
    -    return domain, result
    -
    -
    -def _construct_expression(coeffs, opt):
    -    """The last resort case, i.e. use the expression domain. """
    -    domain, result = EX, []
    -
    -    for coeff in coeffs:
    -        result.append(domain.from_sympy(coeff))
    -
    -    return domain, result
    -
    -
    -
    [docs]def construct_domain(obj, **args): - """Construct a minimal domain for the list of coefficients. """ - opt = build_options(args) - - if hasattr(obj, '__iter__'): - if isinstance(obj, dict): - monoms, coeffs = list(zip(*list(obj.items()))) - else: - coeffs = obj - else: - coeffs = [obj] - - coeffs = list(map(sympify, coeffs)) - result = _construct_simple(coeffs, opt) - - if result is not None: - if result is not False: - domain, coeffs = result - else: - domain, coeffs = _construct_expression(coeffs, opt) - else: - if opt.composite: - result = _construct_composite(coeffs, opt) - else: - result = None - - if result is not None: - domain, coeffs = result - else: - domain, coeffs = _construct_expression(coeffs, opt) - - if hasattr(obj, '__iter__'): - if isinstance(obj, dict): - return domain, dict(list(zip(monoms, coeffs))) - else: - return domain, coeffs - else: - return domain, coeffs[0]
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/polys/densearith.html b/dev-py3k/_modules/sympy/polys/densearith.html deleted file mode 100644 index 9dd1d2e5b0b..00000000000 --- a/dev-py3k/_modules/sympy/polys/densearith.html +++ /dev/null @@ -1,2139 +0,0 @@ - - - - - - - - - - sympy.polys.densearith — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.polys.densearith

    -"""Arithmetics for dense recursive polynomials in ``K[x]`` or ``K[X]``. """
    -
    -from sympy.polys.densebasic import (
    -    dup_LC, dmp_LC,
    -    dup_degree, dmp_degree,
    -    dup_normal,
    -    dup_strip, dmp_strip,
    -    dmp_zero_p, dmp_zero,
    -    dmp_one_p, dmp_one,
    -    dmp_ground, dmp_zeros)
    -
    -from sympy.polys.polyerrors import (
    -    ExactQuotientFailed)
    -
    -from sympy.utilities import cythonized
    -
    -
    -@cythonized("i,n,m")
    -def dup_add_term(f, c, i, K):
    -    """
    -    Add ``c*x**i`` to ``f`` in ``K[x]``.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.polys.domains import ZZ
    -    >>> from sympy.polys.densearith import dup_add_term
    -
    -    >>> f = ZZ.map([1, 0, -1])
    -
    -    >>> dup_add_term(f, ZZ(2), 4, ZZ)
    -    [2, 0, 1, 0, -1]
    -
    -    """
    -    if not c:
    -        return f
    -
    -    n = len(f)
    -    m = n - i - 1
    -
    -    if i == n - 1:
    -        return dup_strip([f[0] + c] + f[1:])
    -    else:
    -        if i >= n:
    -            return [c] + [K.zero]*(i - n) + f
    -        else:
    -            return f[:m] + [f[m] + c] + f[m + 1:]
    -
    -
    -@cythonized("i,u,v,n,m")
    -
    [docs]def dmp_add_term(f, c, i, u, K): - """ - Add ``c(x_2..x_u)*x_0**i`` to ``f`` in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dmp_add_term - - >>> f = ZZ.map([[1, 0], [1]]) - >>> c = ZZ.map([2]) - - >>> dmp_add_term(f, c, 2, 1, ZZ) - [[2], [1, 0], [1]] - - """ - if not u: - return dup_add_term(f, c, i, K) - - v = u - 1 - - if dmp_zero_p(c, v): - return f - - n = len(f) - m = n - i - 1 - - if i == n - 1: - return dmp_strip([dmp_add(f[0], c, v, K)] + f[1:], u) - else: - if i >= n: - return [c] + dmp_zeros(i - n, v, K) + f - else: - return f[:m] + [dmp_add(f[m], c, v, K)] + f[m + 1:] - -
    -@cythonized("i,n,m") -def dup_sub_term(f, c, i, K): - """ - Subtract ``c*x**i`` from ``f`` in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dup_sub_term - - >>> f = ZZ.map([2, 0, 1, 0, -1]) - - >>> dup_sub_term(f, ZZ(2), 4, ZZ) - [1, 0, -1] - - """ - if not c: - return f - - n = len(f) - m = n - i - 1 - - if i == n - 1: - return dup_strip([f[0] - c] + f[1:]) - else: - if i >= n: - return [-c] + [K.zero]*(i - n) + f - else: - return f[:m] + [f[m] - c] + f[m + 1:] - - -@cythonized("i,u,v,n,m") -
    [docs]def dmp_sub_term(f, c, i, u, K): - """ - Subtract ``c(x_2..x_u)*x_0**i`` from ``f`` in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dmp_sub_term - - >>> f = ZZ.map([[2], [1, 0], [1]]) - >>> c = ZZ.map([2]) - - >>> dmp_sub_term(f, c, 2, 1, ZZ) - [[1, 0], [1]] - - """ - if not u: - return dup_add_term(f, -c, i, K) - - v = u - 1 - - if dmp_zero_p(c, v): - return f - - n = len(f) - m = n - i - 1 - - if i == n - 1: - return dmp_strip([dmp_sub(f[0], c, v, K)] + f[1:], u) - else: - if i >= n: - return [dmp_neg(c, v, K)] + dmp_zeros(i - n, v, K) + f - else: - return f[:m] + [dmp_sub(f[m], c, v, K)] + f[m + 1:] - -
    -@cythonized("i") -def dup_mul_term(f, c, i, K): - """ - Multiply ``f`` by ``c*x**i`` in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dup_mul_term - - >>> f = ZZ.map([1, 0, -1]) - - >>> dup_mul_term(f, ZZ(3), 2, ZZ) - [3, 0, -3, 0, 0] - - """ - if not c or not f: - return [] - else: - return [ cf * c for cf in f ] + [K.zero]*i - - -@cythonized("i,u,v") -
    [docs]def dmp_mul_term(f, c, i, u, K): - """ - Multiply ``f`` by ``c(x_2..x_u)*x_0**i`` in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dmp_mul_term - - >>> f = ZZ.map([[1, 0], [1], []]) - >>> c = ZZ.map([3, 0]) - - >>> dmp_mul_term(f, c, 2, 1, ZZ) - [[3, 0, 0], [3, 0], [], [], []] - - """ - if not u: - return dup_mul_term(f, c, i, K) - - v = u - 1 - - if dmp_zero_p(f, u): - return f - if dmp_zero_p(c, v): - return dmp_zero(u) - else: - return [ dmp_mul(cf, c, v, K) for cf in f ] + dmp_zeros(i, v, K) - -
    -def dup_add_ground(f, c, K): - """ - Add an element of the ground domain to ``f``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dup_add_ground - - >>> f = ZZ.map([1, 2, 3, 4]) - - >>> dup_add_ground(f, ZZ(4), ZZ) - [1, 2, 3, 8] - - """ - return dup_add_term(f, c, 0, K) - - -
    [docs]def dmp_add_ground(f, c, u, K): - """ - Add an element of the ground domain to ``f``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dmp_add_ground - - >>> f = ZZ.map([[1], [2], [3], [4]]) - - >>> dmp_add_ground(f, ZZ(4), 1, ZZ) - [[1], [2], [3], [8]] - - """ - return dmp_add_term(f, dmp_ground(c, u - 1), 0, u, K) - -
    -def dup_sub_ground(f, c, K): - """ - Subtract an element of the ground domain from ``f``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dup_sub_ground - - >>> f = ZZ.map([1, 2, 3, 4]) - - >>> dup_sub_ground(f, ZZ(4), ZZ) - [1, 2, 3, 0] - - """ - return dup_sub_term(f, c, 0, K) - - -
    [docs]def dmp_sub_ground(f, c, u, K): - """ - Subtract an element of the ground domain from ``f``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dmp_sub_ground - - >>> f = ZZ.map([[1], [2], [3], [4]]) - - >>> dmp_sub_ground(f, ZZ(4), 1, ZZ) - [[1], [2], [3], []] - - """ - return dmp_sub_term(f, dmp_ground(c, u - 1), 0, u, K) - -
    -def dup_mul_ground(f, c, K): - """ - Multiply ``f`` by a constant value in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dup_mul_ground - - >>> f = ZZ.map([1, 2, -1]) - - >>> dup_mul_ground(f, ZZ(3), ZZ) - [3, 6, -3] - - """ - if not c or not f: - return [] - else: - return [ cf * c for cf in f ] - - -@cythonized("u,v") -
    [docs]def dmp_mul_ground(f, c, u, K): - """ - Multiply ``f`` by a constant value in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dmp_mul_ground - - >>> f = ZZ.map([[2], [2, 0]]) - - >>> dmp_mul_ground(f, ZZ(3), 1, ZZ) - [[6], [6, 0]] - - """ - if not u: - return dup_mul_ground(f, c, K) - - v = u - 1 - - return [ dmp_mul_ground(cf, c, v, K) for cf in f ] - -
    -def dup_quo_ground(f, c, K): - """ - Quotient by a constant in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ, QQ - >>> from sympy.polys.densearith import dup_quo_ground - - >>> f = ZZ.map([3, 0, 2]) - >>> g = QQ.map([3, 0, 2]) - - >>> dup_quo_ground(f, ZZ(2), ZZ) - [1, 0, 1] - - >>> dup_quo_ground(g, QQ(2), QQ) - [3/2, 0/1, 1/1] - - """ - if not c: - raise ZeroDivisionError('polynomial division') - if not f: - return f - - if K.has_Field or not K.is_Exact: - return [ K.quo(cf, c) for cf in f ] - else: - return [ cf // c for cf in f ] - - -@cythonized("u,v") -
    [docs]def dmp_quo_ground(f, c, u, K): - """ - Quotient by a constant in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ, QQ - >>> from sympy.polys.densearith import dmp_quo_ground - - >>> f = ZZ.map([[2, 0], [3], []]) - >>> g = QQ.map([[2, 0], [3], []]) - - >>> dmp_quo_ground(f, ZZ(2), 1, ZZ) - [[1, 0], [1], []] - - >>> dmp_quo_ground(g, QQ(2), 1, QQ) - [[1/1, 0/1], [3/2], []] - - """ - if not u: - return dup_quo_ground(f, c, K) - - v = u - 1 - - return [ dmp_quo_ground(cf, c, v, K) for cf in f ] - -
    -def dup_exquo_ground(f, c, K): - """ - Exact quotient by a constant in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ, QQ - >>> from sympy.polys.densearith import dup_exquo_ground - - >>> f = QQ.map([1, 0, 2]) - - >>> dup_exquo_ground(f, QQ(2), QQ) - [1/2, 0/1, 1/1] - - """ - if not c: - raise ZeroDivisionError('polynomial division') - if not f: - return f - - return [ K.exquo(cf, c) for cf in f ] - - -@cythonized("u,v") -
    [docs]def dmp_exquo_ground(f, c, u, K): - """ - Exact quotient by a constant in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ, QQ - >>> from sympy.polys.densearith import dmp_exquo_ground - - >>> f = QQ.map([[1, 0], [2], []]) - - >>> dmp_exquo_ground(f, QQ(2), 1, QQ) - [[1/2, 0/1], [1/1], []] - - """ - if not u: - return dup_exquo_ground(f, c, K) - - v = u - 1 - - return [ dmp_exquo_ground(cf, c, v, K) for cf in f ] - -
    -@cythonized("n") -
    [docs]def dup_lshift(f, n, K): - """ - Efficiently multiply ``f`` by ``x**n`` in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dup_lshift - - >>> f = ZZ.map([1, 0, 1]) - - >>> dup_lshift(f, 2, ZZ) - [1, 0, 1, 0, 0] - - """ - if not f: - return f - else: - return f + [K.zero]*n - -
    -@cythonized("n") -
    [docs]def dup_rshift(f, n, K): - """ - Efficiently divide ``f`` by ``x**n`` in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dup_rshift - - >>> f = ZZ.map([1, 0, 1, 0, 0]) - >>> g = ZZ.map([1, 0, 1, 0, 2]) - - >>> dup_rshift(f, 2, ZZ) - [1, 0, 1] - - >>> dup_rshift(g, 2, ZZ) - [1, 0, 1] - - """ - return f[:-n] - -
    -def dup_abs(f, K): - """ - Make all coefficients positive in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dup_abs - - >>> f = ZZ.map([1, 0, -1]) - - >>> dup_abs(f, ZZ) - [1, 0, 1] - - """ - return [ K.abs(coeff) for coeff in f ] - - -@cythonized("u,v") -
    [docs]def dmp_abs(f, u, K): - """ - Make all coefficients positive in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dmp_abs - - >>> f = ZZ.map([[1, 0], [-1], []]) - - >>> dmp_abs(f, 1, ZZ) - [[1, 0], [1], []] - - """ - if not u: - return dup_abs(f, K) - - v = u - 1 - - return [ dmp_abs(cf, v, K) for cf in f ] - -
    -def dup_neg(f, K): - """ - Negate a polynomial in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dup_neg - - >>> f = ZZ.map([1, 0, -1]) - - >>> dup_neg(f, ZZ) - [-1, 0, 1] - - """ - return [ -coeff for coeff in f ] - - -@cythonized("u,v") -
    [docs]def dmp_neg(f, u, K): - """ - Negate a polynomial in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dmp_neg - - >>> f = ZZ.map([[1, 0], [-1], []]) - - >>> dmp_neg(f, 1, ZZ) - [[-1, 0], [1], []] - - """ - if not u: - return dup_neg(f, K) - - v = u - 1 - - return [ dmp_neg(cf, v, K) for cf in f ] - -
    -@cythonized("df,dg,k") -def dup_add(f, g, K): - """ - Add dense polynomials in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dup_add - - >>> f = ZZ.map([1, 0, -1]) - >>> g = ZZ.map([1, -2]) - - >>> dup_add(f, g, ZZ) - [1, 1, -3] - - """ - if not f: - return g - if not g: - return f - - df = dup_degree(f) - dg = dup_degree(g) - - if df == dg: - return dup_strip([ a + b for a, b in zip(f, g) ]) - else: - k = abs(df - dg) - - if df > dg: - h, f = f[:k], f[k:] - else: - h, g = g[:k], g[k:] - - return h + [ a + b for a, b in zip(f, g) ] - - -@cythonized("u,v,df,dg,k") -
    [docs]def dmp_add(f, g, u, K): - """ - Add dense polynomials in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dmp_add - - >>> f = ZZ.map([[1], [], [1, 0]]) - >>> g = ZZ.map([[1, 0], [1], []]) - - >>> dmp_add(f, g, 1, ZZ) - [[1, 1], [1], [1, 0]] - - """ - if not u: - return dup_add(f, g, K) - - df = dmp_degree(f, u) - - if df < 0: - return g - - dg = dmp_degree(g, u) - - if dg < 0: - return f - - v = u - 1 - - if df == dg: - return dmp_strip([ dmp_add(a, b, v, K) for a, b in zip(f, g) ], u) - else: - k = abs(df - dg) - - if df > dg: - h, f = f[:k], f[k:] - else: - h, g = g[:k], g[k:] - - return h + [ dmp_add(a, b, v, K) for a, b in zip(f, g) ] - -
    -@cythonized("df,dg,k") -def dup_sub(f, g, K): - """ - Subtract dense polynomials in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dup_sub - - >>> f = ZZ.map([1, 0, -1]) - >>> g = ZZ.map([1, -2]) - - >>> dup_sub(f, g, ZZ) - [1, -1, 1] - - """ - if not f: - return dup_neg(g, K) - if not g: - return f - - df = dup_degree(f) - dg = dup_degree(g) - - if df == dg: - return dup_strip([ a - b for a, b in zip(f, g) ]) - else: - k = abs(df - dg) - - if df > dg: - h, f = f[:k], f[k:] - else: - h, g = dup_neg(g[:k], K), g[k:] - - return h + [ a - b for a, b in zip(f, g) ] - - -@cythonized("u,v,df,dg,k") -
    [docs]def dmp_sub(f, g, u, K): - """ - Subtract dense polynomials in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dmp_sub - - >>> f = ZZ.map([[1], [], [1, 0]]) - >>> g = ZZ.map([[1, 0], [1], []]) - - >>> dmp_sub(f, g, 1, ZZ) - [[-1, 1], [-1], [1, 0]] - - """ - if not u: - return dup_sub(f, g, K) - - df = dmp_degree(f, u) - - if df < 0: - return dmp_neg(g, u, K) - - dg = dmp_degree(g, u) - - if dg < 0: - return f - - v = u - 1 - - if df == dg: - return dmp_strip([ dmp_sub(a, b, v, K) for a, b in zip(f, g) ], u) - else: - k = abs(df - dg) - - if df > dg: - h, f = f[:k], f[k:] - else: - h, g = dmp_neg(g[:k], u, K), g[k:] - - return h + [ dmp_sub(a, b, v, K) for a, b in zip(f, g) ] - -
    -def dup_add_mul(f, g, h, K): - """ - Returns ``f + g*h`` where ``f, g, h`` are in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dup_add_mul - - >>> f = ZZ.map([1, 0, -1]) - >>> g = ZZ.map([1, -2]) - >>> h = ZZ.map([1, 2]) - - >>> dup_add_mul(f, g, h, ZZ) - [2, 0, -5] - - """ - return dup_add(f, dup_mul(g, h, K), K) - - -@cythonized("u") -
    [docs]def dmp_add_mul(f, g, h, u, K): - """ - Returns ``f + g*h`` where ``f, g, h`` are in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dmp_add_mul - - >>> f = ZZ.map([[1], [], [1, 0]]) - >>> g = ZZ.map([[1], []]) - >>> h = ZZ.map([[1], [2]]) - - >>> dmp_add_mul(f, g, h, 1, ZZ) - [[2], [2], [1, 0]] - - """ - return dmp_add(f, dmp_mul(g, h, u, K), u, K) - -
    -def dup_sub_mul(f, g, h, K): - """ - Returns ``f - g*h`` where ``f, g, h`` are in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dup_sub_mul - - >>> f = ZZ.map([1, 0, -1]) - >>> g = ZZ.map([1, -2]) - >>> h = ZZ.map([1, 2]) - - >>> dup_sub_mul(f, g, h, ZZ) - [3] - - """ - return dup_sub(f, dup_mul(g, h, K), K) - - -@cythonized("u") -
    [docs]def dmp_sub_mul(f, g, h, u, K): - """ - Returns ``f - g*h`` where ``f, g, h`` are in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dmp_sub_mul - - >>> f = ZZ.map([[1], [], [1, 0]]) - >>> g = ZZ.map([[1], []]) - >>> h = ZZ.map([[1], [2]]) - - >>> dmp_sub_mul(f, g, h, 1, ZZ) - [[-2], [1, 0]] - - """ - return dmp_sub(f, dmp_mul(g, h, u, K), u, K) - -
    -@cythonized("df,dg,i,j") -def dup_mul(f, g, K): - """ - Multiply dense polynomials in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dup_mul - - >>> f = ZZ.map([1, -2]) - >>> g = ZZ.map([1, 2]) - - >>> dup_mul(f, g, ZZ) - [1, 0, -4] - - """ - if f == g: - return dup_sqr(f, K) - - if not (f and g): - return [] - - df = dup_degree(f) - dg = dup_degree(g) - - h = [] - - for i in range(0, df + dg + 1): - coeff = K.zero - - for j in range(max(0, i - dg), min(df, i) + 1): - coeff += f[j]*g[i - j] - - h.append(coeff) - - return dup_strip(h) - - -@cythonized("u,v,df,dg,i,j") -
    [docs]def dmp_mul(f, g, u, K): - """ - Multiply dense polynomials in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dmp_mul - - >>> f = ZZ.map([[1, 0], [1]]) - >>> g = ZZ.map([[1], []]) - - >>> dmp_mul(f, g, 1, ZZ) - [[1, 0], [1], []] - - """ - if not u: - return dup_mul(f, g, K) - - if f == g: - return dmp_sqr(f, u, K) - - df = dmp_degree(f, u) - - if df < 0: - return f - - dg = dmp_degree(g, u) - - if dg < 0: - return g - - h, v = [], u - 1 - - for i in range(0, df + dg + 1): - coeff = dmp_zero(v) - - for j in range(max(0, i - dg), min(df, i) + 1): - coeff = dmp_add(coeff, dmp_mul(f[j], g[i - j], v, K), v, K) - - h.append(coeff) - - return dmp_strip(h, u) - -
    -@cythonized("df,jmin,jmax,n,i,j") -def dup_sqr(f, K): - """ - Square dense polynomials in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dup_sqr - - >>> f = ZZ.map([1, 0, 1]) - - >>> dup_sqr(f, ZZ) - [1, 0, 2, 0, 1] - - """ - df, h = dup_degree(f), [] - - for i in range(0, 2*df + 1): - c = K.zero - - jmin = max(0, i - df) - jmax = min(i, df) - - n = jmax - jmin + 1 - - jmax = jmin + n // 2 - 1 - - for j in range(jmin, jmax + 1): - c += f[j]*f[i - j] - - c += c - - if n & 1: - elem = f[jmax + 1] - c += elem**2 - - h.append(c) - - return dup_strip(h) - - -@cythonized("u,v,df,jmin,jmax,n,i,j") -
    [docs]def dmp_sqr(f, u, K): - """ - Square dense polynomials in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dmp_sqr - - >>> f = ZZ.map([[1], [1, 0], [1, 0, 0]]) - - >>> dmp_sqr(f, 1, ZZ) - [[1], [2, 0], [3, 0, 0], [2, 0, 0, 0], [1, 0, 0, 0, 0]] - - """ - if not u: - return dup_sqr(f, K) - - df = dmp_degree(f, u) - - if df < 0: - return f - - h, v = [], u - 1 - - for i in range(0, 2*df + 1): - c = dmp_zero(v) - - jmin = max(0, i - df) - jmax = min(i, df) - - n = jmax - jmin + 1 - - jmax = jmin + n // 2 - 1 - - for j in range(jmin, jmax + 1): - c = dmp_add(c, dmp_mul(f[j], f[i - j], v, K), v, K) - - c = dmp_mul_ground(c, K(2), v, K) - - if n & 1: - elem = dmp_sqr(f[jmax + 1], v, K) - c = dmp_add(c, elem, v, K) - - h.append(c) - - return dmp_strip(h, u) - -
    -@cythonized("n,m") -def dup_pow(f, n, K): - """ - Raise ``f`` to the ``n``-th power in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dup_pow - - >>> dup_pow([ZZ(1), -ZZ(2)], 3, ZZ) - [1, -6, 12, -8] - - """ - if not n: - return [K.one] - if n < 0: - raise ValueError("can't raise polynomial to a negative power") - if n == 1 or not f or f == [K.one]: - return f - - g = [K.one] - - while True: - n, m = n//2, n - - if m % 2: - g = dup_mul(g, f, K) - - if not n: - break - - f = dup_sqr(f, K) - - return g - - -@cythonized("u,n,m") -
    [docs]def dmp_pow(f, n, u, K): - """ - Raise ``f`` to the ``n``-th power in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dmp_pow - - >>> f = ZZ.map([[1, 0], [1]]) - - >>> dmp_pow(f, 3, 1, ZZ) - [[1, 0, 0, 0], [3, 0, 0], [3, 0], [1]] - - """ - if not u: - return dup_pow(f, n, K) - - if not n: - return dmp_one(u, K) - if n < 0: - raise ValueError("can't raise polynomial to a negative power") - if n == 1 or dmp_zero_p(f, u) or dmp_one_p(f, u, K): - return f - - g = dmp_one(u, K) - - while True: - n, m = n//2, n - - if m & 1: - g = dmp_mul(g, f, u, K) - - if not n: - break - - f = dmp_sqr(f, u, K) - - return g - -
    -@cythonized("df,dg,dr,N,j") -def dup_pdiv(f, g, K): - """ - Polynomial pseudo-division in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dup_pdiv - - >>> f = ZZ.map([1, 0, 1]) - >>> g = ZZ.map([2, -4]) - - >>> dup_pdiv(f, g, ZZ) - ([2, 4], [20]) - - """ - df = dup_degree(f) - dg = dup_degree(g) - - q, r = [], f - - if not g: - raise ZeroDivisionError("polynomial division") - elif df < dg: - return q, r - - N = df - dg + 1 - lc_g = dup_LC(g, K) - - while True: - dr = dup_degree(r) - - if dr < dg: - break - - lc_r = dup_LC(r, K) - j, N = dr - dg, N - 1 - - Q = dup_mul_ground(q, lc_g, K) - q = dup_add_term(Q, lc_r, j, K) - - R = dup_mul_ground(r, lc_g, K) - G = dup_mul_term(g, lc_r, j, K) - r = dup_sub(R, G, K) - - c = lc_g**N - - q = dup_mul_ground(q, c, K) - r = dup_mul_ground(r, c, K) - - return q, r - - -@cythonized("df,dg,dr,N,j") -def dup_prem(f, g, K): - """ - Polynomial pseudo-remainder in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dup_prem - - >>> f = ZZ.map([1, 0, 1]) - >>> g = ZZ.map([2, -4]) - - >>> dup_prem(f, g, ZZ) - [20] - - """ - df = dup_degree(f) - dg = dup_degree(g) - - r = f - - if not g: - raise ZeroDivisionError("polynomial division") - elif df < dg: - return r - - N = df - dg + 1 - lc_g = dup_LC(g, K) - - while True: - dr = dup_degree(r) - - if dr < dg: - break - - lc_r = dup_LC(r, K) - j, N = dr - dg, N - 1 - - R = dup_mul_ground(r, lc_g, K) - G = dup_mul_term(g, lc_r, j, K) - r = dup_sub(R, G, K) - - return dup_mul_ground(r, lc_g**N, K) - - -def dup_pquo(f, g, K): - """ - Polynomial exact pseudo-quotient in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dup_pquo - - >>> f = ZZ.map([1, 0, -1]) - >>> g = ZZ.map([2, -2]) - - >>> dup_pquo(f, g, ZZ) - [2, 2] - - >>> f = ZZ.map([1, 0, 1]) - >>> g = ZZ.map([2, -4]) - - >>> dup_pquo(f, g, ZZ) - [2, 4] - - """ - return dup_pdiv(f, g, K)[0] - - -def dup_pexquo(f, g, K): - """ - Polynomial pseudo-quotient in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dup_pexquo - - >>> f = ZZ.map([1, 0, -1]) - >>> g = ZZ.map([2, -2]) - - >>> dup_pexquo(f, g, ZZ) - [2, 2] - - >>> f = ZZ.map([1, 0, 1]) - >>> g = ZZ.map([2, -4]) - - >>> dup_pexquo(f, g, ZZ) - Traceback (most recent call last): - ... - ExactQuotientFailed: [2, -4] does not divide [1, 0, 1] - - """ - q, r = dup_pdiv(f, g, K) - - if not r: - return q - else: - raise ExactQuotientFailed(f, g) - - -@cythonized("u,df,dg,dr,N,j") -
    [docs]def dmp_pdiv(f, g, u, K): - """ - Polynomial pseudo-division in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dmp_pdiv - - >>> f = ZZ.map([[1], [1, 0], []]) - >>> g = ZZ.map([[2], [2]]) - - >>> dmp_pdiv(f, g, 1, ZZ) - ([[2], [2, -2]], [[-4, 4]]) - - """ - if not u: - return dup_pdiv(f, g, K) - - df = dmp_degree(f, u) - dg = dmp_degree(g, u) - - if dg < 0: - raise ZeroDivisionError("polynomial division") - - q, r = dmp_zero(u), f - - if df < dg: - return q, r - - N = df - dg + 1 - lc_g = dmp_LC(g, K) - - while True: - dr = dmp_degree(r, u) - - if dr < dg: - break - - lc_r = dmp_LC(r, K) - j, N = dr - dg, N - 1 - - Q = dmp_mul_term(q, lc_g, 0, u, K) - q = dmp_add_term(Q, lc_r, j, u, K) - - R = dmp_mul_term(r, lc_g, 0, u, K) - G = dmp_mul_term(g, lc_r, j, u, K) - r = dmp_sub(R, G, u, K) - - c = dmp_pow(lc_g, N, u - 1, K) - - q = dmp_mul_term(q, c, 0, u, K) - r = dmp_mul_term(r, c, 0, u, K) - - return q, r - -
    -@cythonized("u,df,dg,dr,N,j") -
    [docs]def dmp_prem(f, g, u, K): - """ - Polynomial pseudo-remainder in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dmp_prem - - >>> f = ZZ.map([[1], [1, 0], []]) - >>> g = ZZ.map([[2], [2]]) - - >>> dmp_prem(f, g, 1, ZZ) - [[-4, 4]] - - """ - if not u: - return dup_prem(f, g, K) - - df = dmp_degree(f, u) - dg = dmp_degree(g, u) - - if dg < 0: - raise ZeroDivisionError("polynomial division") - - r = f - - if df < dg: - return r - - N = df - dg + 1 - lc_g = dmp_LC(g, K) - - while True: - dr = dmp_degree(r, u) - - if dr < dg: - break - - lc_r = dmp_LC(r, K) - j, N = dr - dg, N - 1 - - R = dmp_mul_term(r, lc_g, 0, u, K) - G = dmp_mul_term(g, lc_r, j, u, K) - r = dmp_sub(R, G, u, K) - - c = dmp_pow(lc_g, N, u - 1, K) - - return dmp_mul_term(r, c, 0, u, K) - -
    -
    [docs]def dmp_pquo(f, g, u, K): - """ - Polynomial exact pseudo-quotient in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dmp_pquo - - >>> f = ZZ.map([[1], [1, 0], []]) - >>> g = ZZ.map([[2], [2, 0]]) - >>> h = ZZ.map([[2], [2]]) - - >>> dmp_pquo(f, g, 1, ZZ) - [[2], []] - - >>> dmp_pquo(f, h, 1, ZZ) - [[2], [2, -2]] - - """ - return dmp_pdiv(f, g, u, K)[0] - -
    -
    [docs]def dmp_pexquo(f, g, u, K): - """ - Polynomial pseudo-quotient in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dmp_pexquo - - >>> f = ZZ.map([[1], [1, 0], []]) - >>> g = ZZ.map([[2], [2, 0]]) - >>> h = ZZ.map([[2], [2]]) - - >>> dmp_pexquo(f, g, 1, ZZ) - [[2], []] - - >>> dmp_pexquo(f, h, 1, ZZ) - Traceback (most recent call last): - ... - ExactQuotientFailed: [[2], [2]] does not divide [[1], [1, 0], []] - - """ - q, r = dmp_pdiv(f, g, u, K) - - if dmp_zero_p(r, u): - return q - else: - raise ExactQuotientFailed(f, g) - -
    -@cythonized("df,dg,dr,j") -def dup_rr_div(f, g, K): - """ - Univariate division with remainder over a ring. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dup_rr_div - - >>> f = ZZ.map([1, 0, 1]) - >>> g = ZZ.map([2, -4]) - - >>> dup_rr_div(f, g, ZZ) - ([], [1, 0, 1]) - - """ - df = dup_degree(f) - dg = dup_degree(g) - - q, r = [], f - - if not g: - raise ZeroDivisionError("polynomial division") - elif df < dg: - return q, r - - lc_g = dup_LC(g, K) - - while True: - dr = dup_degree(r) - - if dr < dg: - break - - lc_r = dup_LC(r, K) - - if lc_r % lc_g: - break - - c = K.exquo(lc_r, lc_g) - j = dr - dg - - q = dup_add_term(q, c, j, K) - h = dup_mul_term(g, c, j, K) - - r = dup_sub(r, h, K) - - return q, r - - -@cythonized("u,df,dg,dr,j") -
    [docs]def dmp_rr_div(f, g, u, K): - """ - Multivariate division with remainder over a ring. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dmp_rr_div - - >>> f = ZZ.map([[1], [1, 0], []]) - >>> g = ZZ.map([[2], [2]]) - - >>> dmp_rr_div(f, g, 1, ZZ) - ([[]], [[1], [1, 0], []]) - - """ - if not u: - return dup_rr_div(f, g, K) - - df = dmp_degree(f, u) - dg = dmp_degree(g, u) - - if dg < 0: - raise ZeroDivisionError("polynomial division") - - q, r = dmp_zero(u), f - - if df < dg: - return q, r - - lc_g, v = dmp_LC(g, K), u - 1 - - while True: - dr = dmp_degree(r, u) - - if dr < dg: - break - - lc_r = dmp_LC(r, K) - - c, R = dmp_rr_div(lc_r, lc_g, v, K) - - if not dmp_zero_p(R, v): - break - - j = dr - dg - - q = dmp_add_term(q, c, j, u, K) - h = dmp_mul_term(g, c, j, u, K) - - r = dmp_sub(r, h, u, K) - - return q, r - -
    -@cythonized("df,dg,dr,j") -def dup_ff_div(f, g, K): - """ - Polynomial division with remainder over a field. - - Examples - ======== - - >>> from sympy.polys.domains import QQ - >>> from sympy.polys.densearith import dup_ff_div - - >>> f = QQ.map([1, 0, 1]) - >>> g = QQ.map([2, -4]) - - >>> dup_ff_div(f, g, QQ) - ([1/2, 1/1], [5/1]) - - """ - df = dup_degree(f) - dg = dup_degree(g) - - q, r = [], f - - if not g: - raise ZeroDivisionError("polynomial division") - elif df < dg: - return q, r - - lc_g = dup_LC(g, K) - - while True: - dr = dup_degree(r) - - if dr < dg: - break - - lc_r = dup_LC(r, K) - - c = K.exquo(lc_r, lc_g) - j = dr - dg - - q = dup_add_term(q, c, j, K) - h = dup_mul_term(g, c, j, K) - - r = dup_sub(r, h, K) - - if not K.is_Exact: - r = dup_normal(r, K) - - return q, r - - -@cythonized("u,df,dg,dr,j") -
    [docs]def dmp_ff_div(f, g, u, K): - """ - Polynomial division with remainder over a field. - - Examples - ======== - - >>> from sympy.polys.domains import QQ - >>> from sympy.polys.densearith import dmp_ff_div - - >>> f = QQ.map([[1], [1, 0], []]) - >>> g = QQ.map([[2], [2]]) - - >>> dmp_ff_div(f, g, 1, QQ) - ([[1/2], [1/2, -1/2]], [[-1/1, 1/1]]) - - """ - if not u: - return dup_ff_div(f, g, K) - - df = dmp_degree(f, u) - dg = dmp_degree(g, u) - - if dg < 0: - raise ZeroDivisionError("polynomial division") - - q, r = dmp_zero(u), f - - if df < dg: - return q, r - - lc_g, v = dmp_LC(g, K), u - 1 - - while True: - dr = dmp_degree(r, u) - - if dr < dg: - break - - lc_r = dmp_LC(r, K) - - c, R = dmp_ff_div(lc_r, lc_g, v, K) - - if not dmp_zero_p(R, v): - break - - j = dr - dg - - q = dmp_add_term(q, c, j, u, K) - h = dmp_mul_term(g, c, j, u, K) - - r = dmp_sub(r, h, u, K) - - return q, r - -
    -def dup_div(f, g, K): - """ - Polynomial division with remainder in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ, QQ - >>> from sympy.polys.densearith import dup_div - - >>> f = ZZ.map([1, 0, 1]) - >>> g = ZZ.map([2, -4]) - - >>> dup_div(f, g, ZZ) - ([], [1, 0, 1]) - - >>> f = QQ.map([1, 0, 1]) - >>> g = QQ.map([2, -4]) - - >>> dup_div(f, g, QQ) - ([1/2, 1/1], [5/1]) - - """ - if K.has_Field or not K.is_Exact: - return dup_ff_div(f, g, K) - else: - return dup_rr_div(f, g, K) - - -def dup_rem(f, g, K): - """ - Returns polynomial remainder in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ, QQ - >>> from sympy.polys.densearith import dup_rem - - >>> f = ZZ.map([1, 0, 1]) - >>> g = ZZ.map([2, -4]) - - >>> dup_rem(f, g, ZZ) - [1, 0, 1] - - >>> f = QQ.map([1, 0, 1]) - >>> g = QQ.map([2, -4]) - - >>> dup_rem(f, g, QQ) - [5/1] - - """ - return dup_div(f, g, K)[1] - - -def dup_quo(f, g, K): - """ - Returns exact polynomial quotient in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ, QQ - >>> from sympy.polys.densearith import dup_quo - - >>> f = ZZ.map([1, 0, 1]) - >>> g = ZZ.map([2, -4]) - - >>> dup_quo(f, g, ZZ) - [] - - >>> f = QQ.map([1, 0, 1]) - >>> g = QQ.map([2, -4]) - - >>> dup_quo(f, g, QQ) - [1/2, 1/1] - - """ - return dup_div(f, g, K)[0] - - -def dup_exquo(f, g, K): - """ - Returns polynomial quotient in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dup_exquo - - >>> f = ZZ.map([1, 0, -1]) - >>> g = ZZ.map([1, -1]) - - >>> dup_exquo(f, g, ZZ) - [1, 1] - - >>> f = ZZ.map([1, 0, 1]) - >>> g = ZZ.map([2, -4]) - - >>> dup_exquo(f, g, ZZ) - Traceback (most recent call last): - ... - ExactQuotientFailed: [2, -4] does not divide [1, 0, 1] - - """ - q, r = dup_div(f, g, K) - - if not r: - return q - else: - raise ExactQuotientFailed(f, g) - - -@cythonized("u") -
    [docs]def dmp_div(f, g, u, K): - """ - Polynomial division with remainder in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ, QQ - >>> from sympy.polys.densearith import dmp_div - - >>> f = ZZ.map([[1], [1, 0], []]) - >>> g = ZZ.map([[2], [2]]) - - >>> dmp_div(f, g, 1, ZZ) - ([[]], [[1], [1, 0], []]) - - >>> f = QQ.map([[1], [1, 0], []]) - >>> g = QQ.map([[2], [2]]) - - >>> dmp_div(f, g, 1, QQ) - ([[1/2], [1/2, -1/2]], [[-1/1, 1/1]]) - - """ - if K.has_Field or not K.is_Exact: - return dmp_ff_div(f, g, u, K) - else: - return dmp_rr_div(f, g, u, K) - -
    -@cythonized("u") -
    [docs]def dmp_rem(f, g, u, K): - """ - Returns polynomial remainder in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ, QQ - >>> from sympy.polys.densearith import dmp_rem - - >>> f = ZZ.map([[1], [1, 0], []]) - >>> g = ZZ.map([[2], [2]]) - - >>> dmp_rem(f, g, 1, ZZ) - [[1], [1, 0], []] - - >>> f = QQ.map([[1], [1, 0], []]) - >>> g = QQ.map([[2], [2]]) - - >>> dmp_rem(f, g, 1, QQ) - [[-1/1, 1/1]] - - """ - return dmp_div(f, g, u, K)[1] - -
    -@cythonized("u") -
    [docs]def dmp_quo(f, g, u, K): - """ - Returns exact polynomial quotient in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ, QQ - >>> from sympy.polys.densearith import dmp_quo - - >>> f = ZZ.map([[1], [1, 0], []]) - >>> g = ZZ.map([[2], [2]]) - - >>> dmp_quo(f, g, 1, ZZ) - [[]] - - >>> f = QQ.map([[1], [1, 0], []]) - >>> g = QQ.map([[2], [2]]) - - >>> dmp_quo(f, g, 1, QQ) - [[1/2], [1/2, -1/2]] - - """ - return dmp_div(f, g, u, K)[0] - -
    -@cythonized("u") -
    [docs]def dmp_exquo(f, g, u, K): - """ - Returns polynomial quotient in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dmp_exquo - - >>> f = ZZ.map([[1], [1, 0], []]) - >>> g = ZZ.map([[1], [1, 0]]) - >>> h = ZZ.map([[2], [2]]) - - >>> dmp_exquo(f, g, 1, ZZ) - [[1], []] - - >>> dmp_exquo(f, h, 1, ZZ) - Traceback (most recent call last): - ... - ExactQuotientFailed: [[2], [2]] does not divide [[1], [1, 0], []] - - """ - q, r = dmp_div(f, g, u, K) - - if dmp_zero_p(r, u): - return q - else: - raise ExactQuotientFailed(f, g) - -
    -def dup_max_norm(f, K): - """ - Returns maximum norm of a polynomial in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dup_max_norm - - >>> f = ZZ.map([-1, 2, -3]) - - >>> dup_max_norm(f, ZZ) - 3 - - """ - if not f: - return K.zero - else: - return max(dup_abs(f, K)) - - -@cythonized("u,v") -
    [docs]def dmp_max_norm(f, u, K): - """ - Returns maximum norm of a polynomial in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dmp_max_norm - - >>> f = ZZ.map([[2, -1], [-3]]) - - >>> dmp_max_norm(f, 1, ZZ) - 3 - - """ - if not u: - return dup_max_norm(f, K) - - v = u - 1 - - return max([ dmp_max_norm(c, v, K) for c in f ]) - -
    -def dup_l1_norm(f, K): - """ - Returns l1 norm of a polynomial in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dup_l1_norm - - >>> f = ZZ.map([2, -3, 0, 1]) - - >>> dup_l1_norm(f, ZZ) - 6 - - """ - if not f: - return K.zero - else: - return sum(dup_abs(f, K)) - - -@cythonized("u,v") -
    [docs]def dmp_l1_norm(f, u, K): - """ - Returns l1 norm of a polynomial in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dmp_l1_norm - - >>> f = ZZ.map([[2, -1], [-3]]) - - >>> dmp_l1_norm(f, 1, ZZ) - 6 - - """ - if not u: - return dup_l1_norm(f, K) - - v = u - 1 - - return sum([ dmp_l1_norm(c, v, K) for c in f ]) - -
    -def dup_expand(polys, K): - """ - Multiply together several polynomials in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dup_expand - - >>> f = ZZ.map([1, 0, -1]) - >>> g = ZZ.map([1, 0]) - >>> h = ZZ.map([2]) - - >>> dup_expand([f, g, h], ZZ) - [2, 0, -2, 0] - - """ - if not polys: - return [K.one] - - f = polys[0] - - for g in polys[1:]: - f = dup_mul(f, g, K) - - return f - - -@cythonized("u") -
    [docs]def dmp_expand(polys, u, K): - """ - Multiply together several polynomials in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densearith import dmp_expand - - >>> f = ZZ.map([[1], [], [1, 0, 0]]) - >>> g = ZZ.map([[1], [1]]) - - >>> dmp_expand([f, g], 1, ZZ) - [[1], [1], [1, 0, 0], [1, 0, 0]] - - """ - if not polys: - return dmp_one(u, K) - - f = polys[0] - - for g in polys[1:]: - f = dmp_mul(f, g, u, K) - - return f
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/polys/densebasic.html b/dev-py3k/_modules/sympy/polys/densebasic.html deleted file mode 100644 index 445a1e0bd9f..00000000000 --- a/dev-py3k/_modules/sympy/polys/densebasic.html +++ /dev/null @@ -1,2050 +0,0 @@ - - - - - - - - - - sympy.polys.densebasic — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.polys.densebasic

    -"""Basic tools for dense recursive polynomials in ``K[x]`` or ``K[X]``. """
    -
    -from sympy.core import igcd
    -
    -from sympy.polys.monomialtools import (
    -    monomial_key, monomial_min, monomial_div
    -)
    -
    -from sympy.polys.distributedpolys import sdp_sort
    -
    -from sympy.utilities import cythonized
    -
    -import random
    -
    -
    -def poly_LC(f, K):
    -    """
    -    Return leading coefficient of ``f``.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.polys.domains import ZZ
    -    >>> from sympy.polys.densebasic import poly_LC
    -
    -    >>> poly_LC([], ZZ)
    -    0
    -    >>> poly_LC([ZZ(1), ZZ(2), ZZ(3)], ZZ)
    -    1
    -
    -    """
    -    if not f:
    -        return K.zero
    -    else:
    -        return f[0]
    -
    -
    -def poly_TC(f, K):
    -    """
    -    Return trailing coefficient of ``f``.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.polys.domains import ZZ
    -    >>> from sympy.polys.densebasic import poly_TC
    -
    -    >>> poly_TC([], ZZ)
    -    0
    -    >>> poly_TC([ZZ(1), ZZ(2), ZZ(3)], ZZ)
    -    3
    -
    -    """
    -    if not f:
    -        return K.zero
    -    else:
    -        return f[-1]
    -
    -dup_LC = dmp_LC = poly_LC
    -dup_TC = dmp_TC = poly_TC
    -
    -
    -@cythonized("u")
    -
    [docs]def dmp_ground_LC(f, u, K): - """ - Return the ground leading coefficient. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_ground_LC - - >>> f = ZZ.map([[[1], [2, 3]]]) - - >>> dmp_ground_LC(f, 2, ZZ) - 1 - - """ - while u: - f = dmp_LC(f, K) - u -= 1 - - return dup_LC(f, K) - -
    -@cythonized("u") -
    [docs]def dmp_ground_TC(f, u, K): - """ - Return the ground trailing coefficient. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_ground_TC - - >>> f = ZZ.map([[[1], [2, 3]]]) - - >>> dmp_ground_TC(f, 2, ZZ) - 3 - - """ - while u: - f = dmp_TC(f, K) - u -= 1 - - return dup_TC(f, K) - -
    -@cythonized("u") -
    [docs]def dmp_true_LT(f, u, K): - """ - Return the leading term ``c * x_1**n_1 ... x_k**n_k``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_true_LT - - >>> f = ZZ.map([[4], [2, 0], [3, 0, 0]]) - - >>> dmp_true_LT(f, 1, ZZ) - ((2, 0), 4) - - """ - monom = [] - - while u: - monom.append(len(f) - 1) - f, u = f[0], u - 1 - - if not f: - monom.append(0) - else: - monom.append(len(f) - 1) - - return tuple(monom), dup_LC(f, K) - -
    -def dup_degree(f): - """ - Return the leading degree of ``f`` in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dup_degree - - >>> f = ZZ.map([1, 2, 0, 3]) - - >>> dup_degree(f) - 3 - - """ - return len(f) - 1 - - -@cythonized("u") -
    [docs]def dmp_degree(f, u): - """ - Return the leading degree of ``f`` in ``x_0`` in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_degree - - >>> dmp_degree([[[]]], 2) - -1 - - >>> f = ZZ.map([[2], [1, 2, 3]]) - - >>> dmp_degree(f, 1) - 1 - - """ - if dmp_zero_p(f, u): - return -1 - else: - return len(f) - 1 - -
    -@cythonized("v,i,j") -def _rec_degree_in(g, v, i, j): - """Recursive helper function for :func:`dmp_degree_in`.""" - if i == j: - return dmp_degree(g, v) - - v, i = v - 1, i + 1 - - return max([ _rec_degree_in(c, v, i, j) for c in g ]) - - -@cythonized("j,u") -
    [docs]def dmp_degree_in(f, j, u): - """ - Return the leading degree of ``f`` in ``x_j`` in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_degree_in - - >>> f = ZZ.map([[2], [1, 2, 3]]) - - >>> dmp_degree_in(f, 0, 1) - 1 - >>> dmp_degree_in(f, 1, 1) - 2 - - """ - if not j: - return dmp_degree(f, u) - if j < 0 or j > u: - raise IndexError("0 <= j <= %s expected, got %s" % (u, j)) - - return _rec_degree_in(f, u, 0, j) - -
    -@cythonized("v,i") -def _rec_degree_list(g, v, i, degs): - """Recursive helper for :func:`dmp_degree_list`.""" - degs[i] = max(degs[i], dmp_degree(g, v)) - - if v > 0: - v, i = v - 1, i + 1 - - for c in g: - _rec_degree_list(c, v, i, degs) - - -@cythonized("u") -
    [docs]def dmp_degree_list(f, u): - """ - Return a list of degrees of ``f`` in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_degree_list - - >>> f = ZZ.map([[1], [1, 2, 3]]) - - >>> dmp_degree_list(f, 1) - (1, 2) - - """ - degs = [-1]*(u + 1) - _rec_degree_list(f, u, 0, degs) - return tuple(degs) - -
    -@cythonized("i") -def dup_strip(f): - """ - Remove leading zeros from ``f`` in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.densebasic import dup_strip - - >>> dup_strip([0, 0, 1, 2, 3, 0]) - [1, 2, 3, 0] - - """ - if not f or f[0]: - return f - - i = 0 - - for cf in f: - if cf: - break - else: - i += 1 - - return f[i:] - - -@cythonized("u,v,i") -
    [docs]def dmp_strip(f, u): - """ - Remove leading zeros from ``f`` in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.densebasic import dmp_strip - - >>> dmp_strip([[], [0, 1, 2], [1]], 1) - [[0, 1, 2], [1]] - - """ - if not u: - return dup_strip(f) - - if dmp_zero_p(f, u): - return f - - i, v = 0, u - 1 - - for c in f: - if not dmp_zero_p(c, v): - break - else: - i += 1 - - if i == len(f): - return dmp_zero(u) - else: - return f[i:] - -
    -@cythonized("i,j") -def _rec_validate(f, g, i, K): - """Recursive helper for :func:`dmp_validate`.""" - if type(g) is not list: - if K is not None and not K.of_type(g): - raise TypeError("%s in %s in not of type %s" % (g, f, K.dtype)) - - return set([i - 1]) - elif not g: - return set([i]) - else: - j, levels = i + 1, set([]) - - for c in g: - levels |= _rec_validate(f, c, i + 1, K) - - return levels - - -@cythonized("v,w") -def _rec_strip(g, v): - """Recursive helper for :func:`_rec_strip`.""" - if not v: - return dup_strip(g) - - w = v - 1 - - return dmp_strip([ _rec_strip(c, w) for c in g ], v) - - -@cythonized("u") -
    [docs]def dmp_validate(f, K=None): - """ - Return the number of levels in ``f`` and recursively strip it. - - Examples - ======== - - >>> from sympy.polys.densebasic import dmp_validate - - >>> dmp_validate([[], [0, 1, 2], [1]]) - ([[1, 2], [1]], 1) - - >>> dmp_validate([[1], 1]) - Traceback (most recent call last): - ... - ValueError: invalid data structure for a multivariate polynomial - - """ - levels = _rec_validate(f, f, 0, K) - - u = levels.pop() - - if not levels: - return _rec_strip(f, u), u - else: - raise ValueError( - "invalid data structure for a multivariate polynomial") - -
    -
    [docs]def dup_reverse(f): - """ - Compute ``x**n * f(1/x)``, i.e.: reverse ``f`` in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dup_reverse - - >>> f = ZZ.map([1, 2, 3, 0]) - - >>> dup_reverse(f) - [3, 2, 1] - - """ - return dup_strip(list(reversed(f))) - -
    -def dup_copy(f): - """ - Create a new copy of a polynomial ``f`` in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dup_copy - - >>> f = ZZ.map([1, 2, 3, 0]) - - >>> dup_copy([1, 2, 3, 0]) - [1, 2, 3, 0] - - """ - return list(f) - - -@cythonized("u,v") -
    [docs]def dmp_copy(f, u): - """ - Create a new copy of a polynomial ``f`` in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_copy - - >>> f = ZZ.map([[1], [1, 2]]) - - >>> dmp_copy(f, 1) - [[1], [1, 2]] - - """ - if not u: - return list(f) - - v = u - 1 - - return [ dmp_copy(c, v) for c in f ] - -
    -def dup_to_tuple(f): - """ - Convert `f` into a tuple. - - This is needed for hashing. This is similar to dup_copy(). - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dup_copy - - >>> f = ZZ.map([1, 2, 3, 0]) - - >>> dup_copy([1, 2, 3, 0]) - [1, 2, 3, 0] - - """ - return tuple(f) - - -@cythonized("u,v") -
    [docs]def dmp_to_tuple(f, u): - """ - Convert `f` into a nested tuple of tuples. - - This is needed for hashing. This is similar to dmp_copy(). - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_to_tuple - - >>> f = ZZ.map([[1], [1, 2]]) - - >>> dmp_to_tuple(f, 1) - ((1,), (1, 2)) - - """ - if not u: - return tuple(f) - v = u - 1 - - return tuple(dmp_to_tuple(c, v) for c in f) - -
    -def dup_normal(f, K): - """ - Normalize univariate polynomial in the given domain. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dup_normal - - >>> dup_normal([0, 1.5, 2, 3], ZZ) - [1, 2, 3] - - """ - return dup_strip([ K.normal(c) for c in f ]) - - -@cythonized("u,v") -
    [docs]def dmp_normal(f, u, K): - """ - Normalize a multivariate polynomial in the given domain. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_normal - - >>> dmp_normal([[], [0, 1.5, 2]], 1, ZZ) - [[1, 2]] - - """ - if not u: - return dup_normal(f, K) - - v = u - 1 - - return dmp_strip([ dmp_normal(c, v, K) for c in f ], u) - -
    -def dup_convert(f, K0, K1): - """ - Convert the ground domain of ``f`` from ``K0`` to ``K1``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.polyclasses import DMP - >>> from sympy.polys.densebasic import dup_convert - - >>> dup_convert([DMP([1], ZZ), DMP([2], ZZ)], ZZ['x'], ZZ) - [1, 2] - - >>> dup_convert([ZZ(1), ZZ(2)], ZZ, ZZ['x']) - [1, 2] - - """ - if K0 is not None and K0 == K1: - return f - else: - return dup_strip([ K1.convert(c, K0) for c in f ]) - - -@cythonized("u,v") -
    [docs]def dmp_convert(f, u, K0, K1): - """ - Convert the ground domain of ``f`` from ``K0`` to ``K1``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.polyclasses import DMP - >>> from sympy.polys.densebasic import dmp_convert - - >>> f = [[DMP([1], ZZ)], [DMP([2], ZZ)]] - >>> g = [[ZZ(1)], [ZZ(2)]] - - >>> dmp_convert(f, 1, ZZ['x'], ZZ) - [[1], [2]] - - >>> dmp_convert(g, 1, ZZ, ZZ['x']) - [[1], [2]] - - """ - if not u: - return dup_convert(f, K0, K1) - if K0 is not None and K0 == K1: - return f - - v = u - 1 - - return dmp_strip([ dmp_convert(c, v, K0, K1) for c in f ], u) - -
    -def dup_from_sympy(f, K): - """ - Convert the ground domain of ``f`` from SymPy to ``K``. - - Examples - ======== - - >>> from sympy import S - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dup_from_sympy - - >>> dup_from_sympy([S(1), S(2)], ZZ) == [ZZ(1), ZZ(2)] - True - - """ - return dup_strip([ K.from_sympy(c) for c in f ]) - - -@cythonized("u,v") -
    [docs]def dmp_from_sympy(f, u, K): - """ - Convert the ground domain of ``f`` from SymPy to ``K``. - - Examples - ======== - - >>> from sympy import S - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_from_sympy - - >>> dmp_from_sympy([[S(1)], [S(2)]], 1, ZZ) == [[ZZ(1)], [ZZ(2)]] - True - - """ - if not u: - return dup_from_sympy(f, K) - - v = u - 1 - - return dmp_strip([ dmp_from_sympy(c, v, K) for c in f ], u) - -
    -@cythonized("n") -def dup_nth(f, n, K): - """ - Return the ``n``-th coefficient of ``f`` in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dup_nth - - >>> f = ZZ.map([1, 2, 3]) - - >>> dup_nth(f, 0, ZZ) - 3 - >>> dup_nth(f, 4, ZZ) - 0 - - """ - if n < 0: - raise IndexError("'n' must be non-negative, got %i" % n) - elif n >= len(f): - return K.zero - else: - return f[dup_degree(f) - n] - - -@cythonized("n,u") -
    [docs]def dmp_nth(f, n, u, K): - """ - Return the ``n``-th coefficient of ``f`` in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_nth - - >>> f = ZZ.map([[1], [2], [3]]) - - >>> dmp_nth(f, 0, 1, ZZ) - [3] - >>> dmp_nth(f, 4, 1, ZZ) - [] - - """ - if n < 0: - raise IndexError("'n' must be non-negative, got %i" % n) - elif n >= len(f): - return dmp_zero(u - 1) - else: - return f[dmp_degree(f, u) - n] - -
    -@cythonized("n,u,v") -
    [docs]def dmp_ground_nth(f, N, u, K): - """ - Return the ground ``n``-th coefficient of ``f`` in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_ground_nth - - >>> f = ZZ.map([[1], [2, 3]]) - - >>> dmp_ground_nth(f, (0, 1), 1, ZZ) - 2 - - """ - v = u - - for n in N: - if n < 0: - raise IndexError("`n` must be non-negative, got %i" % n) - elif n >= len(f): - return K.zero - else: - f, v = f[dmp_degree(f, v) - n], v - 1 - - return f - -
    -@cythonized("u") -
    [docs]def dmp_zero_p(f, u): - """ - Return ``True`` if ``f`` is zero in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.densebasic import dmp_zero_p - - >>> dmp_zero_p([[[[[]]]]], 4) - True - >>> dmp_zero_p([[[[[1]]]]], 4) - False - - """ - while u: - if len(f) != 1: - return False - - f = f[0] - u -= 1 - - return not f - -
    -@cythonized("u") -
    [docs]def dmp_zero(u): - """ - Return a multivariate zero. - - Examples - ======== - - >>> from sympy.polys.densebasic import dmp_zero - - >>> dmp_zero(4) - [[[[[]]]]] - - """ - r = [] - - for i in range(u): - r = [r] - - return r - -
    -@cythonized("u") -
    [docs]def dmp_one_p(f, u, K): - """ - Return ``True`` if ``f`` is one in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_one_p - - >>> dmp_one_p([[[ZZ(1)]]], 2, ZZ) - True - - """ - return dmp_ground_p(f, K.one, u) - -
    -@cythonized("u") -
    [docs]def dmp_one(u, K): - """ - Return a multivariate one over ``K``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_one - - >>> dmp_one(2, ZZ) - [[[1]]] - - """ - return dmp_ground(K.one, u) - -
    -@cythonized("u") -
    [docs]def dmp_ground_p(f, c, u): - """ - Return True if ``f`` is constant in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.densebasic import dmp_ground_p - - >>> dmp_ground_p([[[3]]], 3, 2) - True - >>> dmp_ground_p([[[4]]], None, 2) - True - - """ - if c is not None and not c: - return dmp_zero_p(f, u) - - while u: - if len(f) != 1: - return False - f = f[0] - u -= 1 - - if c is None: - return len(f) <= 1 - else: - return f == [c] - -
    -@cythonized("i,u") -
    [docs]def dmp_ground(c, u): - """ - Return a multivariate constant. - - Examples - ======== - - >>> from sympy.polys.densebasic import dmp_ground - - >>> dmp_ground(3, 5) - [[[[[[3]]]]]] - >>> dmp_ground(1, -1) - 1 - - """ - if not c: - return dmp_zero(u) - - for i in range(u + 1): - c = [c] - - return c - -
    -@cythonized("n,u") -
    [docs]def dmp_zeros(n, u, K): - """ - Return a list of multivariate zeros. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_zeros - - >>> dmp_zeros(3, 2, ZZ) - [[[[]]], [[[]]], [[[]]]] - >>> dmp_zeros(3, -1, ZZ) - [0, 0, 0] - - """ - if not n: - return [] - - if u < 0: - return [K.zero]*n - else: - return [ dmp_zero(u) for i in range(n) ] - -
    -@cythonized("n,u") -
    [docs]def dmp_grounds(c, n, u): - """ - Return a list of multivariate constants. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_grounds - - >>> dmp_grounds(ZZ(4), 3, 2) - [[[[4]]], [[[4]]], [[[4]]]] - >>> dmp_grounds(ZZ(4), 3, -1) - [4, 4, 4] - - """ - if not n: - return [] - - if u < 0: - return [c]*n - else: - return [ dmp_ground(c, u) for i in range(n) ] - -
    -@cythonized("u") -
    [docs]def dmp_negative_p(f, u, K): - """ - Return ``True`` if ``LC(f)`` is negative. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_negative_p - - >>> dmp_negative_p([[ZZ(1)], [-ZZ(1)]], 1, ZZ) - False - >>> dmp_negative_p([[-ZZ(1)], [ZZ(1)]], 1, ZZ) - True - - """ - return K.is_negative(dmp_ground_LC(f, u, K)) - -
    -@cythonized("u") -
    [docs]def dmp_positive_p(f, u, K): - """ - Return ``True`` if ``LC(f)`` is positive. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_positive_p - - >>> dmp_positive_p([[ZZ(1)], [-ZZ(1)]], 1, ZZ) - True - >>> dmp_positive_p([[-ZZ(1)], [ZZ(1)]], 1, ZZ) - False - - """ - return K.is_positive(dmp_ground_LC(f, u, K)) - -
    -@cythonized("n,k") -def dup_from_dict(f, K): - """ - Create a ``K[x]`` polynomial from a ``dict``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dup_from_dict - - >>> dup_from_dict({(0,): ZZ(7), (2,): ZZ(5), (4,): ZZ(1)}, ZZ) - [1, 0, 5, 0, 7] - >>> dup_from_dict({}, ZZ) - [] - - """ - if not f: - return [] - - n, h = max(f.keys()), [] - - if type(n) is int: - for k in range(n, -1, -1): - h.append(f.get(k, K.zero)) - else: - (n,) = n - - for k in range(n, -1, -1): - h.append(f.get((k,), K.zero)) - - return dup_strip(h) - - -@cythonized("n,k") -def dup_from_raw_dict(f, K): - """ - Create a ``K[x]`` polynomial from a raw ``dict``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dup_from_raw_dict - - >>> dup_from_raw_dict({0: ZZ(7), 2: ZZ(5), 4: ZZ(1)}, ZZ) - [1, 0, 5, 0, 7] - - """ - if not f: - return [] - - n, h = max(f.keys()), [] - - for k in range(n, -1, -1): - h.append(f.get(k, K.zero)) - - return dup_strip(h) - - -@cythonized("u,v,n,k") -
    [docs]def dmp_from_dict(f, u, K): - """ - Create a ``K[X]`` polynomial from a ``dict``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_from_dict - - >>> dmp_from_dict({(0, 0): ZZ(3), (0, 1): ZZ(2), (2, 1): ZZ(1)}, 1, ZZ) - [[1, 0], [], [2, 3]] - >>> dmp_from_dict({}, 0, ZZ) - [] - - """ - if not u: - return dup_from_dict(f, K) - if not f: - return dmp_zero(u) - - coeffs = {} - - for monom, coeff in f.items(): - head, tail = monom[0], monom[1:] - - if head in coeffs: - coeffs[head][tail] = coeff - else: - coeffs[head] = { tail: coeff } - - n, v, h = max(coeffs.keys()), u - 1, [] - - for k in range(n, -1, -1): - coeff = coeffs.get(k) - - if coeff is not None: - h.append(dmp_from_dict(coeff, v, K)) - else: - h.append(dmp_zero(v)) - - return dmp_strip(h, u) - -
    -@cythonized("n,k") -def dup_to_dict(f, K=None, zero=False): - """ - Convert ``K[x]`` polynomial to a ``dict``. - - Examples - ======== - - >>> from sympy.polys.densebasic import dup_to_dict - - >>> dup_to_dict([1, 0, 5, 0, 7]) - {(0,): 7, (2,): 5, (4,): 1} - >>> dup_to_dict([]) - {} - - """ - if not f and zero: - return {(0,): K.zero} - - n, result = dup_degree(f), {} - - for k in range(0, n + 1): - if f[n - k]: - result[(k,)] = f[n - k] - - return result - - -@cythonized("n,k") -def dup_to_raw_dict(f, K=None, zero=False): - """ - Convert a ``K[x]`` polynomial to a raw ``dict``. - - Examples - ======== - - >>> from sympy.polys.densebasic import dup_to_raw_dict - - >>> dup_to_raw_dict([1, 0, 5, 0, 7]) - {0: 7, 2: 5, 4: 1} - - """ - if not f and zero: - return {0: K.zero} - - n, result = dup_degree(f), {} - - for k in range(0, n + 1): - if f[n - k]: - result[k] = f[n - k] - - return result - - -@cythonized("u,v,n,k") -
    [docs]def dmp_to_dict(f, u, K=None, zero=False): - """ - Convert a ``K[X]`` polynomial to a ``dict````. - - Examples - ======== - - >>> from sympy.polys.densebasic import dmp_to_dict - - >>> dmp_to_dict([[1, 0], [], [2, 3]], 1) - {(0, 0): 3, (0, 1): 2, (2, 1): 1} - >>> dmp_to_dict([], 0) - {} - - """ - if not u: - return dup_to_dict(f, K, zero=zero) - - if dmp_zero_p(f, u) and zero: - return {(0,)*(u + 1): K.zero} - - n, v, result = dmp_degree(f, u), u - 1, {} - - for k in range(0, n + 1): - h = dmp_to_dict(f[n - k], v) - - for exp, coeff in h.items(): - result[(k,) + exp] = coeff - - return result - -
    -@cythonized("u,i,j") -
    [docs]def dmp_swap(f, i, j, u, K): - """ - Transform ``K[..x_i..x_j..]`` to ``K[..x_j..x_i..]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_swap - - >>> f = ZZ.map([[[2], [1, 0]], []]) - - >>> dmp_swap(f, 0, 1, 2, ZZ) - [[[2], []], [[1, 0], []]] - >>> dmp_swap(f, 1, 2, 2, ZZ) - [[[1], [2, 0]], [[]]] - >>> dmp_swap(f, 0, 2, 2, ZZ) - [[[1, 0]], [[2, 0], []]] - - """ - if i < 0 or j < 0 or i > u or j > u: - raise IndexError("0 <= i < j <= %s expected" % u) - elif i == j: - return f - - F, H = dmp_to_dict(f, u), {} - - for exp, coeff in F.items(): - H[exp[:i] + (exp[j],) + - exp[i + 1:j] + - (exp[i],) + exp[j + 1:]] = coeff - - return dmp_from_dict(H, u, K) - -
    -@cythonized("u") -
    [docs]def dmp_permute(f, P, u, K): - """ - Return a polynomial in ``K[x_{P(1)},..,x_{P(n)}]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_permute - - >>> f = ZZ.map([[[2], [1, 0]], []]) - - >>> dmp_permute(f, [1, 0, 2], 2, ZZ) - [[[2], []], [[1, 0], []]] - >>> dmp_permute(f, [1, 2, 0], 2, ZZ) - [[[1], []], [[2, 0], []]] - - """ - F, H = dmp_to_dict(f, u), {} - - for exp, coeff in F.items(): - new_exp = [0]*len(exp) - - for e, p in zip(exp, P): - new_exp[p] = e - - H[tuple(new_exp)] = coeff - - return dmp_from_dict(H, u, K) - -
    -@cythonized("i,l") -
    [docs]def dmp_nest(f, l, K): - """ - Return a multivariate value nested ``l``-levels. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_nest - - >>> dmp_nest([[ZZ(1)]], 2, ZZ) - [[[[1]]]] - - """ - if not isinstance(f, list): - return dmp_ground(f, l) - - for i in range(l): - f = [f] - - return f - -
    -@cythonized("l,k,u,v") -
    [docs]def dmp_raise(f, l, u, K): - """ - Return a multivariate polynomial raised ``l``-levels. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_raise - - >>> f = ZZ.map([[], [1, 2]]) - - >>> dmp_raise(f, 2, 1, ZZ) - [[[[]]], [[[1]], [[2]]]] - - """ - if not l: - return f - - if not u: - if not f: - return dmp_zero(l) - - k = l - 1 - - return [ dmp_ground(c, k) for c in f ] - - v = u - 1 - - return [ dmp_raise(c, l, v, K) for c in f ] - -
    -@cythonized("g,i") -def dup_deflate(f, K): - """ - Map ``x**m`` to ``y`` in a polynomial in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dup_deflate - - >>> f = ZZ.map([1, 0, 0, 1, 0, 0, 1]) - - >>> dup_deflate(f, ZZ) - (3, [1, 1, 1]) - - """ - if dup_degree(f) <= 0: - return 1, f - - g = 0 - - for i in range(len(f)): - if not f[-i - 1]: - continue - - g = igcd(g, i) - - if g == 1: - return 1, f - - return g, f[::g] - - -@cythonized("u,i,m,a,b") -
    [docs]def dmp_deflate(f, u, K): - """ - Map ``x_i**m_i`` to ``y_i`` in a polynomial in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_deflate - - >>> f = ZZ.map([[1, 0, 0, 2], [], [3, 0, 0, 4]]) - - >>> dmp_deflate(f, 1, ZZ) - ((2, 3), [[1, 2], [3, 4]]) - - """ - if dmp_zero_p(f, u): - return (1,)*(u + 1), f - - F = dmp_to_dict(f, u) - B = [0]*(u + 1) - - for M in F.keys(): - for i, m in enumerate(M): - B[i] = igcd(B[i], m) - - for i, b in enumerate(B): - if not b: - B[i] = 1 - - B = tuple(B) - - if all(b == 1 for b in B): - return B, f - - H = {} - - for A, coeff in F.items(): - N = [ a // b for a, b in zip(A, B) ] - H[tuple(N)] = coeff - - return B, dmp_from_dict(H, u, K) - -
    -@cythonized("G,g,i") -def dup_multi_deflate(polys, K): - """ - Map ``x**m`` to ``y`` in a set of polynomials in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dup_multi_deflate - - >>> f = ZZ.map([1, 0, 2, 0, 3]) - >>> g = ZZ.map([4, 0, 0]) - - >>> dup_multi_deflate((f, g), ZZ) - (2, ([1, 2, 3], [4, 0])) - - """ - G = 0 - - for p in polys: - if dup_degree(p) <= 0: - return 1, polys - - g = 0 - - for i in range(len(p)): - if not p[-i - 1]: - continue - - g = igcd(g, i) - - if g == 1: - return 1, polys - - G = igcd(G, g) - - return G, tuple([ p[::G] for p in polys ]) - - -@cythonized("u,G,g,m,a,b") -
    [docs]def dmp_multi_deflate(polys, u, K): - """ - Map ``x_i**m_i`` to ``y_i`` in a set of polynomials in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_multi_deflate - - >>> f = ZZ.map([[1, 0, 0, 2], [], [3, 0, 0, 4]]) - >>> g = ZZ.map([[1, 0, 2], [], [3, 0, 4]]) - - >>> dmp_multi_deflate((f, g), 1, ZZ) - ((2, 1), ([[1, 0, 0, 2], [3, 0, 0, 4]], [[1, 0, 2], [3, 0, 4]])) - - """ - if not u: - M, H = dup_multi_deflate(polys, K) - return (M,), H - - F, B = [], [0]*(u + 1) - - for p in polys: - f = dmp_to_dict(p, u) - - if not dmp_zero_p(p, u): - for M in f.keys(): - for i, m in enumerate(M): - B[i] = igcd(B[i], m) - - F.append(f) - - for i, b in enumerate(B): - if not b: - B[i] = 1 - - B = tuple(B) - - if all(b == 1 for b in B): - return B, polys - - H = [] - - for f in F: - h = {} - - for A, coeff in f.items(): - N = [ a // b for a, b in zip(A, B) ] - h[tuple(N)] = coeff - - H.append(dmp_from_dict(h, u, K)) - - return B, tuple(H) - -
    -@cythonized("m") -def dup_inflate(f, m, K): - """ - Map ``y`` to ``x**m`` in a polynomial in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dup_inflate - - >>> f = ZZ.map([1, 1, 1]) - - >>> dup_inflate(f, 3, ZZ) - [1, 0, 0, 1, 0, 0, 1] - - """ - if m <= 0: - raise IndexError("'m' must be positive, got %s" % m) - if m == 1 or not f: - return f - - result = [f[0]] - - for coeff in f[1:]: - result.extend([K.zero]*(m - 1)) - result.append(coeff) - - return result - - -@cythonized("u,v,i,j") -def _rec_inflate(g, M, v, i, K): - """Recursive helper for :func:`dmp_inflate`.""" - if not v: - return dup_inflate(g, M[i], K) - if M[i] <= 0: - raise IndexError("all M[i] must be positive, got %s" % M[i]) - - w, j = v - 1, i + 1 - - g = [ _rec_inflate(c, M, w, j, K) for c in g ] - - result = [g[0]] - - for coeff in g[1:]: - for _ in range(1, M[i]): - result.append(dmp_zero(w)) - - result.append(coeff) - - return result - - -@cythonized("u,m") -
    [docs]def dmp_inflate(f, M, u, K): - """ - Map ``y_i`` to ``x_i**k_i`` in a polynomial in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_inflate - - >>> f = ZZ.map([[1, 2], [3, 4]]) - - >>> dmp_inflate(f, (2, 3), 1, ZZ) - [[1, 0, 0, 2], [], [3, 0, 0, 4]] - - """ - if not u: - return dup_inflate(f, M[0], K) - - if all(m == 1 for m in M): - return f - else: - return _rec_inflate(f, M, u, 0, K) - -
    -@cythonized("u,j") -
    [docs]def dmp_exclude(f, u, K): - """ - Exclude useless levels from ``f``. - - Return the levels excluded, the new excluded ``f``, and the new ``u``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_exclude - - >>> f = ZZ.map([[[1]], [[1], [2]]]) - - >>> dmp_exclude(f, 2, ZZ) - ([2], [[1], [1, 2]], 1) - - """ - if not u or dmp_ground_p(f, None, u): - return [], f, u - - J, F = [], dmp_to_dict(f, u) - - for j in range(0, u + 1): - for monom in F.keys(): - if monom[j]: - break - else: - J.append(j) - - if not J: - return [], f, u - - f = {} - - for monom, coeff in F.items(): - monom = list(monom) - - for j in reversed(J): - del monom[j] - - f[tuple(monom)] = coeff - - u -= len(J) - - return J, dmp_from_dict(f, u, K), u - -
    -@cythonized("u,j") -
    [docs]def dmp_include(f, J, u, K): - """ - Include useless levels in ``f``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_include - - >>> f = ZZ.map([[1], [1, 2]]) - - >>> dmp_include(f, [2], 1, ZZ) - [[[1]], [[1], [2]]] - - """ - if not J: - return f - - F, f = dmp_to_dict(f, u), {} - - for monom, coeff in F.items(): - monom = list(monom) - - for j in J: - monom.insert(j, 0) - - f[tuple(monom)] = coeff - - u += len(J) - - return dmp_from_dict(f, u, K) - -
    -@cythonized("u,v,w") -
    [docs]def dmp_inject(f, u, K, front=False): - """ - Convert ``f`` from ``K[X][Y]`` to ``K[X,Y]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_inject - - >>> K = ZZ['x', 'y'] - - >>> dmp_inject([K([[1]]), K([[1], [2]])], 0, K) - ([[[1]], [[1], [2]]], 2) - >>> dmp_inject([K([[1]]), K([[1], [2]])], 0, K, front=True) - ([[[1]], [[1, 2]]], 2) - - """ - f, h = dmp_to_dict(f, u), {} - - v = len(K.gens) - 1 - - for f_monom, g in f.items(): - g = g.to_dict() - - for g_monom, c in g.items(): - if front: - h[g_monom + f_monom] = c - else: - h[f_monom + g_monom] = c - - w = u + v + 1 - - return dmp_from_dict(h, w, K.dom), w - -
    -@cythonized("u,v") -
    [docs]def dmp_eject(f, u, K, front=False): - """ - Convert ``f`` from ``K[X,Y]`` to ``K[X][Y]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_eject - - >>> K = ZZ['x', 'y'] - - >>> dmp_eject([[[1]], [[1], [2]]], 2, K) - [1, x + 2] - - """ - f, h = dmp_to_dict(f, u), {} - - v = u - len(K.gens) + 1 - - for monom, c in f.items(): - if front: - g_monom, f_monom = monom[:v], monom[v:] - else: - f_monom, g_monom = monom[:v], monom[v:] - - if f_monom in h: - h[f_monom][g_monom] = c - else: - h[f_monom] = {g_monom: c} - - for monom, c in h.items(): - h[monom] = K(c) - - return dmp_from_dict(h, v - 1, K) - -
    -@cythonized("i") -def dup_terms_gcd(f, K): - """ - Remove GCD of terms from ``f`` in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dup_terms_gcd - - >>> f = ZZ.map([1, 0, 1, 0, 0]) - - >>> dup_terms_gcd(f, ZZ) - (2, [1, 0, 1]) - - """ - if dup_TC(f, K) or not f: - return 0, f - - i = 0 - - for c in reversed(f): - if not c: - i += 1 - else: - break - - return i, f[:-i] - - -@cythonized("u,g") -
    [docs]def dmp_terms_gcd(f, u, K): - """ - Remove GCD of terms from ``f`` in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_terms_gcd - - >>> f = ZZ.map([[1, 0], [1, 0, 0], [], []]) - - >>> dmp_terms_gcd(f, 1, ZZ) - ((2, 1), [[1], [1, 0]]) - - """ - if dmp_ground_TC(f, u, K) or dmp_zero_p(f, u): - return (0,)*(u + 1), f - - F = dmp_to_dict(f, u) - G = monomial_min(*list(F.keys())) - - if all(g == 0 for g in G): - return G, f - - f = {} - - for monom, coeff in F.items(): - f[monomial_div(monom, G)] = coeff - - return G, dmp_from_dict(f, u, K) - -
    -@cythonized("v,w,d,i") -def _rec_list_terms(g, v, monom): - """Recursive helper for :func:`dmp_list_terms`.""" - d, terms = dmp_degree(g, v), [] - - if not v: - for i, c in enumerate(g): - if not c: - continue - - terms.append((monom + (d - i,), c)) - else: - w = v - 1 - - for i, c in enumerate(g): - terms.extend(_rec_list_terms(c, w, monom + (d - i,))) - - return terms - - -@cythonized("u") -
    [docs]def dmp_list_terms(f, u, K, order=None): - """ - List all non-zero terms from ``f`` in the given order ``order``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_list_terms - - >>> f = ZZ.map([[1, 1], [2, 3]]) - - >>> dmp_list_terms(f, 1, ZZ) - [((1, 1), 1), ((1, 0), 1), ((0, 1), 2), ((0, 0), 3)] - >>> dmp_list_terms(f, 1, ZZ, order='grevlex') - [((1, 1), 1), ((1, 0), 1), ((0, 1), 2), ((0, 0), 3)] - - """ - terms = _rec_list_terms(f, u, ()) - - if not terms: - return [((0,)*(u + 1), K.zero)] - - if order is None: - return terms - else: - return sdp_sort(terms, monomial_key(order)) - -
    -@cythonized("n,m") -def dup_apply_pairs(f, g, h, args, K): - """ - Apply ``h`` to pairs of coefficients of ``f`` and ``g``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dup_apply_pairs - - >>> h = lambda x, y, z: 2*x + y - z - - >>> dup_apply_pairs([1, 2, 3], [3, 2, 1], h, (1,), ZZ) - [4, 5, 6] - - """ - n, m = len(f), len(g) - - if n != m: - if n > m: - g = [K.zero]*(n - m) + g - else: - f = [K.zero]*(m - n) + f - - result = [] - - for a, b in zip(f, g): - result.append(h(a, b, *args)) - - return dup_strip(result) - - -@cythonized("u,v,n,m") -
    [docs]def dmp_apply_pairs(f, g, h, args, u, K): - """ - Apply ``h`` to pairs of coefficients of ``f`` and ``g``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dmp_apply_pairs - - >>> h = lambda x, y, z: 2*x + y - z - - >>> dmp_apply_pairs([[1], [2, 3]], [[3], [2, 1]], h, (1,), 1, ZZ) - [[4], [5, 6]] - - """ - if not u: - return dup_apply_pairs(f, g, h, args, K) - - n, m, v = len(f), len(g), u - 1 - - if n != m: - if n > m: - g = dmp_zeros(n - m, v, K) + g - else: - f = dmp_zeros(m - n, v, K) + f - - result = [] - - for a, b in zip(f, g): - result.append(dmp_apply_pairs(a, b, h, args, v, K)) - - return dmp_strip(result, u) - -
    -@cythonized("m,n,k,M,N") -def dup_slice(f, m, n, K): - """Take a continuous subsequence of terms of ``f`` in ``K[x]``. """ - k = len(f) - - if k >= m: - M = k - m - else: - M = 0 - if k >= n: - N = k - n - else: - N = 0 - - f = f[N:M] - - if not f: - return [] - else: - return f + [K.zero]*m - - -@cythonized("m,n,u") -
    [docs]def dmp_slice(f, m, n, u, K): - """Take a continuous subsequence of terms of ``f`` in ``K[X]``. """ - return dmp_slice_in(f, m, n, 0, u, K) - -
    -@cythonized("m,n,j,u,k") -def dmp_slice_in(f, m, n, j, u, K): - """Take a continuous subsequence of terms of ``f`` in ``x_j`` in ``K[X]``. """ - if j < 0 or j > u: - raise IndexError("-%s <= j < %s expected, got %s" % (u, u, j)) - - if not u: - return dup_slice(f, m, n, K) - - f, g = dmp_to_dict(f, u), {} - - for monom, coeff in f.items(): - k = monom[j] - - if k < m or k >= n: - monom = monom[:j] + (0,) + monom[j + 1:] - - if monom in g: - g[monom] += coeff - else: - g[monom] = coeff - - return dmp_from_dict(g, u, K) - - -
    [docs]def dup_random(n, a, b, K): - """ - Return a polynomial of degree ``n`` with coefficients in ``[a, b]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densebasic import dup_random - - >>> dup_random(3, -10, 10, ZZ) #doctest: +SKIP - [-2, -8, 9, -4] - - """ - f = [ K.convert(random.randint(a, b)) for _ in range(0, n + 1) ] - - while not f[0]: - f[0] = K.convert(random.randint(a, b)) - - return f
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/polys/densetools.html b/dev-py3k/_modules/sympy/polys/densetools.html deleted file mode 100644 index 2c32136fc53..00000000000 --- a/dev-py3k/_modules/sympy/polys/densetools.html +++ /dev/null @@ -1,1470 +0,0 @@ - - - - - - - - - - sympy.polys.densetools — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.polys.densetools

    -"""Advanced tools for dense recursive polynomials in ``K[x]`` or ``K[X]``. """
    -
    -from sympy.polys.densebasic import (
    -    dup_strip, dmp_strip,
    -    dup_convert, dmp_convert,
    -    dup_degree, dmp_degree,
    -    dmp_to_dict,
    -    dmp_from_dict,
    -    dup_LC, dmp_LC, dmp_ground_LC,
    -    dup_TC, dmp_TC,
    -    dmp_zero, dmp_ground,
    -    dmp_zero_p,
    -    dup_to_raw_dict, dup_from_raw_dict,
    -    dmp_zeros
    -)
    -
    -from sympy.polys.densearith import (
    -    dup_add_term, dmp_add_term,
    -    dup_lshift,
    -    dup_add, dmp_add,
    -    dup_sub, dmp_sub,
    -    dup_mul, dmp_mul,
    -    dup_sqr,
    -    dup_div,
    -    dup_rem, dmp_rem,
    -    dmp_expand,
    -    dup_mul_ground, dmp_mul_ground,
    -    dup_quo_ground, dmp_quo_ground,
    -    dup_exquo_ground, dmp_exquo_ground,
    -)
    -
    -from sympy.polys.polyerrors import (
    -    MultivariatePolynomialError,
    -    DomainError
    -)
    -
    -from sympy.utilities import (
    -    cythonized, variations
    -)
    -
    -from math import ceil as _ceil, log as _log
    -
    -
    -@cythonized("m,n,i,j")
    -def dup_integrate(f, m, K):
    -    """
    -    Computes the indefinite integral of ``f`` in ``K[x]``.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.polys.domains import QQ
    -    >>> from sympy.polys.densetools import dup_integrate
    -
    -    >>> dup_integrate([QQ(1), QQ(2), QQ(0)], 1, QQ)
    -    [1/3, 1/1, 0/1, 0/1]
    -    >>> dup_integrate([QQ(1), QQ(2), QQ(0)], 2, QQ)
    -    [1/12, 1/3, 0/1, 0/1, 0/1]
    -
    -    """
    -    if m <= 0 or not f:
    -        return f
    -
    -    g = [K.zero]*m
    -
    -    for i, c in enumerate(reversed(f)):
    -        n = i + 1
    -
    -        for j in range(1, m):
    -            n *= i + j + 1
    -
    -        g.insert(0, K.exquo(c, K(n)))
    -
    -    return g
    -
    -
    -@cythonized("m,u,v,n,i,j")
    -
    [docs]def dmp_integrate(f, m, u, K): - """ - Computes the indefinite integral of ``f`` in ``x_0`` in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import QQ - >>> from sympy.polys.densetools import dmp_integrate - - >>> dmp_integrate([[QQ(1)], [QQ(2), QQ(0)]], 1, 1, QQ) - [[1/2], [2/1, 0/1], []] - >>> dmp_integrate([[QQ(1)], [QQ(2), QQ(0)]], 2, 1, QQ) - [[1/6], [1/1, 0/1], [], []] - - """ - if not u: - return dup_integrate(f, m, K) - - if m <= 0 or dmp_zero_p(f, u): - return f - - g, v = dmp_zeros(m, u - 1, K), u - 1 - - for i, c in enumerate(reversed(f)): - n = i + 1 - - for j in range(1, m): - n *= i + j + 1 - - g.insert(0, dmp_quo_ground(c, K(n), v, K)) - - return g - -
    -@cythonized("m,v,w,i,j") -def _rec_integrate_in(g, m, v, i, j, K): - """Recursive helper for :func:`dmp_integrate_in`.""" - if i == j: - return dmp_integrate(g, m, v, K) - - w, i = v - 1, i + 1 - - return dmp_strip([ _rec_integrate_in(c, m, w, i, j, K) for c in g ], v) - - -@cythonized("m,j,u") -
    [docs]def dmp_integrate_in(f, m, j, u, K): - """ - Computes the indefinite integral of ``f`` in ``x_j`` in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import QQ - >>> from sympy.polys.densetools import dmp_integrate_in - - >>> dmp_integrate_in([[QQ(1)], [QQ(2), QQ(0)]], 1, 0, 1, QQ) - [[1/2], [2/1, 0/1], []] - >>> dmp_integrate_in([[QQ(1)], [QQ(2), QQ(0)]], 1, 1, 1, QQ) - [[1/1, 0/1], [1/1, 0/1, 0/1]] - - """ - if j < 0 or j > u: - raise IndexError("0 <= j <= u expected, got %s" % (u, j)) - - return _rec_integrate_in(f, m, u, 0, j, K) - -
    -@cythonized("m,n,k,i") -def dup_diff(f, m, K): - """ - ``m``-th order derivative of a polynomial in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densetools import dup_diff - - >>> dup_diff([ZZ(1), ZZ(2), ZZ(3), ZZ(4)], 1, ZZ) - [3, 4, 3] - >>> dup_diff([ZZ(1), ZZ(2), ZZ(3), ZZ(4)], 2, ZZ) - [6, 4] - - """ - if m <= 0: - return f - - n = dup_degree(f) - - if n < m: - return [] - - deriv = [] - - if m == 1: - for coeff in f[:-m]: - deriv.append(K(n)*coeff) - n -= 1 - else: - for coeff in f[:-m]: - k = n - - for i in range(n - 1, n - m, -1): - k *= i - - deriv.append(K(k)*coeff) - n -= 1 - - return dup_strip(deriv) - - -@cythonized("u,v,m,n,k,i") -
    [docs]def dmp_diff(f, m, u, K): - """ - ``m``-th order derivative in ``x_0`` of a polynomial in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densetools import dmp_diff - - >>> f = ZZ.map([[1, 2, 3], [2, 3, 1]]) - - >>> dmp_diff(f, 1, 1, ZZ) - [[1, 2, 3]] - >>> dmp_diff(f, 2, 1, ZZ) - [[]] - - """ - if not u: - return dup_diff(f, m, K) - if m <= 0: - return f - - n = dmp_degree(f, u) - - if n < m: - return dmp_zero(u) - - deriv, v = [], u - 1 - - if m == 1: - for coeff in f[:-m]: - deriv.append(dmp_mul_ground(coeff, K(n), v, K)) - n -= 1 - else: - for coeff in f[:-m]: - k = n - - for i in range(n - 1, n - m, -1): - k *= i - - deriv.append(dmp_mul_ground(coeff, K(k), v, K)) - n -= 1 - - return dmp_strip(deriv, u) - -
    -@cythonized("m,v,w,i,j") -def _rec_diff_in(g, m, v, i, j, K): - """Recursive helper for :func:`dmp_diff_in`.""" - if i == j: - return dmp_diff(g, m, v, K) - - w, i = v - 1, i + 1 - - return dmp_strip([ _rec_diff_in(c, m, w, i, j, K) for c in g ], v) - - -@cythonized("m,j,u") -
    [docs]def dmp_diff_in(f, m, j, u, K): - """ - ``m``-th order derivative in ``x_j`` of a polynomial in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densetools import dmp_diff_in - - >>> f = ZZ.map([[1, 2, 3], [2, 3, 1]]) - - >>> dmp_diff_in(f, 1, 0, 1, ZZ) - [[1, 2, 3]] - >>> dmp_diff_in(f, 1, 1, 1, ZZ) - [[2, 2], [4, 3]] - - """ - if j < 0 or j > u: - raise IndexError("0 <= j <= %s expected, got %s" % (u, j)) - - return _rec_diff_in(f, m, u, 0, j, K) - -
    -def dup_eval(f, a, K): - """ - Evaluate a polynomial at ``x = a`` in ``K[x]`` using Horner scheme. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densetools import dup_eval - - >>> dup_eval([ZZ(1), ZZ(2), ZZ(3)], 2, ZZ) - 11 - - """ - if not a: - return dup_TC(f, K) - - result = K.zero - - for c in f: - result *= a - result += c - - return result - - -@cythonized("u,v") -
    [docs]def dmp_eval(f, a, u, K): - """ - Evaluate a polynomial at ``x_0 = a`` in ``K[X]`` using the Horner scheme. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densetools import dmp_eval - - >>> f = ZZ.map([[2, 3], [1, 2]]) - - >>> dmp_eval(f, 2, 1, ZZ) - [5, 8] - - """ - if not u: - return dup_eval(f, a, K) - - if not a: - return dmp_TC(f, K) - - result, v = dmp_LC(f, K), u - 1 - - for coeff in f[1:]: - result = dmp_mul_ground(result, a, v, K) - result = dmp_add(result, coeff, v, K) - - return result - -
    -@cythonized("v,i,j") -def _rec_eval_in(g, a, v, i, j, K): - """Recursive helper for :func:`dmp_eval_in`.""" - if i == j: - return dmp_eval(g, a, v, K) - - v, i = v - 1, i + 1 - - return dmp_strip([ _rec_eval_in(c, a, v, i, j, K) for c in g ], v) - - -@cythonized("u") -
    [docs]def dmp_eval_in(f, a, j, u, K): - """ - Evaluate a polynomial at ``x_j = a`` in ``K[X]`` using the Horner scheme. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densetools import dmp_eval_in - - >>> f = ZZ.map([[2, 3], [1, 2]]) - - >>> dmp_eval_in(f, 2, 0, 1, ZZ) - [5, 8] - >>> dmp_eval_in(f, 2, 1, 1, ZZ) - [7, 4] - - """ - if j < 0 or j > u: - raise IndexError("0 <= j <= %s expected, got %s" % (u, j)) - - return _rec_eval_in(f, a, u, 0, j, K) - -
    -@cythonized("i,u") -def _rec_eval_tail(g, i, A, u, K): - """Recursive helper for :func:`dmp_eval_tail`.""" - if i == u: - return dup_eval(g, A[-1], K) - else: - h = [ _rec_eval_tail(c, i + 1, A, u, K) for c in g ] - - if i < u - len(A) + 1: - return h - else: - return dup_eval(h, A[-u + i - 1], K) - - -@cythonized("u") -
    [docs]def dmp_eval_tail(f, A, u, K): - """ - Evaluate a polynomial at ``x_j = a_j, ...`` in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densetools import dmp_eval_tail - - >>> f = ZZ.map([[2, 3], [1, 2]]) - - >>> dmp_eval_tail(f, (2, 2), 1, ZZ) - 18 - >>> dmp_eval_tail(f, (2,), 1, ZZ) - [7, 4] - - """ - if not A: - return f - - if dmp_zero_p(f, u): - return dmp_zero(u - len(A)) - - e = _rec_eval_tail(f, 0, A, u, K) - - if u == len(A) - 1: - return e - else: - return dmp_strip(e, u - len(A)) - -
    -@cythonized("m,v,i,j") -def _rec_diff_eval(g, m, a, v, i, j, K): - """Recursive helper for :func:`dmp_diff_eval`.""" - if i == j: - return dmp_eval(dmp_diff(g, m, v, K), a, v, K) - - v, i = v - 1, i + 1 - - return dmp_strip([ _rec_diff_eval(c, m, a, v, i, j, K) for c in g ], v) - - -@cythonized("m,j,u") -
    [docs]def dmp_diff_eval_in(f, m, a, j, u, K): - """ - Differentiate and evaluate a polynomial in ``x_j`` at ``a`` in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densetools import dmp_diff_eval_in - - >>> f = ZZ.map([[1, 2, 3], [2, 3, 1]]) - - >>> dmp_diff_eval_in(f, 1, 2, 0, 1, ZZ) - [1, 2, 3] - >>> dmp_diff_eval_in(f, 1, 2, 1, 1, ZZ) - [6, 11] - - """ - if j > u: - raise IndexError("-%s <= j < %s expected, got %s" % (u, u, j)) - if not j: - return dmp_eval(dmp_diff(f, m, u, K), a, u, K) - - return _rec_diff_eval(f, m, a, u, 0, j, K) - -
    -def dup_trunc(f, p, K): - """ - Reduce a ``K[x]`` polynomial modulo a constant ``p`` in ``K``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densetools import dup_trunc - - >>> f = ZZ.map([2, 3, 5, 7]) - - >>> dup_trunc(f, ZZ(3), ZZ) - [-1, 0, -1, 1] - - """ - if K.is_ZZ: - g = [] - - for c in f: - c = c % p - - if c > p // 2: - g.append(c - p) - else: - g.append(c) - else: - g = [ c % p for c in f ] - - return dup_strip(g) - - -@cythonized("u") -
    [docs]def dmp_trunc(f, p, u, K): - """ - Reduce a ``K[X]`` polynomial modulo a polynomial ``p`` in ``K[Y]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densetools import dmp_trunc - - >>> f = ZZ.map([[3, 8], [5, 6], [2, 3]]) - >>> g = ZZ.map([1, -1]) - - >>> dmp_trunc(f, g, 1, ZZ) - [[11], [11], [5]] - - """ - return dmp_strip([ dmp_rem(c, p, u - 1, K) for c in f ], u) - -
    -@cythonized("u,v") -
    [docs]def dmp_ground_trunc(f, p, u, K): - """ - Reduce a ``K[X]`` polynomial modulo a constant ``p`` in ``K``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densetools import dmp_ground_trunc - - >>> f = ZZ.map([[3, 8], [5, 6], [2, 3]]) - - >>> dmp_ground_trunc(f, ZZ(3), 1, ZZ) - [[-1], [-1, 0], [-1, 0]] - - """ - if not u: - return dup_trunc(f, p, K) - - v = u - 1 - - return dmp_strip([ dmp_ground_trunc(c, p, v, K) for c in f ], u) - -
    -
    [docs]def dup_monic(f, K): - """ - Divide all coefficients by ``LC(f)`` in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ, QQ - >>> from sympy.polys.densetools import dup_monic - - >>> dup_monic([ZZ(3), ZZ(6), ZZ(9)], ZZ) - [1, 2, 3] - - >>> dup_monic([QQ(3), QQ(4), QQ(2)], QQ) - [1/1, 4/3, 2/3] - - """ - if not f: - return f - - lc = dup_LC(f, K) - - if K.is_one(lc): - return f - else: - return dup_exquo_ground(f, lc, K) - -
    -@cythonized("u") -
    [docs]def dmp_ground_monic(f, u, K): - """ - Divide all coefficients by ``LC(f)`` in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ, QQ - >>> from sympy.polys.densetools import dmp_ground_monic - - >>> f = ZZ.map([[3, 6], [3, 0], [9, 3]]) - >>> g = QQ.map([[3, 8], [5, 6], [2, 3]]) - - >>> dmp_ground_monic(f, 1, ZZ) - [[1, 2], [1, 0], [3, 1]] - - >>> dmp_ground_monic(g, 1, QQ) - [[1/1, 8/3], [5/3, 2/1], [2/3, 1/1]] - - """ - if not u: - return dup_monic(f, K) - - if dmp_zero_p(f, u): - return f - - lc = dmp_ground_LC(f, u, K) - - if K.is_one(lc): - return f - else: - return dmp_exquo_ground(f, lc, u, K) - -
    -
    [docs]def dup_content(f, K): - """ - Compute the GCD of coefficients of ``f`` in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ, QQ - >>> from sympy.polys.densetools import dup_content - - >>> f = ZZ.map([6, 8, 12]) - >>> g = QQ.map([6, 8, 12]) - - >>> dup_content(f, ZZ) - 2 - >>> dup_content(g, QQ) - 2/1 - - """ - from sympy.polys.domains import QQ - - if not f: - return K.zero - - cont = K.zero - - if K == QQ: - for c in f: - cont = K.gcd(cont, c) - else: - for c in f: - cont = K.gcd(cont, c) - - if K.is_one(cont): - break - - return cont - -
    -@cythonized("u,v") -
    [docs]def dmp_ground_content(f, u, K): - """ - Compute the GCD of coefficients of ``f`` in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ, QQ - >>> from sympy.polys.densetools import dmp_ground_content - - >>> f = ZZ.map([[2, 6], [4, 12]]) - >>> g = QQ.map([[2, 6], [4, 12]]) - - >>> dmp_ground_content(f, 1, ZZ) - 2 - >>> dmp_ground_content(g, 1, QQ) - 2/1 - - """ - from sympy.polys.domains import QQ - - if not u: - return dup_content(f, K) - - if dmp_zero_p(f, u): - return K.zero - - cont, v = K.zero, u - 1 - - if K == QQ: - for c in f: - cont = K.gcd(cont, dmp_ground_content(c, v, K)) - else: - for c in f: - cont = K.gcd(cont, dmp_ground_content(c, v, K)) - - if K.is_one(cont): - break - - return cont - -
    -
    [docs]def dup_primitive(f, K): - """ - Compute content and the primitive form of ``f`` in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ, QQ - >>> from sympy.polys.densetools import dup_primitive - - >>> f = ZZ.map([6, 8, 12]) - >>> g = QQ.map([6, 8, 12]) - - >>> dup_primitive(f, ZZ) - (2, [3, 4, 6]) - >>> dup_primitive(g, QQ) - (2/1, [3/1, 4/1, 6/1]) - - """ - if not f: - return K.zero, f - - cont = dup_content(f, K) - - if K.is_one(cont): - return cont, f - else: - return cont, dup_quo_ground(f, cont, K) - -
    -@cythonized("u") -
    [docs]def dmp_ground_primitive(f, u, K): - """ - Compute content and the primitive form of ``f`` in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ, QQ - >>> from sympy.polys.densetools import dmp_ground_primitive - - >>> f = ZZ.map([[2, 6], [4, 12]]) - >>> g = QQ.map([[2, 6], [4, 12]]) - - >>> dmp_ground_primitive(f, 1, ZZ) - (2, [[1, 3], [2, 6]]) - >>> dmp_ground_primitive(g, 1, QQ) - (2/1, [[1/1, 3/1], [2/1, 6/1]]) - - """ - if not u: - return dup_primitive(f, K) - - if dmp_zero_p(f, u): - return K.zero, f - - cont = dmp_ground_content(f, u, K) - - if K.is_one(cont): - return cont, f - else: - return cont, dmp_quo_ground(f, cont, u, K) - -
    -
    [docs]def dup_extract(f, g, K): - """ - Extract common content from a pair of polynomials in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densetools import dup_extract - - >>> f = ZZ.map([6, 12, 18]) - >>> g = ZZ.map([4, 8, 12]) - - >>> dup_extract(f, g, ZZ) - (2, [3, 6, 9], [2, 4, 6]) - - """ - fc = dup_content(f, K) - gc = dup_content(g, K) - - gcd = K.gcd(fc, gc) - - if not K.is_one(gcd): - f = dup_quo_ground(f, gcd, K) - g = dup_quo_ground(g, gcd, K) - - return gcd, f, g - -
    -@cythonized("u") -
    [docs]def dmp_ground_extract(f, g, u, K): - """ - Extract common content from a pair of polynomials in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densetools import dmp_ground_extract - - >>> f = ZZ.map([[6, 12], [18]]) - >>> g = ZZ.map([[4, 8], [12]]) - - >>> dmp_ground_extract(f, g, 1, ZZ) - (2, [[3, 6], [9]], [[2, 4], [6]]) - - """ - fc = dmp_ground_content(f, u, K) - gc = dmp_ground_content(g, u, K) - - gcd = K.gcd(fc, gc) - - if not K.is_one(gcd): - f = dmp_quo_ground(f, gcd, u, K) - g = dmp_quo_ground(g, gcd, u, K) - - return gcd, f, g - -
    -
    [docs]def dup_real_imag(f, K): - """ - Return bivariate polynomials ``f1`` and ``f2``, such that ``f = f1 + f2*I``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densetools import dup_real_imag - - >>> dup_real_imag([ZZ(1), ZZ(1), ZZ(1), ZZ(1)], ZZ) - ([[1], [1], [-3, 0, 1], [-1, 0, 1]], [[3, 0], [2, 0], [-1, 0, 1, 0]]) - - """ - if not K.is_ZZ and not K.is_QQ: - raise DomainError( - "computing real and imaginary parts is not supported over %s" % K) - - f1 = dmp_zero(1) - f2 = dmp_zero(1) - - if not f: - return f1, f2 - - g = [[[K.one, K.zero]], [[K.one], []]] - h = dmp_ground(f[0], 2) - - for c in f[1:]: - h = dmp_mul(h, g, 2, K) - h = dmp_add_term(h, dmp_ground(c, 1), 0, 2, K) - - H = dup_to_raw_dict(h) - - for k, h in H.items(): - m = k % 4 - - if not m: - f1 = dmp_add(f1, h, 1, K) - elif m == 1: - f2 = dmp_add(f2, h, 1, K) - elif m == 2: - f1 = dmp_sub(f1, h, 1, K) - else: - f2 = dmp_sub(f2, h, 1, K) - - return f1, f2 - -
    -@cythonized('i,n') -
    [docs]def dup_mirror(f, K): - """ - Evaluate efficiently the composition ``f(-x)`` in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densetools import dup_mirror - - >>> dup_mirror([ZZ(1), ZZ(2), -ZZ(4), ZZ(2)], ZZ) - [-1, 2, 4, 2] - - """ - f, n, a = list(f), dup_degree(f), -K.one - - for i in range(n - 1, -1, -1): - f[i], a = a*f[i], -a - - return f - -
    -@cythonized('i,n') -
    [docs]def dup_scale(f, a, K): - """ - Evaluate efficiently composition ``f(a*x)`` in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densetools import dup_scale - - >>> dup_scale([ZZ(1), -ZZ(2), ZZ(1)], ZZ(2), ZZ) - [4, -4, 1] - - """ - f, n, b = list(f), dup_degree(f), a - - for i in range(n - 1, -1, -1): - f[i], b = b*f[i], b*a - - return f - -
    -@cythonized('i,j,n') -
    [docs]def dup_shift(f, a, K): - """ - Evaluate efficiently Taylor shift ``f(x + a)`` in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densetools import dup_shift - - >>> dup_shift([ZZ(1), -ZZ(2), ZZ(1)], ZZ(2), ZZ) - [1, 2, 1] - - """ - f, n = list(f), dup_degree(f) - - for i in range(n, 0, -1): - for j in range(0, i): - f[j + 1] += a*f[j] - - return f - -
    -@cythonized('i,n') -
    [docs]def dup_transform(f, p, q, K): - """ - Evaluate functional transformation ``q**n * f(p/q)`` in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densetools import dup_transform - - >>> f = ZZ.map([1, -2, 1]) - >>> p = ZZ.map([1, 0, 1]) - >>> q = ZZ.map([1, -1]) - - >>> dup_transform(f, p, q, ZZ) - [1, -2, 5, -4, 4] - - """ - if not f: - return [] - - n = dup_degree(f) - h, Q = [f[0]], [[K.one]] - - for i in range(0, n): - Q.append(dup_mul(Q[-1], q, K)) - - for c, q in zip(f[1:], Q[1:]): - h = dup_mul(h, p, K) - q = dup_mul_ground(q, c, K) - h = dup_add(h, q, K) - - return h - -
    -def dup_compose(f, g, K): - """ - Evaluate functional composition ``f(g)`` in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densetools import dup_compose - - >>> f = ZZ.map([1, 1, 0]) - >>> g = ZZ.map([1, -1]) - - >>> dup_compose(f, g, ZZ) - [1, -1, 0] - - """ - if len(g) <= 1: - return dup_strip([dup_eval(f, dup_LC(g, K), K)]) - - if not f: - return [] - - h = [f[0]] - - for c in f[1:]: - h = dup_mul(h, g, K) - h = dup_add_term(h, c, 0, K) - - return h - - -@cythonized("u") -
    [docs]def dmp_compose(f, g, u, K): - """ - Evaluate functional composition ``f(g)`` in ``K[X]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densetools import dmp_compose - - >>> f = ZZ.map([[1, 2], [1, 0]]) - >>> g = ZZ.map([[1, 0]]) - - >>> dmp_compose(f, g, 1, ZZ) - [[1, 3, 0]] - - """ - if not u: - return dup_compose(f, g, K) - - if dmp_zero_p(f, u): - return f - - h = [f[0]] - - for c in f[1:]: - h = dmp_mul(h, g, u, K) - h = dmp_add_term(h, c, 0, u, K) - - return h - -
    -@cythonized("s,n,r,i,j") -def _dup_right_decompose(f, s, K): - """Helper function for :func:`_dup_decompose`.""" - n = dup_degree(f) - lc = dup_LC(f, K) - - f = dup_to_raw_dict(f) - g = { s: K.one } - - r = n // s - - for i in range(1, s): - coeff = K.zero - - for j in range(0, i): - if not n + j - i in f: - continue - - if not s - j in g: - continue - - fc, gc = f[n + j - i], g[s - j] - coeff += (i - r*j)*fc*gc - - g[s - i] = K.quo(coeff, i*r*lc) - - return dup_from_raw_dict(g, K) - - -@cythonized("i") -def _dup_left_decompose(f, h, K): - """Helper function for :func:`_dup_decompose`.""" - g, i = {}, 0 - - while f: - q, r = dup_div(f, h, K) - - if dup_degree(r) > 0: - return None - else: - g[i] = dup_LC(r, K) - f, i = q, i + 1 - - return dup_from_raw_dict(g, K) - - -@cythonized("df,s") -def _dup_decompose(f, K): - """Helper function for :func:`dup_decompose`.""" - df = dup_degree(f) - - for s in range(2, df): - if df % s != 0: - continue - - h = _dup_right_decompose(f, s, K) - - if h is not None: - g = _dup_left_decompose(f, h, K) - - if g is not None: - return g, h - - return None - - -
    [docs]def dup_decompose(f, K): - """ - Computes functional decomposition of ``f`` in ``K[x]``. - - Given a univariate polynomial ``f`` with coefficients in a field of - characteristic zero, returns list ``[f_1, f_2, ..., f_n]``, where:: - - f = f_1 o f_2 o ... f_n = f_1(f_2(... f_n)) - - and ``f_2, ..., f_n`` are monic and homogeneous polynomials of at - least second degree. - - Unlike factorization, complete functional decompositions of - polynomials are not unique, consider examples: - - 1. ``f o g = f(x + b) o (g - b)`` - 2. ``x**n o x**m = x**m o x**n`` - 3. ``T_n o T_m = T_m o T_n`` - - where ``T_n`` and ``T_m`` are Chebyshev polynomials. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densetools import dup_decompose - - >>> f = ZZ.map([1, -2, 1, 0, 0]) - - >>> dup_decompose(f, ZZ) - [[1, 0, 0], [1, -1, 0]] - - References - ========== - - 1. [Kozen89]_ - - """ - F = [] - - while True: - result = _dup_decompose(f, K) - - if result is not None: - f, h = result - F = [h] + F - else: - break - - return [f] + F - -
    -@cythonized("u") -
    [docs]def dmp_lift(f, u, K): - """ - Convert algebraic coefficients to integers in ``K[X]``. - - Examples - ======== - - >>> from sympy import I - >>> from sympy.polys.domains import QQ - >>> from sympy.polys.densetools import dmp_lift - - >>> K = QQ.algebraic_field(I) - >>> f = [K(1), K([QQ(1), QQ(0)]), K([QQ(2), QQ(0)])] - - >>> dmp_lift(f, 0, K) - [1/1, 0/1, 2/1, 0/1, 9/1, 0/1, -8/1, 0/1, 16/1] - - """ - if not K.is_Algebraic: - raise DomainError( - 'computation can be done only in an algebraic domain') - - F, monoms, polys = dmp_to_dict(f, u), [], [] - - for monom, coeff in F.items(): - if not coeff.is_ground: - monoms.append(monom) - - perms = variations([-1, 1], len(monoms), repetition=True) - - for perm in perms: - G = dict(F) - - for sign, monom in zip(perm, monoms): - if sign == -1: - G[monom] = -G[monom] - - polys.append(dmp_from_dict(G, u, K)) - - return dmp_convert(dmp_expand(polys, u, K), u, K, K.dom) - -
    -
    [docs]def dup_sign_variations(f, K): - """ - Compute the number of sign variations of ``f`` in ``K[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.densetools import dup_sign_variations - - >>> f = ZZ.map([1, 0, -1, -1, 1]) - - >>> dup_sign_variations(f, ZZ) - 2 - - """ - prev, k = K.zero, 0 - - for coeff in f: - if K.is_negative(coeff*prev): - k += 1 - - if coeff: - prev = coeff - - return k - -
    -def dup_clear_denoms(f, K0, K1=None, convert=False): - """ - Clear denominators, i.e. transform ``K_0`` to ``K_1``. - - Examples - ======== - - >>> from sympy.polys.domains import QQ, ZZ - >>> from sympy.polys.densetools import dup_clear_denoms - - >>> f = [QQ(1,2), QQ(1,3)] - >>> dup_clear_denoms(f, QQ, convert=False) - (6, [3/1, 2/1]) - - >>> f = [QQ(1,2), QQ(1,3)] - >>> dup_clear_denoms(f, QQ, convert=True) - (6, [3, 2]) - - """ - if K1 is None: - if K0.has_assoc_Ring: - K1 = K0.get_ring() - else: - K1 = K0 - - common = K1.one - - for c in f: - common = K1.lcm(common, K0.denom(c)) - - if not K1.is_one(common): - f = dup_mul_ground(f, common, K0) - - if not convert: - return common, f - else: - return common, dup_convert(f, K0, K1) - - -@cythonized("v,w") -def _rec_clear_denoms(g, v, K0, K1): - """Recursive helper for :func:`dmp_clear_denoms`.""" - common = K1.one - - if not v: - for c in g: - common = K1.lcm(common, K0.denom(c)) - else: - w = v - 1 - - for c in g: - common = K1.lcm(common, _rec_clear_denoms(c, w, K0, K1)) - - return common - - -@cythonized("u") -
    [docs]def dmp_clear_denoms(f, u, K0, K1=None, convert=False): - """ - Clear denominators, i.e. transform ``K_0`` to ``K_1``. - - Examples - ======== - - >>> from sympy.polys.domains import QQ, ZZ - >>> from sympy.polys.densetools import dmp_clear_denoms - - >>> f = [[QQ(1,2)], [QQ(1,3), QQ(1)]] - >>> dmp_clear_denoms(f, 1, QQ, convert=False) - (6, [[3/1], [2/1, 6/1]]) - - >>> f = [[QQ(1,2)], [QQ(1,3), QQ(1)]] - >>> dmp_clear_denoms(f, 1, QQ, convert=True) - (6, [[3], [2, 6]]) - - """ - if not u: - return dup_clear_denoms(f, K0, K1, convert=convert) - - if K1 is None: - if K0.has_assoc_Ring: - K1 = K0.get_ring() - else: - K1 = K0 - - common = _rec_clear_denoms(f, u, K0, K1) - - if not K1.is_one(common): - f = dmp_mul_ground(f, common, u, K0) - - if not convert: - return common, f - else: - return common, dmp_convert(f, u, K0, K1) - -
    -@cythonized('i,n') -def dup_revert(f, n, K): - """ - Compute ``f**(-1)`` mod ``x**n`` using Newton iteration. - - This function computes first ``2**n`` terms of a polynomial that - is a result of inversion of a polynomial modulo ``x**n``. This is - useful to efficiently compute series expansion of ``1/f``. - - Examples - ======== - - >>> from sympy.polys.domains import QQ - >>> from sympy.polys.densetools import dup_revert - - >>> f = [-QQ(1,720), QQ(0), QQ(1,24), QQ(0), -QQ(1,2), QQ(0), QQ(1)] - - >>> dup_revert(f, 8, QQ) - [61/720, 0/1, 5/24, 0/1, 1/2, 0/1, 1/1] - - """ - g = [K.revert(dup_TC(f, K))] - h = [K.one, K.zero, K.zero] - - N = int(_ceil(_log(n, 2))) - - for i in range(1, N + 1): - a = dup_mul_ground(g, K(2), K) - b = dup_mul(f, dup_sqr(g, K), K) - g = dup_rem(dup_sub(a, b, K), h, K) - h = dup_lshift(h, dup_degree(h), K) - - return g - - -
    [docs]def dmp_revert(f, g, u, K): - """ - Compute ``f**(-1)`` mod ``x**n`` using Newton iteration. - - Examples - ======== - - >>> from sympy.polys.domains import QQ - >>> from sympy.polys.densetools import dmp_revert - - """ - if not u: - return dup_revert(f, g, K) - else: - raise MultivariatePolynomialError(f, g)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/polys/distributedmodules.html b/dev-py3k/_modules/sympy/polys/distributedmodules.html deleted file mode 100644 index ddb3604c9ad..00000000000 --- a/dev-py3k/_modules/sympy/polys/distributedmodules.html +++ /dev/null @@ -1,825 +0,0 @@ - - - - - - - - - - sympy.polys.distributedmodules — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.polys.distributedmodules

    -r"""
    -Sparse distributed elements of free modules over multivariate (generalized)
    -polynomial rings.
    -
    -This code and its data structures are very much like the distributed
    -polynomials ``sdp_...``, except that the first "exponent" of the monomial is
    -a module generator index. That is, the multi-exponent ``(i, e_1, ..., e_n)``
    -represents the "monomial" `x_1^{e_1} \dots x_n^{e_n} f_i` of the free module
    -`F` generated by `f_1, \dots, f_r` over (a localization of) the ring
    -`K[x_1, \dots, x_n]`. A module element is simply stored as a list of terms
    -ordered by the monomial order. Here a term is a pair of a multi-exponent and a
    -coefficient. In general, this coefficient should never be zero (since it can
    -then be omitted). The zero module element is stored as an empty list.
    -
    -The main routines are ``sdm_nf_mora`` and ``sdm_groebner`` which can be used
    -to compute, respectively, weak normal forms and standard bases. They work with
    -arbitrary (not necessarily global) monomial orders.
    -
    -In general, product orders have to be used to construct valid monomial orders
    -for modules. However, ``lex`` can be used as-is.
    -
    -Note that the "level" (number of variables, i.e. parameter u+1 in
    -distributedpolys.py) is never needed in this code.
    -
    -The main reference for this file is [SCA],
    -"A Singular Introduction to Commutative Algebra".
    -"""
    -
    -# TODO cythonize
    -
    -from sympy.polys.monomialtools import (
    -    monomial_mul, monomial_lcm, monomial_div, monomial_deg, monomial_divides
    -)
    -
    -from sympy.polys.distributedpolys import (
    -    sdp_LC, sdp_from_dict, sdp_to_dict, sdp_add, sdp_strip
    -)
    -
    -from sympy.polys.polytools import Poly
    -from sympy.polys.polyutils import parallel_dict_from_expr
    -from sympy import S, sympify
    -from sympy.core.compatibility import permutations, next
    -
    -# Additional monomial tools.
    -
    -
    -
    [docs]def sdm_monomial_mul(M, X): - """ - Multiply tuple ``X`` representing a monomial of `K[X]` into the tuple - ``M`` representing a monomial of `F`. - - Examples - ======== - - Multiplying `xy^3` into `x f_1` yields `x^2 y^3 f_1`: - - >>> from sympy.polys.distributedmodules import sdm_monomial_mul - >>> sdm_monomial_mul((1, 1, 0), (1, 3)) - (1, 2, 3) - """ - return (M[0],) + monomial_mul(X, M[1:]) - -
    -
    [docs]def sdm_monomial_deg(M): - """ - Return the total degree of ``M``. - - Examples - ======== - - For example, the total degree of `x^2 y f_5` is 3: - - >>> from sympy.polys.distributedmodules import sdm_monomial_deg - >>> sdm_monomial_deg((5, 2, 1)) - 3 - """ - return monomial_deg(M[1:]) - -
    -def sdm_monomial_lcm(A, B): - """ - Return the "least common multiple" of ``A`` and ``B``. - - IF `A = M e_j` and `B = N e_j`, where `M` and `N` are polynomial monomials, - this returns `\lcm(M, N) e_j`. Note that ``A`` and ``B`` involve distinct - monomials. - - Otherwise the result is undefined. - - >>> from sympy.polys.distributedmodules import sdm_monomial_lcm - >>> sdm_monomial_lcm((1, 2, 3), (1, 0, 5)) - (1, 2, 5) - """ - return (A[0],) + monomial_lcm(A[1:], B[1:]) - - -
    [docs]def sdm_monomial_divides(A, B): - """ - Does there exist a (polynomial) monomial X such that XA = B? - - Examples - ======== - - Positive examples: - - In the following examples, the monomial is given in terms of x, y and the - generator(s), f_1, f_2 etc. The tuple form of that monomial is used in - the call to sdm_monomial_divides. - Note: the generator appears last in the expression but first in the tuple - and other factors appear in the same order that they appear in the monomial - expression. - - `A = f_1` divides `B = f_1` - - >>> from sympy.polys.distributedmodules import sdm_monomial_divides - >>> sdm_monomial_divides((1, 0, 0), (1, 0, 0)) - True - - `A = f_1` divides `B = x^2 y f_1` - - >>> sdm_monomial_divides((1, 0, 0), (1, 2, 1)) - True - - `A = xy f_5` divides `B = x^2 y f_5` - - >>> sdm_monomial_divides((5, 1, 1), (5, 2, 1)) - True - - Negative examples: - - `A = f_1` does not divide `B = f_2` - - >>> sdm_monomial_divides((1, 0, 0), (2, 0, 0)) - False - - `A = x f_1` does not divide `B = f_1` - - >>> sdm_monomial_divides((1, 1, 0), (1, 0, 0)) - False - - `A = xy^2 f_5` does not divide `B = y f_5` - - >>> sdm_monomial_divides((5, 1, 2), (5, 0, 1)) - False - """ - return A[0] == B[0] and all(a <= b for a, b in zip(A[1:], B[1:])) - - -# The actual distributed modules code. - -# These can be re-used without change:
    -sdm_LC = sdp_LC -sdm_to_dict = sdp_to_dict - - -
    [docs]def sdm_from_dict(d, O): - """ - Create an sdm from a dictionary. - - Here ``O`` is the monomial order to use. - - >>> from sympy.polys.distributedmodules import sdm_from_dict - >>> from sympy.polys import QQ, lex - >>> dic = {(1, 1, 0): QQ(1), (1, 0, 0): QQ(2), (0, 1, 0): QQ(0)} - >>> sdm_from_dict(dic, lex) - [((1, 1, 0), 1/1), ((1, 0, 0), 2/1)] - """ - return sdp_strip(sdp_from_dict(d, O)) - -
    -
    [docs]def sdm_add(f, g, O, K): - """ - Add two module elements ``f``, ``g``. - - Addition is done over the ground field ``K``, monomials are ordered - according to ``O``. - - Examples - ======== - - All examples use lexicographic order. - - `(xy f_1) + (f_2) = f_2 + xy f_1` - - >>> from sympy.polys.distributedmodules import sdm_add - >>> from sympy.polys import lex, QQ - >>> sdm_add([((1, 1, 1), QQ(1))], [((2, 0, 0), QQ(1))], lex, QQ) - [((2, 0, 0), 1/1), ((1, 1, 1), 1/1)] - - `(xy f_1) + (-xy f_1)` = 0` - - >>> sdm_add([((1, 1, 1), QQ(1))], [((1, 1, 1), QQ(-1))], lex, QQ) - [] - - `(f_1) + (2f_1) = 3f_1` - - >>> sdm_add([((1, 0, 0), QQ(1))], [((1, 0, 0), QQ(2))], lex, QQ) - [((1, 0, 0), 3/1)] - - `(yf_1) + (xf_1) = xf_1 + yf_1` - - >>> sdm_add([((1, 0, 1), QQ(1))], [((1, 1, 0), QQ(1))], lex, QQ) - [((1, 1, 0), 1/1), ((1, 0, 1), 1/1)] - """ - # send 0 for u (3rd parameter) since it is not needed - return sdp_add(f, g, 0, O, K) - -
    -
    [docs]def sdm_LM(f): - r""" - Returns the leading monomial of ``f``. - - Only valid if `f \ne 0`. - - Examples - ======== - - >>> from sympy.polys.distributedmodules import sdm_LM, sdm_from_dict - >>> from sympy.polys import QQ, lex - >>> dic = {(1, 2, 3): QQ(1), (4, 0, 0): QQ(1), (4, 0, 1): QQ(1)} - >>> sdm_LM(sdm_from_dict(dic, lex)) - (4, 0, 1) - """ - return f[0][0] - -
    -
    [docs]def sdm_LT(f): - r""" - Returns the leading term of ``f``. - - Only valid if `f \ne 0`. - - Examples - ======== - - >>> from sympy.polys.distributedmodules import sdm_LT, sdm_from_dict - >>> from sympy.polys import QQ, lex - >>> dic = {(1, 2, 3): QQ(1), (4, 0, 0): QQ(2), (4, 0, 1): QQ(3)} - >>> sdm_LT(sdm_from_dict(dic, lex)) - ((4, 0, 1), 3/1) - """ - return f[0] - -
    -
    [docs]def sdm_mul_term(f, term, O, K): - """ - Multiply a distributed module element ``f`` by a (polynomial) term ``term``. - - Multiplication of coefficients is done over the ground field ``K``, and - monomials are ordered according to ``O``. - - Examples - ======== - - `0 f_1 = 0` - - >>> from sympy.polys.distributedmodules import sdm_mul_term - >>> from sympy.polys import lex, QQ - >>> sdm_mul_term([((1, 0, 0), QQ(1))], ((0, 0), QQ(0)), lex, QQ) - [] - - `x 0 = 0` - - >>> sdm_mul_term([], ((1, 0), QQ(1)), lex, QQ) - [] - - `(x) (f_1) = xf_1` - - >>> sdm_mul_term([((1, 0, 0), QQ(1))], ((1, 0), QQ(1)), lex, QQ) - [((1, 1, 0), 1/1)] - - `(2xy) (3x f_1 + 4y f_2) = 8xy^2 f_2 + 6x^2y f_1` - - >>> f = [((2, 0, 1), QQ(4)), ((1, 1, 0), QQ(3))] - >>> sdm_mul_term(f, ((1, 1), QQ(2)), lex, QQ) - [((2, 1, 2), 8/1), ((1, 2, 1), 6/1)] - """ - # Note O is not used in this implementation - # This is an almost-verbatim copy of sdp_mul_term - X, c = term - - if not f or not c: - return [] - else: - if K.is_one(c): - return [ (sdm_monomial_mul(f_M, X), f_c) for f_M, f_c in f ] - else: - return [ (sdm_monomial_mul(f_M, X), f_c * c) for f_M, f_c in f ] - -
    -
    [docs]def sdm_zero(): - """Return the zero module element.""" - return [] - -
    -
    [docs]def sdm_deg(f): - """ - Degree of ``f``. - - This is the maximum of the degrees of all its monomials. - Invalid if ``f`` is zero. - - Examples - ======== - - >>> from sympy.polys.distributedmodules import sdm_deg - >>> sdm_deg([((1, 2, 3), 1), ((10, 0, 1), 1), ((2, 3, 4), 4)]) - 7 - """ - return max(sdm_monomial_deg(M[0]) for M in f) - - -# Conversion -
    -
    [docs]def sdm_from_vector(vec, O, K, **opts): - """ - Create an sdm from an iterable of expressions. - - Coefficients are created in the ground field ``K``, and terms are ordered - according to monomial order ``O``. Named arguments are passed on to the - polys conversion code and can be used to specify for example generators. - - Examples - ======== - - >>> from sympy.polys.distributedmodules import sdm_from_vector - >>> from sympy.abc import x, y, z - >>> from sympy.polys import QQ, lex - >>> sdm_from_vector([x**2+y**2, 2*z], lex, QQ) - [((1, 0, 0, 1), 2/1), ((0, 2, 0, 0), 1/1), ((0, 0, 2, 0), 1/1)] - """ - dics, gens = parallel_dict_from_expr(sympify(vec), **opts) - dic = {} - for i, d in enumerate(dics): - for k, v in d.items(): - dic[(i,) + k] = K.convert(v) - return sdm_from_dict(dic, O) - -
    -
    [docs]def sdm_to_vector(f, gens, K, n=None): - """ - Convert sdm ``f`` into a list of polynomial expressions. - - The generators for the polynomial ring are specified via ``gens``. The rank - of the module is guessed, or passed via ``n``. The ground field is assumed - to be ``K``. - - Examples - ======== - - >>> from sympy.polys.distributedmodules import sdm_to_vector - >>> from sympy.abc import x, y, z - >>> from sympy.polys import QQ, lex - >>> f = [((1, 0, 0, 1), QQ(2)), ((0, 2, 0, 0), QQ(1)), ((0, 0, 2, 0), QQ(1))] - >>> sdm_to_vector(f, [x, y, z], QQ) - [x**2 + y**2, 2*z] - """ - dic = sdm_to_dict(f) - dics = {} - for k, v in dic.items(): - dics.setdefault(k[0], []).append((k[1:], v)) - n = n or len(dics) - res = [] - for k in range(n): - if k in dics: - res.append(Poly(dict(dics[k]), gens=gens, domain=K).as_expr()) - else: - res.append(S.Zero) - return res - -# Algorithms. - -
    -
    [docs]def sdm_spoly(f, g, O, K, phantom=None): - """ - Compute the generalized s-polynomial of ``f`` and ``g``. - - The ground field is assumed to be ``K``, and monomials ordered according to - ``O``. - - This is invalid if either of ``f`` or ``g`` is zero. - - If the leading terms of `f` and `g` involve different basis elements of - `F`, their s-poly is defined to be zero. Otherwise it is a certain linear - combination of `f` and `g` in which the leading terms cancel. - See [SCA, defn 2.3.6] for details. - - If ``phantom`` is not ``None``, it should be a pair of module elements on - which to perform the same operation(s) as on ``f`` and ``g``. The in this - case both results are returned. - - Examples - ======== - - >>> from sympy.polys.distributedmodules import sdm_spoly - >>> from sympy.polys import QQ, lex - >>> f = [((2, 1, 1), QQ(1)), ((1, 0, 1), QQ(1))] - >>> g = [((2, 3, 0), QQ(1))] - >>> h = [((1, 2, 3), QQ(1))] - >>> sdm_spoly(f, h, lex, QQ) - [] - >>> sdm_spoly(f, g, lex, QQ) - [((1, 2, 1), 1/1)] - """ - if not f or not g: - return sdm_zero() - LM1 = sdm_LM(f) - LM2 = sdm_LM(g) - if LM1[0] != LM2[0]: - return sdm_zero() - LM1 = LM1[1:] - LM2 = LM2[1:] - lcm = monomial_lcm(LM1, LM2) - m1 = monomial_div(lcm, LM1) - m2 = monomial_div(lcm, LM2) - c = K.quo(-sdm_LC(f, K), sdm_LC(g, K)) - r1 = sdm_add(sdm_mul_term(f, (m1, K.one), O, K), - sdm_mul_term(g, (m2, c), O, K), O, K) - if phantom is None: - return r1 - r2 = sdm_add(sdm_mul_term(phantom[0], (m1, K.one), O, K), - sdm_mul_term(phantom[1], (m2, c), O, K), O, K) - return r1, r2 - -
    -
    [docs]def sdm_ecart(f): - """ - Compute the ecart of ``f``. - - This is defined to be the difference of the total degree of `f` and the - total degree of the leading monomial of `f` [SCA, defn 2.3.7]. - - Invalid if f is zero. - - Examples - ======== - - >>> from sympy.polys.distributedmodules import sdm_ecart - >>> sdm_ecart([((1, 2, 3), 1), ((1, 0, 1), 1)]) - 0 - >>> sdm_ecart([((2, 2, 1), 1), ((1, 5, 1), 1)]) - 3 - """ - return sdm_deg(f) - sdm_monomial_deg(sdm_LM(f)) - -
    -
    [docs]def sdm_nf_mora(f, G, O, K, phantom=None): - r""" - Compute a weak normal form of ``f`` with respect to ``G`` and order ``O``. - - The ground field is assumed to be ``K``, and monomials ordered according to - ``O``. - - Weak normal forms are defined in [SCA, defn 2.3.3]. They are not unique. - This function deterministically computes a weak normal form, depending on - the order of `G`. - - The most important property of a weak normal form is the following: if - `R` is the ring associated with the monomial ordering (if the ordering is - global, we just have `R = K[x_1, \dots, x_n]`, otherwise it is a certain - localization thereof), `I` any ideal of `R` and `G` a standard basis for - `I`, then for any `f \in R`, we have `f \in I` if and only if - `NF(f | G) = 0`. - - This is the generalized Mora algorithm for computing weak normal forms with - respect to arbitrary monomial orders [SCA, algorithm 2.3.9]. - - If ``phantom`` is not ``None``, it should be a pair of "phantom" arguments - on which to perform the same computations as on ``f``, ``G``, both results - are then returned. - """ - from itertools import repeat - h = f - T = list(G) - if phantom is not None: - # "phantom" variables with suffix p - hp = phantom[0] - Tp = list(phantom[1]) - phantom = True - else: - Tp = repeat([]) - phantom = False - while h: - # TODO better data structure!!! - Th = [(g, sdm_ecart(g), gp) for g, gp in zip(T, Tp) - if sdm_monomial_divides(sdm_LM(g), sdm_LM(h))] - if not Th: - break - g, _, gp = min(Th, key=lambda x: x[1]) - if sdm_ecart(g) > sdm_ecart(h): - T.append(h) - if phantom: - Tp.append(hp) - if phantom: - h, hp = sdm_spoly(h, g, O, K, phantom=(hp, gp)) - else: - h = sdm_spoly(h, g, O, K) - if phantom: - return h, hp - return h - -
    -def sdm_nf_buchberger(f, G, O, K, phantom=None): - r""" - Compute a weak normal form of ``f`` with respect to ``G`` and order ``O``. - - The ground field is assumed to be ``K``, and monomials ordered according to - ``O``. - - This is the standard Buchberger algorithm for computing weak normal forms with - respect to *global* monomial orders [SCA, algorithm 1.6.10]. - - If ``phantom`` is not ``None``, it should be a pair of "phantom" arguments - on which to perform the same computations as on ``f``, ``G``, both results - are then returned. - """ - from itertools import repeat - h = f - T = list(G) - if phantom is not None: - # "phantom" variables with suffix p - hp = phantom[0] - Tp = list(phantom[1]) - phantom = True - else: - Tp = repeat([]) - phantom = False - while h: - try: - g, gp = next((g, gp) for g, gp in zip(T, Tp) - if sdm_monomial_divides(sdm_LM(g), sdm_LM(h))) - except StopIteration: - break - if phantom: - h, hp = sdm_spoly(h, g, O, K, phantom=(hp, gp)) - else: - h = sdm_spoly(h, g, O, K) - if phantom: - return h, hp - return h - - -def sdm_nf_buchberger_reduced(f, G, O, K): - r""" - Compute a reduced normal form of ``f`` with respect to ``G`` and order ``O``. - - The ground field is assumed to be ``K``, and monomials ordered according to - ``O``. - - In contrast to weak normal forms, reduced normal forms *are* unique, but - their computation is more expensive. - - This is the standard Buchberger algorithm for computing reduced normal forms - with respect to *global* monomial orders [SCA, algorithm 1.6.11]. - - The ``pantom`` option is not supported, so this normal form cannot be used - as a normal form for the "extended" groebner algorithm. - """ - h = sdm_zero() - g = f - while g: - g = sdm_nf_buchberger(g, G, O, K) - if g: - h = sdm_add(h, [sdm_LT(g)], O, K) - g = g[1:] - return h - - -
    [docs]def sdm_groebner(G, NF, O, K, extended=False): - """ - Compute a minimal standard basis of ``G`` with respect to order ``O``. - - The algorithm uses a normal form ``NF``, for example ``sdm_nf_mora``. - The ground field is assumed to be ``K``, and monomials ordered according - to ``O``. - - Let `N` denote the submodule generated by elements of `G`. A standard - basis for `N` is a subset `S` of `N`, such that `in(S) = in(N)`, where for - any subset `X` of `F`, `in(X)` denotes the submodule generated by the - initial forms of elements of `X`. [SCA, defn 2.3.2] - - A standard basis is called minimal if no subset of it is a standard basis. - - One may show that standard bases are always generating sets. - - Minimal standard bases are not unique. This algorithm computes a - deterministic result, depending on the particular order of `G`. - - If ``extended=True``, also compute the transition matrix from the initial - generators to the groebner basis. That is, return a list of coefficient - vectors, expressing the elements of the groebner basis in terms of the - elements of ``G``. - - This functions implements the "sugar" strategy, see - - Giovini et al: "One sugar cube, please" OR Selection strategies in - Buchberger algorithm. - """ - - # The critical pair set. - # A critical pair is stored as (i, j, s, t) where (i, j) defines the pair - # (by indexing S), s is the sugar of the pair, and t is the lcm of their - # leading monomials. - P = [] - - # The eventual standard basis. - S = [] - Sugars = [] - - def Ssugar(i, j): - """Compute the sugar of the S-poly corresponding to (i, j).""" - LMi = sdm_LM(S[i]) - LMj = sdm_LM(S[j]) - return max(Sugars[i] - sdm_monomial_deg(LMi), - Sugars[j] - sdm_monomial_deg(LMj)) \ - + sdm_monomial_deg(sdm_monomial_lcm(LMi, LMj)) - - ourkey = lambda p: (p[2], O(p[3]), p[1]) - - def update(f, sugar, P): - """Add f with sugar ``sugar`` to S, update P.""" - if not f: - return P - k = len(S) - S.append(f) - Sugars.append(sugar) - - LMf = sdm_LM(f) - - def removethis(pair): - i, j, s, t = pair - if LMf[0] != t[0]: - return False - tik = sdm_monomial_lcm(LMf, sdm_LM(S[i])) - tjk = sdm_monomial_lcm(LMf, sdm_LM(S[j])) - return tik != t and tjk != t and sdm_monomial_divides(tik, t) and \ - sdm_monomial_divides(tjk, t) - # apply the chain criterion - P = [p for p in P if not removethis(p)] - - # new-pair set - N = [(i, k, Ssugar(i, k), sdm_monomial_lcm(LMf, sdm_LM(S[i]))) - for i in range(k) if LMf[0] == sdm_LM(S[i])[0]] - # TODO apply the product criterion? - N.sort(key=ourkey) - remove = set() - for i, p in enumerate(N): - for j in range(i + 1, len(N)): - if sdm_monomial_divides(p[3], N[j][3]): - remove.add(j) - - # TODO mergesort? - P.extend(reversed([p for i, p in enumerate(N) if not i in remove])) - P.sort(key=ourkey, reverse=True) - # NOTE reverse-sort, because we want to pop from the end - return P - - # Figure out the number of generators in the ground ring. - try: - # NOTE: we look for the first non-zero vector, take its first monomial - # the number of generators in the ring is one less than the length - # (since the zeroth entry is for the module generators) - numgens = len(next(x[0] for x in G if x)[0]) - 1 - except StopIteration: - # No non-zero elements in G ... - if extended: - return [], [] - return [] - - # This list will store expressions of the elements of S in terms of the - # initial generators - coefficients = [] - - # First add all the elements of G to S - for i, f in enumerate(G): - P = update(f, sdm_deg(f), P) - if extended and f: - coefficients.append(sdm_from_dict({(i,) + (0,)*numgens: K(1)}, O)) - - # Now carry out the buchberger algorithm. - while P: - i, j, s, t = P.pop() - f, sf, g, sg = S[i], Sugars[i], S[j], Sugars[j] - if extended: - sp, coeff = sdm_spoly(f, g, O, K, - phantom=(coefficients[i], coefficients[j])) - h, hcoeff = NF(sp, S, O, K, phantom=(coeff, coefficients)) - if h: - coefficients.append(hcoeff) - else: - h = NF(sdm_spoly(f, g, O, K), S, O, K) - P = update(h, Ssugar(i, j), P) - - # Finally interreduce the standard basis. - # (TODO again, better data structures) - S = set((tuple(f), i) for i, f in enumerate(S)) - for (a, ai), (b, bi) in permutations(S, 2): - A = sdm_LM(a) - B = sdm_LM(b) - if sdm_monomial_divides(A, B) and (b, bi) in S and (a, ai) in S: - S.remove((b, bi)) - - L = sorted(((list(f), i) for f, i in S), key=lambda p: O(sdm_LM(p[0])), - reverse=True) - res = [x[0] for x in L] - if extended: - return res, [coefficients[i] for _, i in L] - return res
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/polys/distributedpolys.html b/dev-py3k/_modules/sympy/polys/distributedpolys.html deleted file mode 100644 index 54ea90bb54e..00000000000 --- a/dev-py3k/_modules/sympy/polys/distributedpolys.html +++ /dev/null @@ -1,682 +0,0 @@ - - - - - - - - - - sympy.polys.distributedpolys — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.polys.distributedpolys

    -"""Sparse distributed multivariate polynomials. """
    -
    -from sympy.polys.monomialtools import (
    -    monomial_mul, monomial_div, monomial_lcm, lex,
    -)
    -
    -from sympy.polys.polyerrors import (
    -    ExactQuotientFailed,
    -)
    -
    -
    -
    [docs]def sdp_LC(f, K): - """Returns the leading coeffcient of `f`. """ - if not f: - return K.zero - else: - return f[0][1] - -
    -
    [docs]def sdp_LM(f, u): - """Returns the leading monomial of `f`. """ - if not f: - return (0,) * (u + 1) - else: - return f[0][0] - -
    -
    [docs]def sdp_LT(f, u, K): - """Returns the leading term of `f`. """ - if f: - return f[0] - else: - return (0,) * (u + 1), K.zero - -
    -
    [docs]def sdp_del_LT(f): - """Removes the leading from `f`. """ - return f[1:] - -
    -def sdp_coeffs(f): - """Returns a list of monomials in `f`. """ - return [ coeff for _, coeff in f ] - - -
    [docs]def sdp_monoms(f): - """Returns a list of monomials in `f`. """ - return [ monom for monom, _ in f ] - -
    -
    [docs]def sdp_sort(f, O): - """Sort terms in `f` using the given monomial order `O`. """ - return sorted(f, key=lambda term: O(term[0]), reverse=True) - -
    -
    [docs]def sdp_strip(f): - """Remove terms with zero coefficients from `f` in `K[X]`. """ - return [ (monom, coeff) for monom, coeff in f if coeff ] - -
    -
    [docs]def sdp_normal(f, K): - """Normalize distributed polynomial in the given domain. """ - return [ (monom, K.convert(coeff)) for monom, coeff in f if coeff ] - -
    -
    [docs]def sdp_from_dict(f, O): - """Make a distributed polynomial from a dictionary. """ - return sdp_sort(list(f.items()), O) - -
    -
    [docs]def sdp_to_dict(f): - """Make a dictionary from a distributed polynomial. """ - return dict(f) - -
    -
    [docs]def sdp_indep_p(f, j, u): - """Returns `True` if a polynomial is independent of `x_j`. """ - if j < 0 or j > u: - raise IndexError("-%s <= j < %s expected, got %s" % (u, u, j)) - else: - return all(not monom[j] for monom in sdp_monoms(h)) - -
    -
    [docs]def sdp_one_p(f, u, K): - """Returns True if `f` is a multivariate one in `K[X]`. """ - return f == sdp_one(u, K) - -
    -
    [docs]def sdp_one(u, K): - """Returns a multivariate one in `K[X]`. """ - return (((0,) * (u + 1), K.one),) - -
    -
    [docs]def sdp_term_p(f): - """Returns True if `f` has a single term or is zero. """ - return len(f) <= 1 - -
    -
    [docs]def sdp_abs(f, u, O, K): - """Make all coefficients positive in `K[X]`. """ - return [ (monom, K.abs(coeff)) for monom, coeff in f ] - -
    -
    [docs]def sdp_neg(f, u, O, K): - """Negate a polynomial in `K[X]`. """ - return [ (monom, -coeff) for monom, coeff in f ] - -
    -
    [docs]def sdp_add_term(f, term, u, O, K): - """Add a single term using bisection method. """ - M, c = term - - if not c: - return f - if not f: - return [(M, c)] - - monoms = sdp_monoms(f) - - if O(M) > O(monoms[ 0]): - return [(M, c)] + f - if O(M) < O(monoms[-1]): - return f + [(M, c)] - - lo, hi = 0, len(monoms) - 1 - - while lo <= hi: - i = (lo + hi) // 2 - - if O(M) == O(monoms[i]): - coeff = f[i][1] + c - - if not coeff: - return f[:i] + f[i + 1:] - else: - return f[:i] + [(M, coeff)] + f[i + 1:] - else: - if O(M) > O(monoms[i]): - hi = i - 1 - else: - lo = i + 1 - else: - return f[:i] + [(M, c)] + f[i + 1:] - -
    -
    [docs]def sdp_sub_term(f, term, u, O, K): - """Sub a single term using bisection method. """ - M, c = term - - if not c: - return f - if not f: - return [(M, -c)] - - monoms = sdp_monoms(f) - - if O(M) > O(monoms[ 0]): - return [(M, -c)] + f - if O(M) < O(monoms[-1]): - return f + [(M, -c)] - - lo, hi = 0, len(monoms) - 1 - - while lo <= hi: - i = (lo + hi) // 2 - - if O(M) == O(monoms[i]): - coeff = f[i][1] - c - - if not coeff: - return f[:i] + f[i + 1:] - else: - return f[:i] + [(M, coeff)] + f[i + 1:] - else: - if O(M) > O(monoms[i]): - hi = i - 1 - else: - lo = i + 1 - else: - return f[:i] + [(M, -c)] + f[i + 1:] - -
    -
    [docs]def sdp_mul_term(f, term, u, O, K): - """Multiply a distributed polynomial by a term. """ - M, c = term - - if not f or not c: - return [] - else: - if K.is_one(c): - return [ (monomial_mul(f_M, M), f_c) for f_M, f_c in f ] - else: - return [ (monomial_mul(f_M, M), f_c * c) for f_M, f_c in f ] - -
    -
    [docs]def sdp_add(f, g, u, O, K): - """Add distributed polynomials in `K[X]`. """ - h = dict(f) - - for monom, c in g: - if monom in h: - coeff = h[monom] + c - - if not coeff: - del h[monom] - else: - h[monom] = coeff - else: - h[monom] = c - - return sdp_from_dict(h, O) - -
    -
    [docs]def sdp_sub(f, g, u, O, K): - """Subtract distributed polynomials in `K[X]`. """ - h = dict(f) - - for monom, c in g: - if monom in h: - coeff = h[monom] - c - - if not coeff: - del h[monom] - else: - h[monom] = coeff - else: - h[monom] = -c - - return sdp_from_dict(h, O) - -
    -
    [docs]def sdp_mul(f, g, u, O, K): - """Multiply distributed polynomials in `K[X]`. """ - if sdp_term_p(f): - if not f: - return f - else: - return sdp_mul_term(g, f[0], u, O, K) - - if sdp_term_p(g): - if not g: - return g - else: - return sdp_mul_term(f, g[0], u, O, K) - - h = {} - - for fm, fc in f: - for gm, gc in g: - monom = monomial_mul(fm, gm) - coeff = fc * gc - - if monom in h: - coeff += h[monom] - - if not coeff: - del h[monom] - continue - - h[monom] = coeff - - return sdp_from_dict(h, O) - -
    -
    [docs]def sdp_sqr(f, u, O, K): - """Square a distributed polynomial in `K[X]`. """ - h = {} - - for fm, fc in f: - for Fm, Fc in f: - monom = monomial_mul(fm, Fm) - coeff = fc * Fc - - if monom in h: - coeff += h[monom] - - if not coeff: - del h[monom] - continue - - h[monom] = coeff - - return sdp_from_dict(h, O) - -
    -
    [docs]def sdp_pow(f, n, u, O, K): - """Raise `f` to the n-th power in `K[X]`. """ - if not n: - return sdp_one(u, K) - if n < 0: - raise ValueError("can't raise a polynomial to negative power") - if n == 1 or not f or sdp_one_p(f, u, K): - return f - - g = sdp_one(u, K) - - while True: - n, m = n // 2, n - - if m & 1: - g = sdp_mul(g, f, u, O, K) - - if not n: - break - - f = sdp_sqr(f, u, O, K) - - return g - -
    -
    [docs]def sdp_monic(f, K): - """Divides all coefficients by `LC(f)` in `K[X]`. """ - if not f: - return f - - lc_f = sdp_LC(f, K) - - if K.is_one(lc_f): - return f - else: - return [ (m, K.quo(c, lc_f)) for m, c in f ] - -
    -
    [docs]def sdp_content(f, K): - """Returns GCD of coefficients in `K[X]`. """ - from sympy.polys.domains import QQ - - if K.has_Field: - return K.one - else: - cont = K.zero - - if K == QQ: - for c in f: - cont = K.gcd(cont, c) - else: - for c in f: - cont = K.gcd(cont, c) - - if K.is_one(cont) and i: - break - - return cont - -
    -
    [docs]def sdp_primitive(f, K): - """Returns content and a primitive polynomial in `K[X]`. """ - if K.has_Field: - return K.one, f - else: - cont = sdp_content(f, K) - - if K.is_one(cont): - return cont, f - else: - return cont, [ (m, K.quo(c, cont)) for m, c in f ] - -
    -def _term_rr_div(a, b, K): - """Division of two terms in over a ring. """ - a_lm, a_lc = a - b_lm, b_lc = b - - monom = monomial_div(a_lm, b_lm) - - if not (monom is None or a_lc % b_lc): - return monom, K.quo(a_lc, b_lc) - else: - return None - - -def _term_ff_div(a, b, K): - """Division of two terms in over a field. """ - a_lm, a_lc = a - b_lm, b_lc = b - - monom = monomial_div(a_lm, b_lm) - - if monom is not None: - return monom, K.quo(a_lc, b_lc) - else: - return None - - -
    [docs]def sdp_div(f, G, u, O, K): - """ - Generalized polynomial division with remainder in `K[X]`. - - Given polynomial `f` and a set of polynomials `g = (g_1, ..., g_n)` - compute a set of quotients `q = (q_1, ..., q_n)` and remainder `r` - such that `f = q_1*f_1 + ... + q_n*f_n + r`, where `r = 0` or `r` - is a completely reduced polynomial with respect to `g`. - - References - ========== - - 1. [Cox97]_ - 2. [Ajwa95]_ - - """ - Q, r = [ [] for _ in range(len(G)) ], [] - - if K.has_Field: - term_div = _term_ff_div - else: - term_div = _term_rr_div - - while f: - for i, g in enumerate(G): - tq = term_div(sdp_LT(f, u, K), sdp_LT(g, u, K), K) - - if tq is not None: - Q[i] = sdp_add_term(Q[i], tq, u, O, K) - f = sdp_sub(f, sdp_mul_term(g, tq, u, O, K), u, O, K) - - break - else: - r = sdp_add_term(r, sdp_LT(f, u, K), u, O, K) - f = sdp_del_LT(f) - - return Q, r - -
    -
    [docs]def sdp_rem(f, G, u, O, K): - """Returns polynomial remainder in `K[X]`. """ - r = {} - - if K.has_Field: - term_div = _term_ff_div - else: - term_div = _term_rr_div - - ltf = sdp_LT(f, u, K) - f = dict(f) - get = f.get - while f: - for g in G: - tq = term_div(ltf, sdp_LT(g, u, K), K) - - if tq is not None: - m, c = tq - for mg, cg in g: - m1 = monomial_mul(mg, m) - c1 = get(m1, 0) - c*cg - if not c1: - del f[m1] - else: - f[m1] = c1 - if f: - if O == lex: - ltm = max(f) - else: - ltm = max(f, key=lambda mx: O(mx)) - ltf = ltm, f[ltm] - - break - else: - ltm, ltc = ltf - if ltm in r: - r[ltm] += ltc - else: - r[ltm] = ltc - del f[ltm] - if f: - if O == lex: - ltm = max(f) - else: - ltm = max(f, key=lambda mx: O(mx)) - ltf = ltm, f[ltm] - return sdp_from_dict(r, O) - -
    -
    [docs]def sdp_quo(f, g, u, O, K): - """Returns polynomial quotient in `K[x]`. """ - return sdp_div(f, g, u, O, K)[0] - -
    -
    [docs]def sdp_exquo(f, g, u, O, K): - """Returns exact polynomial quotient in `K[X]`. """ - q, r = sdp_div(f, g, u, O, K) - - if not r: - return q - else: - raise ExactQuotientFailed(f, g) - -
    -
    [docs]def sdp_lcm(f, g, u, O, K): - """ - Computes LCM of two polynomials in `K[X]`. - - The LCM is computed as the unique generater of the intersection - of the two ideals generated by `f` and `g`. The approach is to - compute a Groebner basis with respect to lexicographic ordering - of `t*f` and `(1 - t)*g`, where `t` is an unrelated variable and - then filtering out the solution that doesn't contain `t`. - - References - ========== - - 1. [Cox97]_ - - """ - from sympy.polys.groebnertools import sdp_groebner - - if not f or not g: - return [] - - if sdp_term_p(f) and sdp_term_p(g): - monom = monomial_lcm(sdp_LM(f, u), sdp_LM(g, u)) - - fc, gc = sdp_LC(f, K), sdp_LC(g, K) - - if K.has_Field: - coeff = K.one - else: - coeff = K.lcm(fc, gc) - - return [(monom, coeff)] - - if not K.has_Field: - lcm = K.one - else: - fc, f = sdp_primitive(f, K) - gc, g = sdp_primitive(g, K) - - lcm = K.lcm(fc, gc) - - f_terms = tuple( ((1,) + m, c) for m, c in f ) - g_terms = tuple( ((0,) + m, c) for m, c in g ) \ - + tuple( ((1,) + m, -c) for m, c in g ) - - F = sdp_sort(f_terms, lex) - G = sdp_sort(g_terms, lex) - - basis = sdp_groebner([F, G], u, lex, K) - - H = [ h for h in basis if sdp_indep_p(h, 0, u) ] - - if K.is_one(lcm): - h = [ (m[1:], c) for m, c in H[0] ] - else: - h = [ (m[1:], c * lcm) for m, c in H[0] ] - - return sdp_sort(h, O) - -
    -
    [docs]def sdp_gcd(f, g, u, O, K): - """Compute GCD of two polynomials in `K[X]` via LCM. """ - if not K.has_Field: - fc, f = sdp_primitive(f, K) - gc, g = sdp_primitive(g, K) - - gcd = K.gcd(fc, gc) - - h = sdp_quo(sdp_mul(f, g, u, O, K), - sdp_lcm(f, g, u, O, K), u, O, K) - - if not K.has_Field: - if K.is_one(gcd): - return h - else: - return [ (m, c * gcd) for m, c in h ] - else: - return sdp_monic(h, K)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/polys/domains.html b/dev-py3k/_modules/sympy/polys/domains.html deleted file mode 100644 index 97505ca0daf..00000000000 --- a/dev-py3k/_modules/sympy/polys/domains.html +++ /dev/null @@ -1,207 +0,0 @@ - - - - - - - - - - sympy.polys.domains — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.polys.domains

    -"""Implementation of mathematical domains. """
    -
    -from .domain import Domain
    -from .ring import Ring
    -from .field import Field
    -
    -from .simpledomain import SimpleDomain
    -from .compositedomain import CompositeDomain
    -from .characteristiczero import CharacteristicZero
    -
    -from .finitefield import FiniteField
    -from .integerring import IntegerRing
    -from .rationalfield import RationalField
    -from .realdomain import RealDomain
    -
    -from .pythonfinitefield import PythonFiniteField
    -from .sympyfinitefield import SymPyFiniteField
    -from .gmpyfinitefield import GMPYFiniteField
    -
    -from .pythonintegerring import PythonIntegerRing
    -from .sympyintegerring import SymPyIntegerRing
    -from .gmpyintegerring import GMPYIntegerRing
    -
    -from .pythonrationalfield import PythonRationalField
    -from .sympyrationalfield import SymPyRationalField
    -from .gmpyrationalfield import GMPYRationalField
    -
    -from .mpmathrealdomain import MPmathRealDomain
    -
    -from .algebraicfield import AlgebraicField
    -
    -from .polynomialring import PolynomialRing
    -from .fractionfield import FractionField
    -
    -from .expressiondomain import ExpressionDomain
    -
    -from .quotientring import QuotientRing
    -
    -FF_python = PythonFiniteField
    -FF_sympy = SymPyFiniteField
    -FF_gmpy = GMPYFiniteField
    -
    -ZZ_python = PythonIntegerRing
    -ZZ_sympy = SymPyIntegerRing
    -ZZ_gmpy = GMPYIntegerRing
    -
    -QQ_python = PythonRationalField
    -QQ_sympy = SymPyRationalField
    -QQ_gmpy = GMPYRationalField
    -
    -RR_mpmath = MPmathRealDomain
    -
    -from .pythonrationaltype import PythonRationalType
    -
    -from .groundtypes import HAS_GMPY
    -
    -
    -def _getenv(key, default=None):
    -    from os import getenv
    -    return getenv(key, default)
    -
    -GROUND_TYPES = _getenv('SYMPY_GROUND_TYPES', 'auto').lower()
    -
    -if GROUND_TYPES == 'auto':
    -    if HAS_GMPY:
    -        GROUND_TYPES = 'gmpy'
    -    else:
    -        GROUND_TYPES = 'python'
    -
    -if GROUND_TYPES == 'gmpy' and not HAS_GMPY:
    -    from warnings import warn
    -    warn("gmpy library is not installed, switching to 'python' ground types")
    -    GROUND_TYPES = 'python'
    -
    -_GROUND_TYPES_MAP = {
    -    'gmpy': (FF_gmpy, ZZ_gmpy(), QQ_gmpy()),
    -    'sympy': (FF_sympy, ZZ_sympy(), QQ_sympy()),
    -    'python': (FF_python, ZZ_python(), QQ_python()),
    -}
    -
    -try:
    -    FF, ZZ, QQ = _GROUND_TYPES_MAP[GROUND_TYPES]
    -except KeyError:
    -    raise ValueError("invalid ground types: %s" % GROUND_TYPES)
    -
    -GF = FF
    -
    -RR = RR_mpmath()
    -
    -EX = ExpressionDomain()
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/polys/euclidtools.html b/dev-py3k/_modules/sympy/polys/euclidtools.html deleted file mode 100644 index aeca271110a..00000000000 --- a/dev-py3k/_modules/sympy/polys/euclidtools.html +++ /dev/null @@ -1,2087 +0,0 @@ - - - - - - - - - - sympy.polys.euclidtools — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.polys.euclidtools

    -"""Euclidean algorithms, GCDs, LCMs and polynomial remainder sequences. """
    -
    -from sympy.polys.densebasic import (
    -    dup_strip, dmp_raise,
    -    dmp_zero, dmp_one, dmp_ground,
    -    dmp_one_p, dmp_zero_p,
    -    dmp_zeros,
    -    dup_degree, dmp_degree, dmp_degree_in,
    -    dup_LC, dmp_LC, dmp_ground_LC,
    -    dmp_multi_deflate, dmp_inflate,
    -    dup_convert, dmp_convert,
    -    dmp_apply_pairs)
    -
    -from sympy.polys.densearith import (
    -    dup_sub_mul,
    -    dup_neg, dmp_neg,
    -    dmp_add,
    -    dmp_sub,
    -    dup_mul, dmp_mul,
    -    dmp_pow,
    -    dup_div, dmp_div,
    -    dup_rem,
    -    dup_quo, dmp_quo,
    -    dup_prem, dmp_prem,
    -    dup_mul_ground, dmp_mul_ground,
    -    dmp_mul_term,
    -    dup_quo_ground, dmp_quo_ground,
    -    dup_max_norm, dmp_max_norm)
    -
    -from sympy.polys.densetools import (
    -    dup_clear_denoms, dmp_clear_denoms,
    -    dup_diff, dmp_diff,
    -    dup_eval, dmp_eval, dmp_eval_in,
    -    dup_trunc, dmp_ground_trunc,
    -    dup_monic, dmp_ground_monic,
    -    dup_primitive, dmp_ground_primitive,
    -    dup_extract, dmp_ground_extract)
    -
    -from sympy.polys.galoistools import (
    -    gf_int, gf_crt)
    -
    -from sympy.polys.polyerrors import (
    -    MultivariatePolynomialError,
    -    HeuristicGCDFailed,
    -    HomomorphismFailed,
    -    NotInvertible,
    -    DomainError)
    -
    -from sympy.polys.polyconfig import query
    -
    -from sympy.utilities import cythonized
    -
    -from sympy.ntheory import nextprime
    -
    -
    -def dup_half_gcdex(f, g, K):
    -    """
    -    Half extended Euclidean algorithm in `F[x]`.
    -
    -    Returns ``(s, h)`` such that ``h = gcd(f, g)`` and ``s*f = h (mod g)``.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.polys.domains import QQ
    -    >>> from sympy.polys.euclidtools import dup_half_gcdex
    -
    -    >>> f = QQ.map([1, -2, -6, 12, 15])
    -    >>> g = QQ.map([1, 1, -4, -4])
    -
    -    >>> dup_half_gcdex(f, g, QQ)
    -    ([-1/5, 3/5], [1/1, 1/1])
    -
    -    """
    -    if not (K.has_Field or not K.is_Exact):
    -        raise DomainError("can't compute half extended GCD over %s" % K)
    -
    -    a, b = [K.one], []
    -
    -    while g:
    -        q, r = dup_div(f, g, K)
    -        f, g = g, r
    -        a, b = b, dup_sub_mul(a, q, b, K)
    -
    -    a = dup_quo_ground(a, dup_LC(f, K), K)
    -    f = dup_monic(f, K)
    -
    -    return a, f
    -
    -
    -
    [docs]def dmp_half_gcdex(f, g, u, K): - """ - Half extended Euclidean algorithm in `F[X]`. - - Examples - ======== - - >>> from sympy.polys.domains import QQ - >>> from sympy.polys.euclidtools import dmp_half_gcdex - - """ - if not u: - return dup_half_gcdex(f, g, K) - else: - raise MultivariatePolynomialError(f, g) - -
    -def dup_gcdex(f, g, K): - """ - Extended Euclidean algorithm in `F[x]`. - - Returns ``(s, t, h)`` such that ``h = gcd(f, g)`` and ``s*f + t*g = h``. - - Examples - ======== - - >>> from sympy.polys.domains import QQ - >>> from sympy.polys.euclidtools import dup_gcdex - - >>> f = QQ.map([1, -2, -6, 12, 15]) - >>> g = QQ.map([1, 1, -4, -4]) - - >>> dup_gcdex(f, g, QQ) - ([-1/5, 3/5], [1/5, -6/5, 2/1], [1/1, 1/1]) - - """ - s, h = dup_half_gcdex(f, g, K) - - F = dup_sub_mul(h, s, f, K) - t = dup_quo(F, g, K) - - return s, t, h - - -
    [docs]def dmp_gcdex(f, g, u, K): - """ - Extended Euclidean algorithm in `F[X]`. - - Examples - ======== - - >>> from sympy.polys.domains import QQ - >>> from sympy.polys.euclidtools import dmp_gcdex - - """ - if not u: - return dup_gcdex(f, g, K) - else: - raise MultivariatePolynomialError(f, g) - -
    -def dup_invert(f, g, K): - """ - Compute multiplicative inverse of `f` modulo `g` in `F[x]`. - - Examples - ======== - - >>> from sympy.polys.domains import QQ - >>> from sympy.polys.euclidtools import dup_invert - - >>> f = QQ.map([1, 0, -1]) - >>> g = QQ.map([2, -1]) - >>> h = QQ.map([1, -1]) - - >>> dup_invert(f, g, QQ) - [-4/3] - - >>> dup_invert(f, h, QQ) - Traceback (most recent call last): - ... - NotInvertible: zero divisor - - """ - s, h = dup_half_gcdex(f, g, K) - - if h == [K.one]: - return dup_rem(s, g, K) - else: - raise NotInvertible("zero divisor") - - -
    [docs]def dmp_invert(f, g, u, K): - """ - Compute multiplicative inverse of `f` modulo `g` in `F[X]`. - - Examples - ======== - - >>> from sympy.polys.domains import QQ - >>> from sympy.polys.euclidtools import dmp_invert - - """ - if not u: - return dup_invert(f, g, K) - else: - raise MultivariatePolynomialError(f, g) - -
    -def dup_euclidean_prs(f, g, K): - """ - Euclidean polynomial remainder sequence (PRS) in `K[x]`. - - Examples - ======== - - >>> from sympy.polys.domains import QQ - >>> from sympy.polys.euclidtools import dup_euclidean_prs - - >>> f = QQ.map([1, 0, 1, 0, -3, -3, 8, 2, -5]) - >>> g = QQ.map([3, 0, 5, 0, -4, -9, 21]) - - >>> prs = dup_euclidean_prs(f, g, QQ) - - >>> prs[0] - [1/1, 0/1, 1/1, 0/1, -3/1, -3/1, 8/1, 2/1, -5/1] - >>> prs[1] - [3/1, 0/1, 5/1, 0/1, -4/1, -9/1, 21/1] - >>> prs[2] - [-5/9, 0/1, 1/9, 0/1, -1/3] - >>> prs[3] - [-117/25, -9/1, 441/25] - >>> prs[4] - [233150/19773, -102500/6591] - >>> prs[5] - [-1288744821/543589225] - - """ - prs = [f, g] - h = dup_rem(f, g, K) - - while h: - prs.append(h) - f, g = g, h - h = dup_rem(f, g, K) - - return prs - - -
    [docs]def dmp_euclidean_prs(f, g, u, K): - """ - Euclidean polynomial remainder sequence (PRS) in `K[X]`. - - Examples - ======== - - >>> from sympy.polys.domains import QQ - >>> from sympy.polys.euclidtools import dmp_euclidean_prs - - """ - if not u: - return dup_euclidean_prs(f, g, K) - else: - raise MultivariatePolynomialError(f, g) - -
    -def dup_primitive_prs(f, g, K): - """ - Primitive polynomial remainder sequence (PRS) in `K[x]`. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dup_primitive_prs - - >>> f = ZZ.map([1, 0, 1, 0, -3, -3, 8, 2, -5]) - >>> g = ZZ.map([3, 0, 5, 0, -4, -9, 21]) - - >>> prs = dup_primitive_prs(f, g, ZZ) - - >>> prs[0] - [1, 0, 1, 0, -3, -3, 8, 2, -5] - >>> prs[1] - [3, 0, 5, 0, -4, -9, 21] - >>> prs[2] - [-5, 0, 1, 0, -3] - >>> prs[3] - [13, 25, -49] - >>> prs[4] - [4663, -6150] - >>> prs[5] - [1] - - """ - prs = [f, g] - _, h = dup_primitive(dup_prem(f, g, K), K) - - while h: - prs.append(h) - f, g = g, h - _, h = dup_primitive(dup_prem(f, g, K), K) - - return prs - - -
    [docs]def dmp_primitive_prs(f, g, u, K): - """ - Primitive polynomial remainder sequence (PRS) in `K[X]`. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dmp_primitive_prs - - """ - if not u: - return dup_primitive_prs(f, g, K) - else: - raise MultivariatePolynomialError(f, g) - -
    -@cythonized("n,m,d,k") -def dup_inner_subresultants(f, g, K): - """ - Subresultant PRS algorithm in `K[x]`. - - Computes the subresultant polynomial remainder sequence (PRS) of `f` - and `g`, and the values for `\beta_i` and `\delta_i`. The last two - sequences of values are necessary for computing the resultant in - :func:`dup_prs_resultant`. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dup_inner_subresultants - - >>> f = ZZ.map([1, 0, 1]) - >>> g = ZZ.map([1, 0, -1]) - - >>> dup_inner_subresultants(f, g, ZZ) - ([[1, 0, 1], [1, 0, -1], [-2]], [-1, -1], [0, 2]) - - """ - n = dup_degree(f) - m = dup_degree(g) - - if n < m: - f, g = g, f - n, m = m, n - - R = [f, g] - d = n - m - - b = (-K.one)**(d + 1) - c = -K.one - - B, D = [b], [d] - - if not f or not g: - return R, B, D - - h = dup_prem(f, g, K) - h = dup_mul_ground(h, b, K) - - while h: - k = dup_degree(h) - R.append(h) - - lc = dup_LC(g, K) - - if not d: - q = c - else: - q = c**(d - 1) - - c = K.quo((-lc)**d, q) - b = -lc * c**(m - k) - - f, g, m, d = g, h, k, m - k - - B.append(b) - D.append(d) - - h = dup_prem(f, g, K) - - h = dup_quo_ground(h, b, K) - - return R, B, D - - -def dup_subresultants(f, g, K): - """ - Computes subresultant PRS of two polynomials in `K[x]`. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dup_subresultants - - >>> f = ZZ.map([1, 0, 1]) - >>> g = ZZ.map([1, 0, -1]) - - >>> dup_subresultants(f, g, ZZ) - [[1, 0, 1], [1, 0, -1], [-2]] - - """ - return dup_inner_subresultants(f, g, K)[0] - - -@cythonized("s,i,du,dv,dw") -def dup_prs_resultant(f, g, K): - """ - Resultant algorithm in `K[x]` using subresultant PRS. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dup_prs_resultant - - >>> f = ZZ.map([1, 0, 1]) - >>> g = ZZ.map([1, 0, -1]) - - >>> dup_prs_resultant(f, g, ZZ) - (4, [[1, 0, 1], [1, 0, -1], [-2]]) - - """ - if not f or not g: - return (K.zero, []) - - R, B, D = dup_inner_subresultants(f, g, K) - - if dup_degree(R[-1]) > 0: - return (K.zero, R) - if R[-2] == [K.one]: - return (dup_LC(R[-1], K), R) - - s, i = 1, 1 - p, q = K.one, K.one - - for b, d in list(zip(B, D))[:-1]: - du = dup_degree(R[i - 1]) - dv = dup_degree(R[i ]) - dw = dup_degree(R[i + 1]) - - if du % 2 and dv % 2: - s = -s - - lc, i = dup_LC(R[i], K), i + 1 - - p *= b**dv * lc**(du - dw) - q *= lc**(dv*(1 + d)) - - if s < 0: - p = -p - - i = dup_degree(R[-2]) - - res = dup_LC(R[-1], K)**i - - res = K.quo(res*p, q) - - return res, R - - -def dup_resultant(f, g, K, includePRS=False): - """ - Computes resultant of two polynomials in `K[x]`. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dup_resultant - - >>> f = ZZ.map([1, 0, 1]) - >>> g = ZZ.map([1, 0, -1]) - - >>> dup_resultant(f, g, ZZ) - 4 - - """ - if includePRS: - return dup_prs_resultant(f, g, K) - return dup_prs_resultant(f, g, K)[0] - - -@cythonized("u,v,n,m,d,k") -
    [docs]def dmp_inner_subresultants(f, g, u, K): - """ - Subresultant PRS algorithm in `K[X]`. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dmp_inner_subresultants - - >>> f = ZZ.map([[3, 0], [], [-1, 0, 0, -4]]) - >>> g = ZZ.map([[1], [1, 0, 0, 0], [-9]]) - - >>> a = [[3, 0, 0, 0, 0], [1, 0, -27, 4]] - >>> b = [[-3, 0, 0, -12, 1, 0, -54, 8, 729, -216, 16]] - - >>> R = ZZ.map([f, g, a, b]) - >>> B = ZZ.map([[-1], [1], [9, 0, 0, 0, 0, 0, 0, 0, 0]]) - >>> D = ZZ.map([0, 1, 1]) - - >>> dmp_inner_subresultants(f, g, 1, ZZ) == (R, B, D) - True - - """ - if not u: - return dup_inner_subresultants(f, g, K) - - n = dmp_degree(f, u) - m = dmp_degree(g, u) - - if n < m: - f, g = g, f - n, m = m, n - - R = [f, g] - d = n - m - v = u - 1 - - b = dmp_pow(dmp_ground(-K.one, v), d + 1, v, K) - c = dmp_ground(-K.one, v) - - B, D = [b], [d] - - if dmp_zero_p(f, u) or dmp_zero_p(g, u): - return R, B, D - - h = dmp_prem(f, g, u, K) - h = dmp_mul_term(h, b, 0, u, K) - - while not dmp_zero_p(h, u): - k = dmp_degree(h, u) - R.append(h) - - lc = dmp_LC(g, K) - - p = dmp_pow(dmp_neg(lc, v, K), d, v, K) - - if not d: - q = c - else: - q = dmp_pow(c, d - 1, v, K) - - c = dmp_quo(p, q, v, K) - b = dmp_mul(dmp_neg(lc, v, K), - dmp_pow(c, m - k, v, K), v, K) - - f, g, m, d = g, h, k, m - k - - B.append(b) - D.append(d) - - h = dmp_prem(f, g, u, K) - - h = [ dmp_quo(ch, b, v, K) for ch in h ] - - return R, B, D - -
    -@cythonized("u") -
    [docs]def dmp_subresultants(f, g, u, K): - """ - Computes subresultant PRS of two polynomials in `K[X]`. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dmp_subresultants - - >>> f = [[3, 0], [], [-1, 0, 0, -4]] - >>> g = [[1], [1, 0, 0, 0], [-9]] - - >>> a = [[3, 0, 0, 0, 0], [1, 0, -27, 4]] - >>> b = [[-3, 0, 0, -12, 1, 0, -54, 8, 729, -216, 16]] - - >>> dmp_subresultants(f, g, 1, ZZ) == [f, g, a, b] - True - - """ - return dmp_inner_subresultants(f, g, u, K)[0] - -
    -@cythonized("u,v,s,i,d,du,dv,dw") -
    [docs]def dmp_prs_resultant(f, g, u, K): - """ - Resultant algorithm in `K[X]` using subresultant PRS. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dmp_prs_resultant - - >>> f = ZZ.map([[3, 0], [], [-1, 0, 0, -4]]) - >>> g = ZZ.map([[1], [1, 0, 0, 0], [-9]]) - - >>> a = ZZ.map([[3, 0, 0, 0, 0], [1, 0, -27, 4]]) - >>> b = ZZ.map([[-3, 0, 0, -12, 1, 0, -54, 8, 729, -216, 16]]) - - >>> dmp_prs_resultant(f, g, 1, ZZ) == (b[0], [f, g, a, b]) - True - - """ - if not u: - return dup_prs_resultant(f, g, K) - - if dmp_zero_p(f, u) or dmp_zero_p(g, u): - return (dmp_zero(u - 1), []) - - R, B, D = dmp_inner_subresultants(f, g, u, K) - - if dmp_degree(R[-1], u) > 0: - return (dmp_zero(u - 1), R) - if dmp_one_p(R[-2], u, K): - return (dmp_LC(R[-1], K), R) - - s, i, v = 1, 1, u - 1 - - p = dmp_one(v, K) - q = dmp_one(v, K) - - for b, d in list(zip(B, D))[:-1]: - du = dmp_degree(R[i - 1], u) - dv = dmp_degree(R[i ], u) - dw = dmp_degree(R[i + 1], u) - - if du % 2 and dv % 2: - s = -s - - lc, i = dmp_LC(R[i], K), i + 1 - - p = dmp_mul(dmp_mul(p, dmp_pow(b, dv, v, K), v, K), - dmp_pow(lc, du - dw, v, K), v, K) - q = dmp_mul(q, dmp_pow(lc, dv*(1 + d), v, K), v, K) - - _, p, q = dmp_inner_gcd(p, q, v, K) - - if s < 0: - p = dmp_neg(p, v, K) - - i = dmp_degree(R[-2], u) - - res = dmp_pow(dmp_LC(R[-1], K), i, v, K) - res = dmp_quo(dmp_mul(res, p, v, K), q, v, K) - - return res, R - -
    -@cythonized("u,v,n,m,N,M,B") -
    [docs]def dmp_zz_modular_resultant(f, g, p, u, K): - """ - Compute resultant of `f` and `g` modulo a prime `p`. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dmp_zz_modular_resultant - - >>> f = ZZ.map([[1], [1, 2]]) - >>> g = ZZ.map([[2, 1], [3]]) - - >>> dmp_zz_modular_resultant(f, g, ZZ(5), 1, ZZ) - [-2, 0, 1] - - """ - if not u: - return gf_int(dup_prs_resultant(f, g, K)[0] % p, p) - - v = u - 1 - - n = dmp_degree(f, u) - m = dmp_degree(g, u) - - N = dmp_degree_in(f, 1, u) - M = dmp_degree_in(g, 1, u) - - B = n*M + m*N - - D, a = [K.one], -K.one - r = dmp_zero(v) - - while dup_degree(D) <= B: - while True: - a += K.one - - if a == p: - raise HomomorphismFailed('no luck') - - F = dmp_eval_in(f, gf_int(a, p), 1, u, K) - - if dmp_degree(F, v) == n: - G = dmp_eval_in(g, gf_int(a, p), 1, u, K) - - if dmp_degree(G, v) == m: - break - - R = dmp_zz_modular_resultant(F, G, p, v, K) - e = dmp_eval(r, a, v, K) - - if not v: - R = dup_strip([R]) - e = dup_strip([e]) - else: - R = [R] - e = [e] - - d = K.invert(dup_eval(D, a, K), p) - d = dup_mul_ground(D, d, K) - d = dmp_raise(d, v, 0, K) - - c = dmp_mul(d, dmp_sub(R, e, v, K), v, K) - r = dmp_add(r, c, v, K) - - r = dmp_ground_trunc(r, p, v, K) - - D = dup_mul(D, [K.one, -a], K) - D = dup_trunc(D, p, K) - - return r - -
    -def _collins_crt(r, R, P, p, K): - """Wrapper of CRT for Collins's resultant algorithm. """ - return gf_int(gf_crt([r, R], [P, p], K), P*p) - - -@cythonized("u,v,n,m") -
    [docs]def dmp_zz_collins_resultant(f, g, u, K): - """ - Collins's modular resultant algorithm in `Z[X]`. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dmp_zz_collins_resultant - - >>> f = ZZ.map([[1], [1, 2]]) - >>> g = ZZ.map([[2, 1], [3]]) - - >>> dmp_zz_collins_resultant(f, g, 1, ZZ) - [-2, -5, 1] - - """ - - n = dmp_degree(f, u) - m = dmp_degree(g, u) - - if n < 0 or m < 0: - return dmp_zero(u - 1) - - A = dmp_max_norm(f, u, K) - B = dmp_max_norm(g, u, K) - - a = dmp_ground_LC(f, u, K) - b = dmp_ground_LC(g, u, K) - - v = u - 1 - - B = K(2)*K.factorial(K(n + m))*A**m*B**n - r, p, P = dmp_zero(v), K.one, K.one - - while P <= B: - p = K(nextprime(p)) - - while not (a % p) or not (b % p): - p = K(nextprime(p)) - - F = dmp_ground_trunc(f, p, u, K) - G = dmp_ground_trunc(g, p, u, K) - - try: - R = dmp_zz_modular_resultant(F, G, p, u, K) - except HomomorphismFailed: - continue - - if K.is_one(P): - r = R - else: - r = dmp_apply_pairs(r, R, _collins_crt, (P, p, K), v, K) - - P *= p - - return r - -
    -@cythonized("u,n,m") -
    [docs]def dmp_qq_collins_resultant(f, g, u, K0): - """ - Collins's modular resultant algorithm in `Q[X]`. - - Examples - ======== - - >>> from sympy.polys.domains import QQ - >>> from sympy.polys.euclidtools import dmp_qq_collins_resultant - - >>> f = [[QQ(1,2)], [QQ(1), QQ(2,3)]] - >>> g = [[QQ(2), QQ(1)], [QQ(3)]] - - >>> dmp_qq_collins_resultant(f, g, 1, QQ) - [-2/1, -7/3, 5/6] - - """ - n = dmp_degree(f, u) - m = dmp_degree(g, u) - - if n < 0 or m < 0: - return dmp_zero(u - 1) - - K1 = K0.get_ring() - - cf, f = dmp_clear_denoms(f, u, K0, K1) - cg, g = dmp_clear_denoms(g, u, K0, K1) - - f = dmp_convert(f, u, K0, K1) - g = dmp_convert(g, u, K0, K1) - - r = dmp_zz_collins_resultant(f, g, u, K1) - r = dmp_convert(r, u - 1, K1, K0) - - c = K0.convert(cf**m * cg**n, K1) - - return dmp_quo_ground(r, c, u - 1, K0) - -
    -@cythonized("u") -
    [docs]def dmp_resultant(f, g, u, K, includePRS=False): - """ - Computes resultant of two polynomials in `K[X]`. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dmp_resultant - - >>> f = ZZ.map([[3, 0], [], [-1, 0, 0, -4]]) - >>> g = ZZ.map([[1], [1, 0, 0, 0], [-9]]) - - >>> dmp_resultant(f, g, 1, ZZ) - [-3, 0, 0, -12, 1, 0, -54, 8, 729, -216, 16] - - """ - if not u: - return dup_resultant(f, g, K, includePRS=includePRS) - - if includePRS: - return dmp_prs_resultant(f, g, u, K) - - if K.has_Field: - if K.is_QQ and query('USE_COLLINS_RESULTANT'): - return dmp_qq_collins_resultant(f, g, u, K) - else: - if K.is_ZZ and query('USE_COLLINS_RESULTANT'): - return dmp_zz_collins_resultant(f, g, u, K) - - return dmp_prs_resultant(f, g, u, K)[0] - -
    -@cythonized("d,s") -def dup_discriminant(f, K): - """ - Computes discriminant of a polynomial in `K[x]`. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dup_discriminant - - >>> dup_discriminant([ZZ(1), ZZ(2), ZZ(3)], ZZ) - -8 - - """ - d = dup_degree(f) - - if d <= 0: - return K.zero - else: - s = (-1)**((d*(d - 1)) // 2) - c = dup_LC(f, K) - - r = dup_resultant(f, dup_diff(f, 1, K), K) - - return K.quo(r, c*K(s)) - - -@cythonized("u,v,d,s") -
    [docs]def dmp_discriminant(f, u, K): - """ - Computes discriminant of a polynomial in `K[X]`. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dmp_discriminant - - >>> f = ZZ.map([[[[1]], [[]]], [[[1], []]], [[[1, 0]]]]) - - >>> dmp_discriminant(f, 3, ZZ) - [[[-4, 0]], [[1], [], []]] - - """ - if not u: - return dup_discriminant(f, K) - - d, v = dmp_degree(f, u), u - 1 - - if d <= 0: - return dmp_zero(v) - else: - s = (-1)**((d*(d - 1)) // 2) - c = dmp_LC(f, K) - - r = dmp_resultant(f, dmp_diff(f, 1, u, K), u, K) - c = dmp_mul_ground(c, K(s), v, K) - - return dmp_quo(r, c, v, K) - -
    -def _dup_rr_trivial_gcd(f, g, K): - """Handle trivial cases in GCD algorithm over a ring. """ - if not (f or g): - return [], [], [] - elif not f: - if K.is_nonnegative(dup_LC(g, K)): - return g, [], [K.one] - else: - return dup_neg(g, K), [], [-K.one] - elif not g: - if K.is_nonnegative(dup_LC(f, K)): - return f, [K.one], [] - else: - return dup_neg(f, K), [-K.one], [] - - return None - - -def _dup_ff_trivial_gcd(f, g, K): - """Handle trivial cases in GCD algorithm over a field. """ - if not (f or g): - return [], [], [] - elif not f: - return dup_monic(g, K), [], [dup_LC(g, K)] - elif not g: - return dup_monic(f, K), [dup_LC(f, K)], [] - else: - return None - - -@cythonized("u") -def _dmp_rr_trivial_gcd(f, g, u, K): - """Handle trivial cases in GCD algorithm over a ring. """ - zero_f = dmp_zero_p(f, u) - zero_g = dmp_zero_p(g, u) - - if zero_f and zero_g: - return tuple(dmp_zeros(3, u, K)) - elif zero_f: - if K.is_nonnegative(dmp_ground_LC(g, u, K)): - return g, dmp_zero(u), dmp_one(u, K) - else: - return dmp_neg(g, u, K), dmp_zero(u), dmp_ground(-K.one, u) - elif zero_g: - if K.is_nonnegative(dmp_ground_LC(f, u, K)): - return f, dmp_one(u, K), dmp_zero(u) - else: - return dmp_neg(f, u, K), dmp_ground(-K.one, u), dmp_zero(u) - elif query('USE_SIMPLIFY_GCD'): - return _dmp_simplify_gcd(f, g, u, K) - else: - return None - - -@cythonized("u") -def _dmp_ff_trivial_gcd(f, g, u, K): - """Handle trivial cases in GCD algorithm over a field. """ - zero_f = dmp_zero_p(f, u) - zero_g = dmp_zero_p(g, u) - - if zero_f and zero_g: - return tuple(dmp_zeros(3, u, K)) - elif zero_f: - return (dmp_ground_monic(g, u, K), - dmp_zero(u), - dmp_ground(dmp_ground_LC(g, u, K), u)) - elif zero_g: - return (dmp_ground_monic(f, u, K), - dmp_ground(dmp_ground_LC(f, u, K), u), - dmp_zero(u)) - elif query('USE_SIMPLIFY_GCD'): - return _dmp_simplify_gcd(f, g, u, K) - else: - return None - - -@cythonized("u,v,df,dg") -def _dmp_simplify_gcd(f, g, u, K): - """Try to eliminate `x_0` from GCD computation in `K[X]`. """ - df = dmp_degree(f, u) - dg = dmp_degree(g, u) - - if df > 0 and dg > 0: - return None - - if not (df or dg): - F = dmp_LC(f, K) - G = dmp_LC(g, K) - else: - if not df: - F = dmp_LC(f, K) - G = dmp_content(g, u, K) - else: - F = dmp_content(f, u, K) - G = dmp_LC(g, K) - - v = u - 1 - h = dmp_gcd(F, G, v, K) - - cff = [ dmp_quo(cf, h, v, K) for cf in f ] - cfg = [ dmp_quo(cg, h, v, K) for cg in g ] - - return [h], cff, cfg - - -def dup_rr_prs_gcd(f, g, K): - """ - Computes polynomial GCD using subresultants over a ring. - - Returns ``(h, cff, cfg)`` such that ``a = gcd(f, g)``, ``cff = quo(f, h)``, - and ``cfg = quo(g, h)``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dup_rr_prs_gcd - - >>> f = ZZ.map([1, 0, -1]) - >>> g = ZZ.map([1, -3, 2]) - - >>> dup_rr_prs_gcd(f, g, ZZ) - ([1, -1], [1, 1], [1, -2]) - - """ - result = _dup_rr_trivial_gcd(f, g, K) - - if result is not None: - return result - - fc, F = dup_primitive(f, K) - gc, G = dup_primitive(g, K) - - c = K.gcd(fc, gc) - - h = dup_subresultants(F, G, K)[-1] - _, h = dup_primitive(h, K) - - if K.is_negative(dup_LC(h, K)): - c = -c - - h = dup_mul_ground(h, c, K) - - cff = dup_quo(f, h, K) - cfg = dup_quo(g, h, K) - - return h, cff, cfg - - -def dup_ff_prs_gcd(f, g, K): - """ - Computes polynomial GCD using subresultants over a field. - - Returns ``(h, cff, cfg)`` such that ``a = gcd(f, g)``, ``cff = quo(f, h)``, - and ``cfg = quo(g, h)``. - - Examples - ======== - - >>> from sympy.polys.domains import QQ - >>> from sympy.polys.euclidtools import dup_ff_prs_gcd - - >>> f = QQ.map([1, 0, -1]) - >>> g = QQ.map([1, -3, 2]) - - >>> dup_ff_prs_gcd(f, g, QQ) - ([1/1, -1/1], [1/1, 1/1], [1/1, -2/1]) - - """ - result = _dup_ff_trivial_gcd(f, g, K) - - if result is not None: - return result - - h = dup_subresultants(f, g, K)[-1] - h = dup_monic(h, K) - - cff = dup_quo(f, h, K) - cfg = dup_quo(g, h, K) - - return h, cff, cfg - - -@cythonized("u") -
    [docs]def dmp_rr_prs_gcd(f, g, u, K): - """ - Computes polynomial GCD using subresultants over a ring. - - Returns ``(h, cff, cfg)`` such that ``a = gcd(f, g)``, ``cff = quo(f, h)``, - and ``cfg = quo(g, h)``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dmp_rr_prs_gcd - - >>> f = ZZ.map([[1], [2, 0], [1, 0, 0]]) - >>> g = ZZ.map([[1], [1, 0], []]) - - >>> dmp_rr_prs_gcd(f, g, 1, ZZ) - ([[1], [1, 0]], [[1], [1, 0]], [[1], []]) - - """ - if not u: - return dup_rr_prs_gcd(f, g, K) - - result = _dmp_rr_trivial_gcd(f, g, u, K) - - if result is not None: - return result - - fc, F = dmp_primitive(f, u, K) - gc, G = dmp_primitive(g, u, K) - - h = dmp_subresultants(F, G, u, K)[-1] - c, _, _ = dmp_rr_prs_gcd(fc, gc, u - 1, K) - - if K.is_negative(dmp_ground_LC(h, u, K)): - h = dmp_neg(h, u, K) - - _, h = dmp_primitive(h, u, K) - h = dmp_mul_term(h, c, 0, u, K) - - cff = dmp_quo(f, h, u, K) - cfg = dmp_quo(g, h, u, K) - - return h, cff, cfg - -
    -@cythonized("u") -
    [docs]def dmp_ff_prs_gcd(f, g, u, K): - """ - Computes polynomial GCD using subresultants over a field. - - Returns ``(h, cff, cfg)`` such that ``a = gcd(f, g)``, ``cff = quo(f, h)``, - and ``cfg = quo(g, h)``. - - Examples - ======== - - >>> from sympy.polys.domains import QQ - >>> from sympy.polys.euclidtools import dmp_ff_prs_gcd - - >>> f = [[QQ(1,2)], [QQ(1), QQ(0)], [QQ(1,2), QQ(0), QQ(0)]] - >>> g = [[QQ(1)], [QQ(1), QQ(0)], []] - - >>> dmp_ff_prs_gcd(f, g, 1, QQ) - ([[1/1], [1/1, 0/1]], [[1/2], [1/2, 0/1]], [[1/1], []]) - - """ - if not u: - return dup_ff_prs_gcd(f, g, K) - - result = _dmp_ff_trivial_gcd(f, g, u, K) - - if result is not None: - return result - - fc, F = dmp_primitive(f, u, K) - gc, G = dmp_primitive(g, u, K) - - h = dmp_subresultants(F, G, u, K)[-1] - c, _, _ = dmp_ff_prs_gcd(fc, gc, u - 1, K) - - _, h = dmp_primitive(h, u, K) - h = dmp_mul_term(h, c, 0, u, K) - h = dmp_ground_monic(h, u, K) - - cff = dmp_quo(f, h, u, K) - cfg = dmp_quo(g, h, u, K) - - return h, cff, cfg -
    -HEU_GCD_MAX = 6 - - -def _dup_zz_gcd_interpolate(h, x, K): - """Interpolate polynomial GCD from integer GCD. """ - f = [] - - while h: - g = h % x - - if g > x // 2: - g -= x - - f.insert(0, g) - h = (h - g) // x - - return f - - -@cythonized("i,df,dg") -def dup_zz_heu_gcd(f, g, K): - """ - Heuristic polynomial GCD in `Z[x]`. - - Given univariate polynomials `f` and `g` in `Z[x]`, returns - their GCD and cofactors, i.e. polynomials ``h``, ``cff`` and ``cfg`` - such that:: - - h = gcd(f, g), cff = quo(f, h) and cfg = quo(g, h) - - The algorithm is purely heuristic which means it may fail to compute - the GCD. This will be signaled by raising an exception. In this case - you will need to switch to another GCD method. - - The algorithm computes the polynomial GCD by evaluating polynomials - f and g at certain points and computing (fast) integer GCD of those - evaluations. The polynomial GCD is recovered from the integer image - by interpolation. The final step is to verify if the result is the - correct GCD. This gives cofactors as a side effect. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dup_zz_heu_gcd - - >>> f = ZZ.map([1, 0, -1]) - >>> g = ZZ.map([1, -3, 2]) - - >>> dup_zz_heu_gcd(f, g, ZZ) - ([1, -1], [1, 1], [1, -2]) - - References - ========== - - 1. [Liao95]_ - - """ - result = _dup_rr_trivial_gcd(f, g, K) - - if result is not None: - return result - - df = dup_degree(f) - dg = dup_degree(g) - - gcd, f, g = dup_extract(f, g, K) - - if df == 0 or dg == 0: - return [gcd], f, g - - f_norm = dup_max_norm(f, K) - g_norm = dup_max_norm(g, K) - - B = K(2*min(f_norm, g_norm) + 29) - - x = max(min(B, 99*K.sqrt(B)), - 2*min(f_norm // abs(dup_LC(f, K)), - g_norm // abs(dup_LC(g, K))) + 2) - - for i in range(0, HEU_GCD_MAX): - ff = dup_eval(f, x, K) - gg = dup_eval(g, x, K) - - if ff and gg: - h = K.gcd(ff, gg) - - cff = ff // h - cfg = gg // h - - h = _dup_zz_gcd_interpolate(h, x, K) - h = dup_primitive(h, K)[1] - - cff_, r = dup_div(f, h, K) - - if not r: - cfg_, r = dup_div(g, h, K) - - if not r: - h = dup_mul_ground(h, gcd, K) - return h, cff_, cfg_ - - cff = _dup_zz_gcd_interpolate(cff, x, K) - - h, r = dup_div(f, cff, K) - - if not r: - cfg_, r = dup_div(g, h, K) - - if not r: - h = dup_mul_ground(h, gcd, K) - return h, cff, cfg_ - - cfg = _dup_zz_gcd_interpolate(cfg, x, K) - - h, r = dup_div(g, cfg, K) - - if not r: - cff_, r = dup_div(f, h, K) - - if not r: - h = dup_mul_ground(h, gcd, K) - return h, cff_, cfg - - x = 73794*x * K.sqrt(K.sqrt(x)) // 27011 - - raise HeuristicGCDFailed('no luck') - - -@cythonized("v") -def _dmp_zz_gcd_interpolate(h, x, v, K): - """Interpolate polynomial GCD from integer GCD. """ - f = [] - - while not dmp_zero_p(h, v): - g = dmp_ground_trunc(h, x, v, K) - f.insert(0, g) - - h = dmp_sub(h, g, v, K) - h = dmp_quo_ground(h, x, v, K) - - if K.is_negative(dmp_ground_LC(f, v + 1, K)): - return dmp_neg(f, v + 1, K) - else: - return f - - -@cythonized("u,v,i,dg,df") -
    [docs]def dmp_zz_heu_gcd(f, g, u, K): - """ - Heuristic polynomial GCD in `Z[X]`. - - Given univariate polynomials `f` and `g` in `Z[X]`, returns - their GCD and cofactors, i.e. polynomials ``h``, ``cff`` and ``cfg`` - such that:: - - h = gcd(f, g), cff = quo(f, h) and cfg = quo(g, h) - - The algorithm is purely heuristic which means it may fail to compute - the GCD. This will be signaled by raising an exception. In this case - you will need to switch to another GCD method. - - The algorithm computes the polynomial GCD by evaluating polynomials - f and g at certain points and computing (fast) integer GCD of those - evaluations. The polynomial GCD is recovered from the integer image - by interpolation. The evaluation proces reduces f and g variable by - variable into a large integer. The final step is to verify if the - interpolated polynomial is the correct GCD. This gives cofactors of - the input polynomials as a side effect. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dmp_zz_heu_gcd - - >>> f = ZZ.map([[1], [2, 0], [1, 0, 0]]) - >>> g = ZZ.map([[1], [1, 0], []]) - - >>> dmp_zz_heu_gcd(f, g, 1, ZZ) - ([[1], [1, 0]], [[1], [1, 0]], [[1], []]) - - References - ========== - - 1. [Liao95]_ - - """ - if not u: - return dup_zz_heu_gcd(f, g, K) - - result = _dmp_rr_trivial_gcd(f, g, u, K) - - if result is not None: - return result - - gcd, f, g = dmp_ground_extract(f, g, u, K) - - f_norm = dmp_max_norm(f, u, K) - g_norm = dmp_max_norm(g, u, K) - - B = K(2*min(f_norm, g_norm) + 29) - - x = max(min(B, 99*K.sqrt(B)), - 2*min(f_norm // abs(dmp_ground_LC(f, u, K)), - g_norm // abs(dmp_ground_LC(g, u, K))) + 2) - - for i in range(0, HEU_GCD_MAX): - ff = dmp_eval(f, x, u, K) - gg = dmp_eval(g, x, u, K) - - v = u - 1 - - if not (dmp_zero_p(ff, v) or dmp_zero_p(gg, v)): - h, cff, cfg = dmp_zz_heu_gcd(ff, gg, v, K) - - h = _dmp_zz_gcd_interpolate(h, x, v, K) - h = dmp_ground_primitive(h, u, K)[1] - - cff_, r = dmp_div(f, h, u, K) - - if dmp_zero_p(r, u): - cfg_, r = dmp_div(g, h, u, K) - - if dmp_zero_p(r, u): - h = dmp_mul_ground(h, gcd, u, K) - return h, cff_, cfg_ - - cff = _dmp_zz_gcd_interpolate(cff, x, v, K) - - h, r = dmp_div(f, cff, u, K) - - if dmp_zero_p(r, u): - cfg_, r = dmp_div(g, h, u, K) - - if dmp_zero_p(r, u): - h = dmp_mul_ground(h, gcd, u, K) - return h, cff, cfg_ - - cfg = _dmp_zz_gcd_interpolate(cfg, x, v, K) - - h, r = dmp_div(g, cfg, u, K) - - if dmp_zero_p(r, u): - cff_, r = dmp_div(f, h, u, K) - - if dmp_zero_p(r, u): - h = dmp_mul_ground(h, gcd, u, K) - return h, cff_, cfg - - x = 73794*x * K.sqrt(K.sqrt(x)) // 27011 - - raise HeuristicGCDFailed('no luck') - -
    -def dup_qq_heu_gcd(f, g, K0): - """ - Heuristic polynomial GCD in `Q[x]`. - - Returns ``(h, cff, cfg)`` such that ``a = gcd(f, g)``, - ``cff = quo(f, h)``, and ``cfg = quo(g, h)``. - - Examples - ======== - - >>> from sympy.polys.domains import QQ - >>> from sympy.polys.euclidtools import dup_qq_heu_gcd - - >>> f = [QQ(1,2), QQ(7,4), QQ(3,2)] - >>> g = [QQ(1,2), QQ(1), QQ(0)] - - >>> dup_qq_heu_gcd(f, g, QQ) - ([1/1, 2/1], [1/2, 3/4], [1/2, 0/1]) - - """ - result = _dup_ff_trivial_gcd(f, g, K0) - - if result is not None: - return result - - K1 = K0.get_ring() - - cf, f = dup_clear_denoms(f, K0, K1) - cg, g = dup_clear_denoms(g, K0, K1) - - f = dup_convert(f, K0, K1) - g = dup_convert(g, K0, K1) - - h, cff, cfg = dup_zz_heu_gcd(f, g, K1) - - h = dup_convert(h, K1, K0) - - c = dup_LC(h, K0) - h = dup_monic(h, K0) - - cff = dup_convert(cff, K1, K0) - cfg = dup_convert(cfg, K1, K0) - - cff = dup_mul_ground(cff, K0.quo(c, cf), K0) - cfg = dup_mul_ground(cfg, K0.quo(c, cg), K0) - - return h, cff, cfg - - -@cythonized("u") -
    [docs]def dmp_qq_heu_gcd(f, g, u, K0): - """ - Heuristic polynomial GCD in `Q[X]`. - - Returns ``(h, cff, cfg)`` such that ``a = gcd(f, g)``, - ``cff = quo(f, h)``, and ``cfg = quo(g, h)``. - - Examples - ======== - - >>> from sympy.polys.domains import QQ - >>> from sympy.polys.euclidtools import dmp_qq_heu_gcd - - >>> f = [[QQ(1,4)], [QQ(1), QQ(0)], [QQ(1), QQ(0), QQ(0)]] - >>> g = [[QQ(1,2)], [QQ(1), QQ(0)], []] - - >>> dmp_qq_heu_gcd(f, g, 1, QQ) - ([[1/1], [2/1, 0/1]], [[1/4], [1/2, 0/1]], [[1/2], []]) - - """ - result = _dmp_ff_trivial_gcd(f, g, u, K0) - - if result is not None: - return result - - K1 = K0.get_ring() - - cf, f = dmp_clear_denoms(f, u, K0, K1) - cg, g = dmp_clear_denoms(g, u, K0, K1) - - f = dmp_convert(f, u, K0, K1) - g = dmp_convert(g, u, K0, K1) - - h, cff, cfg = dmp_zz_heu_gcd(f, g, u, K1) - - h = dmp_convert(h, u, K1, K0) - - c = dmp_ground_LC(h, u, K0) - h = dmp_ground_monic(h, u, K0) - - cff = dmp_convert(cff, u, K1, K0) - cfg = dmp_convert(cfg, u, K1, K0) - - cff = dmp_mul_ground(cff, K0.quo(c, cf), u, K0) - cfg = dmp_mul_ground(cfg, K0.quo(c, cg), u, K0) - - return h, cff, cfg - -
    -def dup_inner_gcd(f, g, K): - """ - Computes polynomial GCD and cofactors of `f` and `g` in `K[x]`. - - Returns ``(h, cff, cfg)`` such that ``a = gcd(f, g)``, - ``cff = quo(f, h)``, and ``cfg = quo(g, h)``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dup_inner_gcd - - >>> f = ZZ.map([1, 0, -1]) - >>> g = ZZ.map([1, -3, 2]) - - >>> dup_inner_gcd(f, g, ZZ) - ([1, -1], [1, 1], [1, -2]) - - """ - if K.has_Field or not K.is_Exact: - if K.is_QQ and query('USE_HEU_GCD'): - try: - return dup_qq_heu_gcd(f, g, K) - except HeuristicGCDFailed: - pass - - return dup_ff_prs_gcd(f, g, K) - else: - if K.is_ZZ and query('USE_HEU_GCD'): - try: - return dup_zz_heu_gcd(f, g, K) - except HeuristicGCDFailed: - pass - - return dup_rr_prs_gcd(f, g, K) - - -@cythonized("u") -def _dmp_inner_gcd(f, g, u, K): - """Helper function for `dmp_inner_gcd()`. """ - if K.has_Field or not K.is_Exact: - if K.is_QQ and query('USE_HEU_GCD'): - try: - return dmp_qq_heu_gcd(f, g, u, K) - except HeuristicGCDFailed: - pass - - return dmp_ff_prs_gcd(f, g, u, K) - else: - if K.is_ZZ and query('USE_HEU_GCD'): - try: - return dmp_zz_heu_gcd(f, g, u, K) - except HeuristicGCDFailed: - pass - - return dmp_rr_prs_gcd(f, g, u, K) - - -@cythonized("u") -
    [docs]def dmp_inner_gcd(f, g, u, K): - """ - Computes polynomial GCD and cofactors of `f` and `g` in `K[X]`. - - Returns ``(h, cff, cfg)`` such that ``a = gcd(f, g)``, - ``cff = quo(f, h)``, and ``cfg = quo(g, h)``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dmp_inner_gcd - - >>> f = ZZ.map([[1], [2, 0], [1, 0, 0]]) - >>> g = ZZ.map([[1], [1, 0], []]) - - >>> dmp_inner_gcd(f, g, 1, ZZ) - ([[1], [1, 0]], [[1], [1, 0]], [[1], []]) - - """ - if not u: - return dup_inner_gcd(f, g, K) - - J, (f, g) = dmp_multi_deflate((f, g), u, K) - h, cff, cfg = _dmp_inner_gcd(f, g, u, K) - - return (dmp_inflate(h, J, u, K), - dmp_inflate(cff, J, u, K), - dmp_inflate(cfg, J, u, K)) - -
    -def dup_gcd(f, g, K): - """ - Computes polynomial GCD of `f` and `g` in `K[x]`. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dup_gcd - - >>> f = ZZ.map([1, 0, -1]) - >>> g = ZZ.map([1, -3, 2]) - - >>> dup_gcd(f, g, ZZ) - [1, -1] - - """ - return dup_inner_gcd(f, g, K)[0] - - -@cythonized("u") -
    [docs]def dmp_gcd(f, g, u, K): - """ - Computes polynomial GCD of `f` and `g` in `K[X]`. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dmp_gcd - - >>> f = ZZ.map([[1], [2, 0], [1, 0, 0]]) - >>> g = ZZ.map([[1], [1, 0], []]) - - >>> dmp_gcd(f, g, 1, ZZ) - [[1], [1, 0]] - - """ - return dmp_inner_gcd(f, g, u, K)[0] - -
    -def dup_rr_lcm(f, g, K): - """ - Computes polynomial LCM over a ring in `K[x]`. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dup_rr_lcm - - >>> f = ZZ.map([1, 0, -1]) - >>> g = ZZ.map([1, -3, 2]) - - >>> dup_rr_lcm(f, g, ZZ) - [1, -2, -1, 2] - - """ - fc, f = dup_primitive(f, K) - gc, g = dup_primitive(g, K) - - c = K.lcm(fc, gc) - - h = dup_quo(dup_mul(f, g, K), - dup_gcd(f, g, K), K) - - return dup_mul_ground(h, c, K) - - -def dup_ff_lcm(f, g, K): - """ - Computes polynomial LCM over a field in `K[x]`. - - Examples - ======== - - >>> from sympy.polys.domains import QQ - >>> from sympy.polys.euclidtools import dup_ff_lcm - - >>> f = [QQ(1,2), QQ(7,4), QQ(3,2)] - >>> g = [QQ(1,2), QQ(1), QQ(0)] - - >>> dup_ff_lcm(f, g, QQ) - [1/1, 7/2, 3/1, 0/1] - - """ - h = dup_quo(dup_mul(f, g, K), - dup_gcd(f, g, K), K) - - return dup_monic(h, K) - - -def dup_lcm(f, g, K): - """ - Computes polynomial LCM of `f` and `g` in `K[x]`. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dup_lcm - - >>> f = ZZ.map([1, 0, -1]) - >>> g = ZZ.map([1, -3, 2]) - - >>> dup_lcm(f, g, ZZ) - [1, -2, -1, 2] - - """ - if K.has_Field or not K.is_Exact: - return dup_ff_lcm(f, g, K) - else: - return dup_rr_lcm(f, g, K) - - -@cythonized("u") -def dmp_rr_lcm(f, g, u, K): - """ - Computes polynomial LCM over a ring in `K[X]`. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dmp_rr_lcm - - >>> f = ZZ.map([[1], [2, 0], [1, 0, 0]]) - >>> g = ZZ.map([[1], [1, 0], []]) - - >>> dmp_rr_lcm(f, g, 1, ZZ) - [[1], [2, 0], [1, 0, 0], []] - - """ - fc, f = dmp_ground_primitive(f, u, K) - gc, g = dmp_ground_primitive(g, u, K) - - c = K.lcm(fc, gc) - - h = dmp_quo(dmp_mul(f, g, u, K), - dmp_gcd(f, g, u, K), u, K) - - return dmp_mul_ground(h, c, u, K) - - -@cythonized("u") -def dmp_ff_lcm(f, g, u, K): - """ - Computes polynomial LCM over a field in `K[X]`. - - Examples - ======== - - >>> from sympy.polys.domains import QQ - >>> from sympy.polys.euclidtools import dmp_ff_lcm - - >>> f = [[QQ(1,4)], [QQ(1), QQ(0)], [QQ(1), QQ(0), QQ(0)]] - >>> g = [[QQ(1,2)], [QQ(1), QQ(0)], []] - - >>> dmp_ff_lcm(f, g, 1, QQ) - [[1/1], [4/1, 0/1], [4/1, 0/1, 0/1], []] - - """ - h = dmp_quo(dmp_mul(f, g, u, K), - dmp_gcd(f, g, u, K), u, K) - - return dmp_ground_monic(h, u, K) - - -@cythonized("u") -
    [docs]def dmp_lcm(f, g, u, K): - """ - Computes polynomial LCM of `f` and `g` in `K[X]`. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dmp_lcm - - >>> f = ZZ.map([[1], [2, 0], [1, 0, 0]]) - >>> g = ZZ.map([[1], [1, 0], []]) - - >>> dmp_lcm(f, g, 1, ZZ) - [[1], [2, 0], [1, 0, 0], []] - - """ - if not u: - return dup_lcm(f, g, K) - - if K.has_Field or not K.is_Exact: - return dmp_ff_lcm(f, g, u, K) - else: - return dmp_rr_lcm(f, g, u, K) - -
    -@cythonized("u,v") -
    [docs]def dmp_content(f, u, K): - """ - Returns GCD of multivariate coefficients. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dmp_content - - >>> f = ZZ.map([[2, 6], [4, 12]]) - - >>> dmp_content(f, 1, ZZ) - [2, 6] - - """ - cont, v = dmp_LC(f, K), u - 1 - - if dmp_zero_p(f, u): - return cont - - for c in f[1:]: - cont = dmp_gcd(cont, c, v, K) - - if dmp_one_p(cont, v, K): - break - - if K.is_negative(dmp_ground_LC(cont, v, K)): - return dmp_neg(cont, v, K) - else: - return cont - -
    -@cythonized("u,v") -
    [docs]def dmp_primitive(f, u, K): - """ - Returns multivariate content and a primitive polynomial. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dmp_primitive - - >>> f = ZZ.map([[2, 6], [4, 12]]) - - >>> dmp_primitive(f, 1, ZZ) - ([2, 6], [[1], [2]]) - - """ - cont, v = dmp_content(f, u, K), u - 1 - - if dmp_zero_p(f, u) or dmp_one_p(cont, v, K): - return cont, f - else: - return cont, [ dmp_quo(c, cont, v, K) for c in f ] - -
    -def dup_cancel(f, g, K, include=True): - """ - Cancel common factors in a rational function `f/g`. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dup_cancel - - >>> f = ZZ.map([2, 0, -2]) - >>> g = ZZ.map([1, -2, 1]) - - >>> dup_cancel(f, g, ZZ) - ([2, 2], [1, -1]) - - """ - return dmp_cancel(f, g, 0, K, include=include) - - -
    [docs]def dmp_cancel(f, g, u, K, include=True): - """ - Cancel common factors in a rational function `f/g`. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.euclidtools import dmp_cancel - - >>> f = ZZ.map([[2], [0], [-2]]) - >>> g = ZZ.map([[1], [-2], [1]]) - - >>> dmp_cancel(f, g, 1, ZZ) - ([[2], [2]], [[1], [-1]]) - - """ - K0 = None - - if K.has_Field and K.has_assoc_Ring: - K0, K = K, K.get_ring() - - cq, f = dmp_clear_denoms(f, u, K0, K, convert=True) - cp, g = dmp_clear_denoms(g, u, K0, K, convert=True) - else: - cp, cq = K.one, K.one - - _, p, q = dmp_inner_gcd(f, g, u, K) - - if K0 is not None: - p = dmp_convert(p, u, K, K0) - q = dmp_convert(q, u, K, K0) - - K = K0 - - p_neg = K.is_negative(dmp_ground_LC(p, u, K)) - q_neg = K.is_negative(dmp_ground_LC(q, u, K)) - - if p_neg and q_neg: - p, q = dmp_neg(p, u, K), dmp_neg(q, u, K) - elif p_neg: - cp, p = -cp, dmp_neg(p, u, K) - elif q_neg: - cp, q = -cp, dmp_neg(q, u, K) - - if not include: - return cp, cq, p, q - - p = dmp_mul_ground(p, cp, u, K) - q = dmp_mul_ground(q, cq, u, K) - - return p, q
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/polys/factortools.html b/dev-py3k/_modules/sympy/polys/factortools.html deleted file mode 100644 index 2abb25892ee..00000000000 --- a/dev-py3k/_modules/sympy/polys/factortools.html +++ /dev/null @@ -1,1470 +0,0 @@ - - - - - - - - - - sympy.polys.factortools — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.polys.factortools

    -"""Polynomial factorization routines in characteristic zero. """
    -
    -from sympy.polys.galoistools import (
    -    gf_from_int_poly, gf_to_int_poly,
    -    gf_lshift, gf_add_mul, gf_mul,
    -    gf_div, gf_rem,
    -    gf_gcdex,
    -    gf_sqf_p,
    -    gf_factor_sqf, gf_factor)
    -
    -from sympy.polys.densebasic import (
    -    dup_LC, dmp_LC, dmp_ground_LC,
    -    dup_TC,
    -    dup_convert, dmp_convert,
    -    dup_degree, dmp_degree,
    -    dmp_degree_in, dmp_degree_list,
    -    dmp_from_dict,
    -    dmp_zero_p,
    -    dmp_one,
    -    dmp_nest, dmp_raise,
    -    dup_strip,
    -    dmp_ground,
    -    dup_inflate,
    -    dmp_exclude, dmp_include,
    -    dmp_inject, dmp_eject,
    -    dup_terms_gcd, dmp_terms_gcd)
    -
    -from sympy.polys.densearith import (
    -    dup_neg, dmp_neg,
    -    dup_add, dmp_add,
    -    dup_sub, dmp_sub,
    -    dup_mul, dmp_mul,
    -    dup_sqr,
    -    dmp_pow,
    -    dup_div, dmp_div,
    -    dup_quo, dmp_quo,
    -    dmp_expand,
    -    dmp_add_mul,
    -    dup_sub_mul, dmp_sub_mul,
    -    dup_lshift,
    -    dup_max_norm, dmp_max_norm,
    -    dup_l1_norm,
    -    dup_mul_ground, dmp_mul_ground,
    -    dmp_quo_ground)
    -
    -from sympy.polys.densetools import (
    -    dup_clear_denoms, dmp_clear_denoms,
    -    dup_trunc, dmp_ground_trunc,
    -    dup_content,
    -    dup_monic, dmp_ground_monic,
    -    dup_primitive, dmp_ground_primitive,
    -    dmp_eval_tail,
    -    dmp_eval_in, dmp_diff_eval_in,
    -    dmp_compose,
    -    dup_shift, dup_mirror)
    -
    -from sympy.polys.euclidtools import (
    -    dmp_primitive,
    -    dup_inner_gcd, dmp_inner_gcd)
    -
    -from sympy.polys.sqfreetools import (
    -    dup_sqf_p,
    -    dup_sqf_norm, dmp_sqf_norm,
    -    dup_sqf_part, dmp_sqf_part)
    -
    -from sympy.polys.polyutils import _sort_factors
    -from sympy.polys.polyconfig import query
    -
    -from sympy.polys.polyerrors import (
    -    ExtraneousFactors, DomainError, CoercionFailed, EvaluationFailed)
    -
    -from sympy.ntheory import nextprime, isprime, factorint
    -from sympy.utilities import subsets, cythonized
    -
    -from math import ceil as _ceil, log as _log
    -
    -
    -@cythonized("k")
    -def dup_trial_division(f, factors, K):
    -    """Determine multiplicities of factors using trial division. """
    -    result = []
    -
    -    for factor in factors:
    -        k = 0
    -
    -        while True:
    -            q, r = dup_div(f, factor, K)
    -
    -            if not r:
    -                f, k = q, k + 1
    -            else:
    -                break
    -
    -        result.append((factor, k))
    -
    -    return _sort_factors(result)
    -
    -
    -@cythonized("u,k")
    -
    [docs]def dmp_trial_division(f, factors, u, K): - """Determine multiplicities of factors using trial division. """ - result = [] - - for factor in factors: - k = 0 - - while True: - q, r = dmp_div(f, factor, u, K) - - if dmp_zero_p(r, u): - f, k = q, k + 1 - else: - break - - result.append((factor, k)) - - return _sort_factors(result) - -
    -def dup_zz_mignotte_bound(f, K): - """Mignotte bound for univariate polynomials in `K[x]`. """ - a = dup_max_norm(f, K) - b = abs(dup_LC(f, K)) - n = dup_degree(f) - - return K.sqrt(K(n + 1))*2**n*a*b - - -
    [docs]def dmp_zz_mignotte_bound(f, u, K): - """Mignotte bound for multivariate polynomials in `K[X]`. """ - a = dmp_max_norm(f, u, K) - b = abs(dmp_ground_LC(f, u, K)) - n = sum(dmp_degree_list(f, u)) - - return K.sqrt(K(n + 1))*2**n*a*b - -
    -
    [docs]def dup_zz_hensel_step(m, f, g, h, s, t, K): - """ - One step in Hensel lifting in `Z[x]`. - - Given positive integer `m` and `Z[x]` polynomials `f`, `g`, `h`, `s` - and `t` such that:: - - f == g*h (mod m) - s*g + t*h == 1 (mod m) - - lc(f) is not a zero divisor (mod m) - lc(h) == 1 - - deg(f) == deg(g) + deg(h) - deg(s) < deg(h) - deg(t) < deg(g) - - returns polynomials `G`, `H`, `S` and `T`, such that:: - - f == G*H (mod m**2) - S*G + T**H == 1 (mod m**2) - - References - ========== - - 1. [Gathen99]_ - - """ - M = m**2 - - e = dup_sub_mul(f, g, h, K) - e = dup_trunc(e, M, K) - - q, r = dup_div(dup_mul(s, e, K), h, K) - - q = dup_trunc(q, M, K) - r = dup_trunc(r, M, K) - - u = dup_add(dup_mul(t, e, K), dup_mul(q, g, K), K) - G = dup_trunc(dup_add(g, u, K), M, K) - H = dup_trunc(dup_add(h, r, K), M, K) - - u = dup_add(dup_mul(s, G, K), dup_mul(t, H, K), K) - b = dup_trunc(dup_sub(u, [K.one], K), M, K) - - c, d = dup_div(dup_mul(s, b, K), H, K) - - c = dup_trunc(c, M, K) - d = dup_trunc(d, M, K) - - u = dup_add(dup_mul(t, b, K), dup_mul(c, G, K), K) - S = dup_trunc(dup_sub(s, d, K), M, K) - T = dup_trunc(dup_sub(t, u, K), M, K) - - return G, H, S, T - -
    -@cythonized("l,r,k,d") -
    [docs]def dup_zz_hensel_lift(p, f, f_list, l, K): - """ - Multifactor Hensel lifting in `Z[x]`. - - Given a prime `p`, polynomial `f` over `Z[x]` such that `lc(f)` - is a unit modulo `p`, monic pair-wise coprime polynomials `f_i` - over `Z[x]` satisfying:: - - f = lc(f) f_1 ... f_r (mod p) - - and a positive integer `l`, returns a list of monic polynomials - `F_1`, `F_2`, ..., `F_r` satisfying:: - - f = lc(f) F_1 ... F_r (mod p**l) - - F_i = f_i (mod p), i = 1..r - - References - ========== - - 1. [Gathen99]_ - - """ - r = len(f_list) - lc = dup_LC(f, K) - - if r == 1: - F = dup_mul_ground(f, K.gcdex(lc, p**l)[0], K) - return [ dup_trunc(F, p**l, K) ] - - m = p - k = r // 2 - d = int(_ceil(_log(l, 2))) - - g = gf_from_int_poly([lc], p) - - for f_i in f_list[:k]: - g = gf_mul(g, gf_from_int_poly(f_i, p), p, K) - - h = gf_from_int_poly(f_list[k], p) - - for f_i in f_list[k + 1:]: - h = gf_mul(h, gf_from_int_poly(f_i, p), p, K) - - s, t, _ = gf_gcdex(g, h, p, K) - - g = gf_to_int_poly(g, p) - h = gf_to_int_poly(h, p) - s = gf_to_int_poly(s, p) - t = gf_to_int_poly(t, p) - - for _ in range(1, d + 1): - (g, h, s, t), m = dup_zz_hensel_step(m, f, g, h, s, t, K), m**2 - - return dup_zz_hensel_lift(p, g, f_list[:k], l, K) \ - + dup_zz_hensel_lift(p, h, f_list[k:], l, K) - -
    -@cythonized("l,s") -
    [docs]def dup_zz_zassenhaus(f, K): - """Factor primitive square-free polynomials in `Z[x]`. """ - n = dup_degree(f) - - if n == 1: - return [f] - - A = dup_max_norm(f, K) - b = dup_LC(f, K) - B = int(abs(K.sqrt(K(n + 1))*2**n*A*b)) - C = int((n + 1)**(2*n)*A**(2*n - 1)) - gamma = int(_ceil(2*_log(C, 2))) - bound = int(2*gamma*_log(gamma)) - - for p in range(3, bound + 1): - if not isprime(p) or b % p == 0: - continue - - p = K.convert(p) - - F = gf_from_int_poly(f, p) - - if gf_sqf_p(F, p, K): - break - - l = int(_ceil(_log(2*B + 1, p))) - - modular = [] - - for ff in gf_factor_sqf(F, p, K)[1]: - modular.append(gf_to_int_poly(ff, p)) - - g = dup_zz_hensel_lift(p, f, modular, l, K) - - sorted_T = list(range(len(g))) - T = set(sorted_T) - factors, s = [], 1 - - while 2*s <= len(T): - for S in subsets(sorted_T, s): - G, H = [b], [b] - - S = set(S) - T_S = T - S - - for i in S: - G = dup_mul(G, g[i], K) - - for i in T_S: - H = dup_mul(H, g[i], K) - - G = dup_trunc(G, p**l, K) - H = dup_trunc(H, p**l, K) - - G_norm = dup_l1_norm(G, K) - H_norm = dup_l1_norm(H, K) - - if G_norm*H_norm <= B: - T = T_S - sorted_T = [i for i in sorted_T if i not in S] - - G = dup_primitive(G, K)[1] - f = dup_primitive(H, K)[1] - - factors.append(G) - b = dup_LC(f, K) - - break - else: - s += 1 - - return factors + [f] - -
    -
    [docs]def dup_zz_irreducible_p(f, K): - """Test irreducibility using Eisenstein's criterion. """ - lc = dup_LC(f, K) - tc = dup_TC(f, K) - - e_fc = dup_content(f[1:], K) - - if e_fc: - e_ff = factorint(int(e_fc)) - - for p in e_ff.keys(): - if (lc % p) and (tc % p**2): - return True - -
    -@cythonized("n,i") -
    [docs]def dup_cyclotomic_p(f, K, irreducible=False): - """ - Efficiently test if ``f`` is a cyclotomic polnomial. - - Examples - ======== - - >>> from sympy.polys.factortools import dup_cyclotomic_p - >>> from sympy.polys.domains import ZZ - - >>> f = [1, 0, 1, 0, 0, 0,-1, 0, 1, 0,-1, 0, 0, 0, 1, 0, 1] - >>> dup_cyclotomic_p(f, ZZ) - False - - >>> g = [1, 0, 1, 0, 0, 0,-1, 0,-1, 0,-1, 0, 0, 0, 1, 0, 1] - >>> dup_cyclotomic_p(g, ZZ) - True - - """ - if K.is_QQ: - try: - K0, K = K, K.get_ring() - f = dup_convert(f, K0, K) - except CoercionFailed: - return False - elif not K.is_ZZ: - return False - - lc = dup_LC(f, K) - tc = dup_TC(f, K) - - if lc != 1 or (tc != -1 and tc != 1): - return False - - if not irreducible: - coeff, factors = dup_factor_list(f, K) - - if coeff != K.one or factors != [(f, 1)]: - return False - - n = dup_degree(f) - g, h = [], [] - - for i in range(n, -1, -2): - g.insert(0, f[i]) - - for i in range(n - 1, -1, -2): - h.insert(0, f[i]) - - g = dup_sqr(dup_strip(g), K) - h = dup_sqr(dup_strip(h), K) - - F = dup_sub(g, dup_lshift(h, 1, K), K) - - if K.is_negative(dup_LC(F, K)): - F = dup_neg(F, K) - - if F == f: - return True - - g = dup_mirror(f, K) - - if K.is_negative(dup_LC(g, K)): - g = dup_neg(g, K) - - if F == g and dup_cyclotomic_p(g, K): - return True - - G = dup_sqf_part(F, K) - - if dup_sqr(G, K) == F and dup_cyclotomic_p(G, K): - return True - - return False - -
    -@cythonized("n,p,k") -
    [docs]def dup_zz_cyclotomic_poly(n, K): - """Efficiently generate n-th cyclotomic polnomial. """ - h = [K.one, -K.one] - - for p, k in factorint(n).items(): - h = dup_quo(dup_inflate(h, p, K), h, K) - h = dup_inflate(h, p**(k - 1), K) - - return h - -
    -@cythonized("n,p,k,i") -def _dup_cyclotomic_decompose(n, K): - H = [[K.one, -K.one]] - - for p, k in factorint(n).items(): - Q = [ dup_quo(dup_inflate(h, p, K), h, K) for h in H ] - H.extend(Q) - - for i in range(1, k): - Q = [ dup_inflate(q, p, K) for q in Q ] - H.extend(Q) - - return H - - -@cythonized("n") -
    [docs]def dup_zz_cyclotomic_factor(f, K): - """ - Efficiently factor polynomials `x**n - 1` and `x**n + 1` in `Z[x]`. - - Given a univariate polynomial `f` in `Z[x]` returns a list of factors - of `f`, provided that `f` is in the form `x**n - 1` or `x**n + 1` for - `n >= 1`. Otherwise returns None. - - Factorization is performed using using cyclotomic decomposition of `f`, - which makes this method much faster that any other direct factorization - approach (e.g. Zassenhaus's). - - References - ========== - - 1. [Weisstein09]_ - - """ - lc_f, tc_f = dup_LC(f, K), dup_TC(f, K) - - if dup_degree(f) <= 0: - return None - - if lc_f != 1 or tc_f not in [-1, 1]: - return None - - if any(bool(cf) for cf in f[1:-1]): - return None - - n = dup_degree(f) - F = _dup_cyclotomic_decompose(n, K) - - if not K.is_one(tc_f): - return F - else: - H = [] - - for h in _dup_cyclotomic_decompose(2*n, K): - if h not in F: - H.append(h) - - return H - -
    -@cythonized("n") -
    [docs]def dup_zz_factor_sqf(f, K): - """Factor square-free (non-primitive) polyomials in `Z[x]`. """ - cont, g = dup_primitive(f, K) - - n = dup_degree(g) - - if dup_LC(g, K) < 0: - cont, g = -cont, dup_neg(g, K) - - if n <= 0: - return cont, [] - elif n == 1: - return cont, [(g, 1)] - - if query('USE_IRREDUCIBLE_IN_FACTOR'): - if dup_zz_irreducible_p(g, K): - return cont, [(g, 1)] - - factors = None - - if query('USE_CYCLOTOMIC_FACTOR'): - factors = dup_zz_cyclotomic_factor(g, K) - - if factors is None: - factors = dup_zz_zassenhaus(g, K) - - return cont, _sort_factors(factors, multiple=False) - -
    -@cythonized("n,k") -
    [docs]def dup_zz_factor(f, K): - """ - Factor (non square-free) polynomials in `Z[x]`. - - Given a univariate polynomial `f` in `Z[x]` computes its complete - factorization `f_1, ..., f_n` into irreducibles over integers:: - - f = content(f) f_1**k_1 ... f_n**k_n - - The factorization is computed by reducing the input polynomial - into a primitive square-free polynomial and factoring it using - Zassenhaus algorithm. Trial division is used to recover the - multiplicities of factors. - - The result is returned as a tuple consisting of:: - - (content(f), [(f_1, k_1), ..., (f_n, k_n)) - - Consider polynomial `f = 2*x**4 - 2`:: - - >>> from sympy.polys.factortools import dup_zz_factor - >>> from sympy.polys.domains import ZZ - - >>> dup_zz_factor([2, 0, 0, 0, -2], ZZ) - (2, [([1, -1], 1), ([1, 1], 1), ([1, 0, 1], 1)]) - - In result we got the following factorization:: - - f = 2 (x - 1) (x + 1) (x**2 + 1) - - Note that this is a complete factorization over integers, - however over Gaussian integers we can factor the last term. - - By default, polynomials `x**n - 1` and `x**n + 1` are factored - using cyclotomic decomposition to speedup computations. To - disable this behaviour set cyclotomic=False. - - References - ========== - - 1. [Gathen99]_ - - """ - cont, g = dup_primitive(f, K) - - n = dup_degree(g) - - if dup_LC(g, K) < 0: - cont, g = -cont, dup_neg(g, K) - - if n <= 0: - return cont, [] - elif n == 1: - return cont, [(g, 1)] - - if query('USE_IRREDUCIBLE_IN_FACTOR'): - if dup_zz_irreducible_p(g, K): - return cont, [(g, 1)] - - g = dup_sqf_part(g, K) - H, factors = None, [] - - if query('USE_CYCLOTOMIC_FACTOR'): - H = dup_zz_cyclotomic_factor(g, K) - - if H is None: - H = dup_zz_zassenhaus(g, K) - - for h in H: - k = 0 - - while True: - q, r = dup_div(f, h, K) - - if not r: - f, k = q, k + 1 - else: - break - - factors.append((h, k)) - - return cont, _sort_factors(factors) - -
    -
    [docs]def dmp_zz_wang_non_divisors(E, cs, ct, K): - """Wang/EEZ: Compute a set of valid divisors. """ - result = [ cs*ct ] - - for q in E: - q = abs(q) - - for r in reversed(result): - while r != 1: - r = K.gcd(r, q) - q = q // r - - if K.is_one(q): - return None - - result.append(q) - - return result[1:] - -
    -@cythonized("u,v") -
    [docs]def dmp_zz_wang_test_points(f, T, ct, A, u, K): - """Wang/EEZ: Test evaluation points for suitability. """ - if not dmp_eval_tail(dmp_LC(f, K), A, u - 1, K): - raise EvaluationFailed('no luck') - - g = dmp_eval_tail(f, A, u, K) - - if not dup_sqf_p(g, K): - raise EvaluationFailed('no luck') - - c, h = dup_primitive(g, K) - - if K.is_negative(dup_LC(h, K)): - c, h = -c, dup_neg(h, K) - - v = u - 1 - - E = [ dmp_eval_tail(t, A, v, K) for t, _ in T ] - D = dmp_zz_wang_non_divisors(E, c, ct, K) - - if D is not None: - return c, h, E - else: - raise EvaluationFailed('no luck') - -
    -@cythonized("u,v,i,j,k") -
    [docs]def dmp_zz_wang_lead_coeffs(f, T, cs, E, H, A, u, K): - """Wang/EEZ: Compute correct leading coefficients. """ - C, J, v = [], [0]*len(E), u - 1 - - for h in H: - c = dmp_one(v, K) - d = dup_LC(h, K)*cs - - for i in reversed(range(len(E))): - k, e, (t, _) = 0, E[i], T[i] - - while not (d % e): - d, k = d//e, k + 1 - - if k != 0: - c, J[i] = dmp_mul(c, dmp_pow(t, k, v, K), v, K), 1 - - C.append(c) - - if any(not j for j in J): - raise ExtraneousFactors # pragma: no cover - - CC, HH = [], [] - - for c, h in zip(C, H): - d = dmp_eval_tail(c, A, v, K) - lc = dup_LC(h, K) - - if K.is_one(cs): - cc = lc//d - else: - g = K.gcd(lc, d) - d, cc = d//g, lc//g - h, cs = dup_mul_ground(h, d, K), cs//d - - c = dmp_mul_ground(c, cc, v, K) - - CC.append(c) - HH.append(h) - - if K.is_one(cs): - return f, HH, CC - - CCC, HHH = [], [] - - for c, h in zip(CC, HH): - CCC.append(dmp_mul_ground(c, cs, v, K)) - HHH.append(dmp_mul_ground(h, cs, 0, K)) - - f = dmp_mul_ground(f, cs**(len(H) - 1), u, K) - - return f, HHH, CCC - -
    -@cythonized("m") -def dup_zz_diophantine(F, m, p, K): - """Wang/EEZ: Solve univariate Diophantine equations. """ - if len(F) == 2: - a, b = F - - f = gf_from_int_poly(a, p) - g = gf_from_int_poly(b, p) - - s, t, G = gf_gcdex(g, f, p, K) - - s = gf_lshift(s, m, K) - t = gf_lshift(t, m, K) - - q, s = gf_div(s, f, p, K) - - t = gf_add_mul(t, q, g, p, K) - - s = gf_to_int_poly(s, p) - t = gf_to_int_poly(t, p) - - result = [s, t] - else: - G = [F[-1]] - - for f in reversed(F[1:-1]): - G.insert(0, dup_mul(f, G[0], K)) - - S, T = [], [[1]] - - for f, g in zip(F, G): - t, s = dmp_zz_diophantine([g, f], T[-1], [], 0, p, 1, K) - T.append(t) - S.append(s) - - result, S = [], S + [T[-1]] - - for s, f in zip(S, F): - s = gf_from_int_poly(s, p) - f = gf_from_int_poly(f, p) - - r = gf_rem(gf_lshift(s, m, K), f, p, K) - s = gf_to_int_poly(r, p) - - result.append(s) - - return result - - -@cythonized("u,v,d,n,i,j,k") -
    [docs]def dmp_zz_diophantine(F, c, A, d, p, u, K): - """Wang/EEZ: Solve multivariate Diophantine equations. """ - if not A: - S = [ [] for _ in F ] - n = dup_degree(c) - - for i, coeff in enumerate(c): - if not coeff: - continue - - T = dup_zz_diophantine(F, n - i, p, K) - - for j, (s, t) in enumerate(list(zip(S, T))): - t = dup_mul_ground(t, coeff, K) - S[j] = dup_trunc(dup_add(s, t, K), p, K) - else: - n = len(A) - e = dmp_expand(F, u, K) - - a, A = A[-1], A[:-1] - B, G = [], [] - - for f in F: - B.append(dmp_quo(e, f, u, K)) - G.append(dmp_eval_in(f, a, n, u, K)) - - C = dmp_eval_in(c, a, n, u, K) - - v = u - 1 - - S = dmp_zz_diophantine(G, C, A, d, p, v, K) - S = [ dmp_raise(s, 1, v, K) for s in S ] - - for s, b in zip(S, B): - c = dmp_sub_mul(c, s, b, u, K) - - c = dmp_ground_trunc(c, p, u, K) - - m = dmp_nest([K.one, -a], n, K) - M = dmp_one(n, K) - - for k in K.map(range(0, d)): - if dmp_zero_p(c, u): - break - - M = dmp_mul(M, m, u, K) - C = dmp_diff_eval_in(c, k + 1, a, n, u, K) - - if not dmp_zero_p(C, v): - C = dmp_quo_ground(C, K.factorial(k + 1), v, K) - T = dmp_zz_diophantine(G, C, A, d, p, v, K) - - for i, t in enumerate(T): - T[i] = dmp_mul(dmp_raise(t, 1, v, K), M, u, K) - - for i, (s, t) in enumerate(list(zip(S, T))): - S[i] = dmp_add(s, t, u, K) - - for t, b in zip(T, B): - c = dmp_sub_mul(c, t, b, u, K) - - c = dmp_ground_trunc(c, p, u, K) - - S = [ dmp_ground_trunc(s, p, u, K) for s in S ] - - return S - -
    -@cythonized("u,v,d,dj,n,i,j,k,w") -
    [docs]def dmp_zz_wang_hensel_lifting(f, H, LC, A, p, u, K): - """Wang/EEZ: Parallel Hensel lifting algorithm. """ - S, n, v = [f], len(A), u - 1 - - H = list(H) - - for i, a in enumerate(reversed(A[1:])): - s = dmp_eval_in(S[0], a, n - i, u - i, K) - S.insert(0, dmp_ground_trunc(s, p, v - i, K)) - - d = max(dmp_degree_list(f, u)[1:]) - - for j, s, a in zip(range(2, n + 2), S, A): - G, w = list(H), j - 1 - - I, J = A[:j - 2], A[j - 1:] - - for i, (h, lc) in enumerate(list(zip(H, LC))): - lc = dmp_ground_trunc(dmp_eval_tail(lc, J, v, K), p, w - 1, K) - H[i] = [lc] + dmp_raise(h[1:], 1, w - 1, K) - - m = dmp_nest([K.one, -a], w, K) - M = dmp_one(w, K) - - c = dmp_sub(s, dmp_expand(H, w, K), w, K) - - dj = dmp_degree_in(s, w, w) - - for k in K.map(range(0, dj)): - if dmp_zero_p(c, w): - break - - M = dmp_mul(M, m, w, K) - C = dmp_diff_eval_in(c, k + 1, a, w, w, K) - - if not dmp_zero_p(C, w - 1): - C = dmp_quo_ground(C, K.factorial(k + 1), w - 1, K) - T = dmp_zz_diophantine(G, C, I, d, p, w - 1, K) - - for i, (h, t) in enumerate(list(zip(H, T))): - h = dmp_add_mul(h, dmp_raise(t, 1, w - 1, K), M, w, K) - H[i] = dmp_ground_trunc(h, p, w, K) - - h = dmp_sub(s, dmp_expand(H, w, K), w, K) - c = dmp_ground_trunc(h, p, w, K) - - if dmp_expand(H, u, K) != f: - raise ExtraneousFactors # pragma: no cover - else: - return H - -
    -@cythonized("u,mod,i,j,s_arg,negative") -
    [docs]def dmp_zz_wang(f, u, K, mod=None, seed=None): - """ - Factor primitive square-free polynomials in `Z[X]`. - - Given a multivariate polynomial `f` in `Z[x_1,...,x_n]`, which is - primitive and square-free in `x_1`, computes factorization of `f` into - irreducibles over integers. - - The procedure is based on Wang's Enhanced Extended Zassenhaus - algorithm. The algorithm works by viewing `f` as a univariate polynomial - in `Z[x_2,...,x_n][x_1]`, for which an evaluation mapping is computed:: - - x_2 -> a_2, ..., x_n -> a_n - - where `a_i`, for `i = 2, ..., n`, are carefully chosen integers. The - mapping is used to transform `f` into a univariate polynomial in `Z[x_1]`, - which can be factored efficiently using Zassenhaus algorithm. The last - step is to lift univariate factors to obtain true multivariate - factors. For this purpose a parallel Hensel lifting procedure is used. - - The parameter ``seed`` is passed to _randint and can be used to seed randint - (when an integer) or (for testing purposes) can be a sequence of numbers. - - References - ========== - - 1. [Wang78]_ - 2. [Geddes92]_ - - """ - from sympy.utilities.randtest import _randint - - randint = _randint(seed) - - ct, T = dmp_zz_factor(dmp_LC(f, K), u - 1, K) - - b = dmp_zz_mignotte_bound(f, u, K) - p = K(nextprime(b)) - - if mod is None: - if u == 1: - mod = 2 - else: - mod = 1 - - history, configs, A, r = set([]), [], [K.zero]*u, None - - try: - cs, s, E = dmp_zz_wang_test_points(f, T, ct, A, u, K) - - _, H = dup_zz_factor_sqf(s, K) - - r = len(H) - - if r == 1: - return [f] - - configs = [(s, cs, E, H, A)] - except EvaluationFailed: - pass - - eez_num_configs = query('EEZ_NUMBER_OF_CONFIGS') - eez_num_tries = query('EEZ_NUMBER_OF_TRIES') - eez_mod_step = query('EEZ_MODULUS_STEP') - - while len(configs) < eez_num_configs: - for _ in range(eez_num_tries): - A = [ K(randint(-mod, mod)) for _ in range(u) ] - - if tuple(A) not in history: - history.add(tuple(A)) - else: - continue - - try: - cs, s, E = dmp_zz_wang_test_points(f, T, ct, A, u, K) - except EvaluationFailed: - continue - - _, H = dup_zz_factor_sqf(s, K) - - rr = len(H) - - if r is not None: - if rr != r: # pragma: no cover - if rr < r: - configs, r = [], rr - else: - continue - else: - r = rr - - if r == 1: - return [f] - - configs.append((s, cs, E, H, A)) - - if len(configs) == eez_num_configs: - break - else: - mod += eez_mod_step - - s_norm, s_arg, i = None, 0, 0 - - for s, _, _, _, _ in configs: - _s_norm = dup_max_norm(s, K) - - if s_norm is not None: - if _s_norm < s_norm: - s_norm = _s_norm - s_arg = i - else: - s_norm = _s_norm - - i += 1 - - _, cs, E, H, A = configs[s_arg] - orig_f = f - - try: - f, H, LC = dmp_zz_wang_lead_coeffs(f, T, cs, E, H, A, u, K) - factors = dmp_zz_wang_hensel_lifting(f, H, LC, A, p, u, K) - except ExtraneousFactors: # pragma: no cover - if query('EEZ_RESTART_IF_NEEDED'): - return dmp_zz_wang(orig_f, u, K, mod + 1) - else: - raise ExtraneousFactors( - "we need to restart algorithm with better parameters") - - negative, result = 0, [] - - for f in factors: - _, f = dmp_ground_primitive(f, u, K) - - if K.is_negative(dmp_ground_LC(f, u, K)): - f = dmp_neg(f, u, K) - - result.append(f) - - return result - -
    -@cythonized("u,d,k") -
    [docs]def dmp_zz_factor(f, u, K): - """ - Factor (non square-free) polynomials in `Z[X]`. - - Given a multivariate polynomial `f` in `Z[x]` computes its complete - factorization `f_1, ..., f_n` into irreducibles over integers:: - - f = content(f) f_1**k_1 ... f_n**k_n - - The factorization is computed by reducing the input polynomial - into a primitive square-free polynomial and factoring it using - Enhanced Extended Zassenhaus (EEZ) algorithm. Trial division - is used to recover the multiplicities of factors. - - The result is returned as a tuple consisting of:: - - (content(f), [(f_1, k_1), ..., (f_n, k_n)) - - Consider polynomial `f = 2*(x**2 - y**2)`:: - - >>> from sympy.polys.factortools import dmp_zz_factor - >>> from sympy.polys.domains import ZZ - - >>> dmp_zz_factor([[2], [], [-2, 0, 0]], 1, ZZ) - (2, [([[1], [-1, 0]], 1), ([[1], [1, 0]], 1)]) - - In result we got the following factorization:: - - f = 2 (x - y) (x + y) - - References - ========== - - 1. [Gathen99]_ - - """ - if not u: - return dup_zz_factor(f, K) - - if dmp_zero_p(f, u): - return K.zero, [] - - cont, g = dmp_ground_primitive(f, u, K) - - if dmp_ground_LC(g, u, K) < 0: - cont, g = -cont, dmp_neg(g, u, K) - - if all(d <= 0 for d in dmp_degree_list(g, u)): - return cont, [] - - G, g = dmp_primitive(g, u, K) - - factors = [] - - if dmp_degree(g, u) > 0: - g = dmp_sqf_part(g, u, K) - H = dmp_zz_wang(g, u, K) - - for h in H: - k = 0 - - while True: - q, r = dmp_div(f, h, u, K) - - if dmp_zero_p(r, u): - f, k = q, k + 1 - else: - break - - factors.append((h, k)) - - for g, k in dmp_zz_factor(G, u - 1, K)[1]: - factors.insert(0, ([g], k)) - - return cont, _sort_factors(factors) - -
    -def dup_ext_factor(f, K): - """Factor univariate polynomials over algebraic number fields. """ - n, lc = dup_degree(f), dup_LC(f, K) - - f = dup_monic(f, K) - - if n <= 0: - return lc, [] - if n == 1: - return lc, [(f, 1)] - - f, F = dup_sqf_part(f, K), f - s, g, r = dup_sqf_norm(f, K) - - factors = dup_factor_list_include(r, K.dom) - - if len(factors) == 1: - return lc, [(f, n//dup_degree(f))] - - H = s*K.unit - - for i, (factor, _) in enumerate(factors): - h = dup_convert(factor, K.dom, K) - h, _, g = dup_inner_gcd(h, g, K) - h = dup_shift(h, H, K) - factors[i] = h - - factors = dup_trial_division(F, factors, K) - - return lc, factors - - -@cythonized("u") -
    [docs]def dmp_ext_factor(f, u, K): - """Factor multivariate polynomials over algebraic number fields. """ - if not u: - return dup_ext_factor(f, K) - - lc = dmp_ground_LC(f, u, K) - f = dmp_ground_monic(f, u, K) - - if all(d <= 0 for d in dmp_degree_list(f, u)): - return lc, [] - - f, F = dmp_sqf_part(f, u, K), f - s, g, r = dmp_sqf_norm(f, u, K) - - factors = dmp_factor_list_include(r, u, K.dom) - - if len(factors) == 1: - coeff, factors = lc, [f] - else: - H = dmp_raise([K.one, s*K.unit], u, 0, K) - - for i, (factor, _) in enumerate(factors): - h = dmp_convert(factor, u, K.dom, K) - h, _, g = dmp_inner_gcd(h, g, u, K) - h = dmp_compose(h, H, u, K) - factors[i] = h - - return lc, dmp_trial_division(F, factors, u, K) - -
    -@cythonized("i") -
    [docs]def dup_gf_factor(f, K): - """Factor univariate polynomials over finite fields. """ - f = dup_convert(f, K, K.dom) - - coeff, factors = gf_factor(f, K.mod, K.dom) - - for i, (f, k) in enumerate(factors): - factors[i] = (dup_convert(f, K.dom, K), k) - - return K.convert(coeff, K.dom), factors - -
    -def dmp_gf_factor(f, u, K): - """Factor multivariate polynomials over finite fields. """ - raise DomainError('multivariate polynomials over %s' % K) - - -@cythonized("i,k,u") -def dup_factor_list(f, K0): - """Factor polynomials into irreducibles in `K[x]`. """ - j, f = dup_terms_gcd(f, K0) - - if not K0.has_CharacteristicZero: - coeff, factors = dup_gf_factor(f, K0) - elif K0.is_Algebraic: - coeff, factors = dup_ext_factor(f, K0) - else: - if not K0.is_Exact: - K0_inexact, K0 = K0, K0.get_exact() - f = dup_convert(f, K0_inexact, K0) - else: - K0_inexact = None - - if K0.has_Field: - K = K0.get_ring() - - denom, f = dup_clear_denoms(f, K0, K) - f = dup_convert(f, K0, K) - else: - K = K0 - - if K.is_ZZ: - coeff, factors = dup_zz_factor(f, K) - elif K.is_Poly: - f, u = dmp_inject(f, 0, K) - - coeff, factors = dmp_factor_list(f, u, K.dom) - - for i, (f, k) in enumerate(factors): - factors[i] = (dmp_eject(f, u, K), k) - - coeff = K.convert(coeff, K.dom) - else: # pragma: no cover - raise DomainError('factorization not supported over %s' % K0) - - if K0.has_Field: - for i, (f, k) in enumerate(factors): - factors[i] = (dup_convert(f, K, K0), k) - - coeff = K0.convert(coeff, K) - denom = K0.convert(denom, K) - - coeff = K0.quo(coeff, denom) - - if K0_inexact is not None: - for i, (f, k) in enumerate(factors): - factors[i] = (dup_convert(f, K0, K0_inexact), k) - - coeff = K0_inexact.convert(coeff, K0) - - if j: - factors.insert(0, ([K0.one, K0.zero], j)) - - return coeff, _sort_factors(factors) - - -def dup_factor_list_include(f, K): - """Factor polynomials into irreducibles in `K[x]`. """ - coeff, factors = dup_factor_list(f, K) - - if not factors: - return [(dup_strip([coeff]), 1)] - else: - g = dup_mul_ground(factors[0][0], coeff, K) - return [(g, factors[0][1])] + factors[1:] - - -@cythonized("u,v,i,k") -
    [docs]def dmp_factor_list(f, u, K0): - """Factor polynomials into irreducibles in `K[X]`. """ - if not u: - return dup_factor_list(f, K0) - - J, f = dmp_terms_gcd(f, u, K0) - - if not K0.has_CharacteristicZero: # pragma: no cover - coeff, factors = dmp_gf_factor(f, u, K0) - elif K0.is_Algebraic: - coeff, factors = dmp_ext_factor(f, u, K0) - else: - if not K0.is_Exact: - K0_inexact, K0 = K0, K0.get_exact() - f = dmp_convert(f, u, K0_inexact, K0) - else: - K0_inexact = None - - if K0.has_Field: - K = K0.get_ring() - - denom, f = dmp_clear_denoms(f, u, K0, K) - f = dmp_convert(f, u, K0, K) - else: - K = K0 - - if K.is_ZZ: - levels, f, v = dmp_exclude(f, u, K) - coeff, factors = dmp_zz_factor(f, v, K) - - for i, (f, k) in enumerate(factors): - factors[i] = (dmp_include(f, levels, v, K), k) - elif K.is_Poly: - f, v = dmp_inject(f, u, K) - - coeff, factors = dmp_factor_list(f, v, K.dom) - - for i, (f, k) in enumerate(factors): - factors[i] = (dmp_eject(f, v, K), k) - - coeff = K.convert(coeff, K.dom) - else: # pragma: no cover - raise DomainError('factorization not supported over %s' % K0) - - if K0.has_Field: - for i, (f, k) in enumerate(factors): - factors[i] = (dmp_convert(f, u, K, K0), k) - - coeff = K0.convert(coeff, K) - denom = K0.convert(denom, K) - - coeff = K0.quo(coeff, denom) - - if K0_inexact is not None: - for i, (f, k) in enumerate(factors): - factors[i] = (dmp_convert(f, u, K0, K0_inexact), k) - - coeff = K0_inexact.convert(coeff, K0) - - for i, j in enumerate(reversed(J)): - if not j: - continue - - term = {(0,)*(u - i) + (1,) + (0,)*i: K0.one} - factors.insert(0, (dmp_from_dict(term, u, K0), j)) - - return coeff, _sort_factors(factors) - -
    -@cythonized("u") -
    [docs]def dmp_factor_list_include(f, u, K): - """Factor polynomials into irreducibles in `K[X]`. """ - if not u: - return dup_factor_list_include(f, K) - - coeff, factors = dmp_factor_list(f, u, K) - - if not factors: - return [(dmp_ground(coeff, u), 1)] - else: - g = dmp_mul_ground(factors[0][0], coeff, u, K) - return [(g, factors[0][1])] + factors[1:] - -
    -def dup_irreducible_p(f, K): - """Returns ``True`` if ``f`` has no factors over its domain. """ - return dmp_irreducible_p(f, 0, K) - - -
    [docs]def dmp_irreducible_p(f, u, K): - """Returns ``True`` if ``f`` has no factors over its domain. """ - _, factors = dmp_factor_list(f, u, K) - - if not factors: - return True - elif len(factors) > 1: - return False - else: - _, k = factors[0] - return k == 1
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/polys/galoistools.html b/dev-py3k/_modules/sympy/polys/galoistools.html deleted file mode 100644 index 8289edf25c3..00000000000 --- a/dev-py3k/_modules/sympy/polys/galoistools.html +++ /dev/null @@ -1,2406 +0,0 @@ - - - - - - - - - - sympy.polys.galoistools — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.polys.galoistools

    -"""Dense univariate polynomials with coefficients in Galois fields. """
    -
    -from random import uniform
    -from math import ceil as _ceil, sqrt as _sqrt
    -
    -from sympy.core.mul import prod
    -from sympy.polys.polyutils import _sort_factors
    -from sympy.polys.polyconfig import query
    -
    -from sympy.polys.polyerrors import ExactQuotientFailed
    -
    -from sympy.utilities import cythonized
    -
    -from sympy.ntheory import factorint
    -
    -
    -
    [docs]def gf_crt(U, M, K=None): - """ - Chinese Remainder Theorem. - - Given a set of integer residues ``u_0,...,u_n`` and a set of - co-prime integer moduli ``m_0,...,m_n``, returns an integer - ``u``, such that ``u = u_i mod m_i`` for ``i = ``0,...,n``. - - As an example consider a set of residues ``U = [49, 76, 65]`` - and a set of moduli ``M = [99, 97, 95]``. Then we have:: - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_crt - >>> from sympy.ntheory.modular import solve_congruence - - >>> gf_crt([49, 76, 65], [99, 97, 95], ZZ) - 639985 - - This is the correct result because:: - - >>> [639985 % m for m in [99, 97, 95]] - [49, 76, 65] - - Note: this is a low-level routine with no error checking. - - See Also - ======== - - sympy.ntheory.modular.crt : a higher level crt routine - sympy.ntheory.modular.solve_congruence - - """ - p = prod(M, start=K.one) - v = K.zero - - for u, m in zip(U, M): - e = p // m - s, _, _ = K.gcdex(e, m) - v += e*(u*s % m) - - return v % p - -
    -
    [docs]def gf_crt1(M, K): - """ - First part of the Chinese Remainder Theorem. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_crt1 - - >>> gf_crt1([99, 97, 95], ZZ) - (912285, [9215, 9405, 9603], [62, 24, 12]) - - """ - E, S = [], [] - p = prod(M, start=K.one) - - for m in M: - E.append(p // m) - S.append(K.gcdex(E[-1], m)[0] % m) - - return p, E, S - -
    -
    [docs]def gf_crt2(U, M, p, E, S, K): - """ - Second part of the Chinese Remainder Theorem. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_crt2 - - >>> U = [49, 76, 65] - >>> M = [99, 97, 95] - >>> p = 912285 - >>> E = [9215, 9405, 9603] - >>> S = [62, 24, 12] - - >>> gf_crt2(U, M, p, E, S, ZZ) - 639985 - - """ - v = K.zero - - for u, m, e, s in zip(U, M, E, S): - v += e*(u*s % m) - - return v % p - -
    -
    [docs]def gf_int(a, p): - """ - Coerce ``a mod p`` to an integer in the range ``[-p/2, p/2]``. - - Examples - ======== - - >>> from sympy.polys.galoistools import gf_int - - >>> gf_int(2, 7) - 2 - >>> gf_int(5, 7) - -2 - - """ - if a <= p // 2: - return a - else: - return a - p - -
    -
    [docs]def gf_degree(f): - """ - Return the leading degree of ``f``. - - Examples - ======== - - >>> from sympy.polys.galoistools import gf_degree - - >>> gf_degree([1, 1, 2, 0]) - 3 - >>> gf_degree([]) - -1 - - """ - return len(f) - 1 - -
    -
    [docs]def gf_LC(f, K): - """ - Return the leading coefficient of ``f``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_LC - - >>> gf_LC([3, 0, 1], ZZ) - 3 - - """ - if not f: - return K.zero - else: - return f[0] - -
    -
    [docs]def gf_TC(f, K): - """ - Return the trailing coefficient of ``f``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_TC - - >>> gf_TC([3, 0, 1], ZZ) - 1 - - """ - if not f: - return K.zero - else: - return f[-1] - -
    -@cythonized("k") -
    [docs]def gf_strip(f): - """ - Remove leading zeros from ``f``. - - - Examples - ======== - - >>> from sympy.polys.galoistools import gf_strip - - >>> gf_strip([0, 0, 0, 3, 0, 1]) - [3, 0, 1] - - """ - if not f or f[0]: - return f - - k = 0 - - for coeff in f: - if coeff: - break - else: - k += 1 - - return f[k:] - -
    -
    [docs]def gf_trunc(f, p): - """ - Reduce all coefficients modulo ``p``. - - Examples - ======== - - >>> from sympy.polys.galoistools import gf_trunc - - >>> gf_trunc([7, -2, 3], 5) - [2, 3, 3] - - """ - return gf_strip([ a % p for a in f ]) - -
    -
    [docs]def gf_normal(f, p, K): - """ - Normalize all coefficients in ``K``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_normal - - >>> gf_normal([5, 10, 21, -3], 5, ZZ) - [1, 2] - - """ - return gf_trunc(list(map(K, f)), p) - -
    -
    [docs]def gf_convert(f, p, K0, K1): - """ - Normalize all coefficients in ``K``. - - - Examples - ======== - - >>> from sympy.polys.domains import ZZ, QQ - >>> from sympy.polys.galoistools import gf_convert - - >>> gf_convert([QQ(1), QQ(4), QQ(0)], 3, QQ, ZZ) - [1, 1, 0] - - """ - return gf_trunc([ K1.convert(c, K0) for c in f ], p) - -
    -@cythonized("k,n") -
    [docs]def gf_from_dict(f, p, K): - """ - Create a ``GF(p)[x]`` polynomial from a dict. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_from_dict - - >>> gf_from_dict({10: ZZ(4), 4: ZZ(33), 0: ZZ(-1)}, 5, ZZ) - [4, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4] - - """ - n, h = max(f.keys()), [] - - if type(n) is int: - for k in range(n, -1, -1): - h.append(f.get(k, K.zero) % p) - else: - (n,) = n - - for k in range(n, -1, -1): - h.append(f.get((k,), K.zero) % p) - - return gf_trunc(h, p) - -
    -@cythonized("k,n") -
    [docs]def gf_to_dict(f, p, symmetric=True): - """ - Convert a ``GF(p)[x]`` polynomial to a dict. - - Examples - ======== - - >>> from sympy.polys.galoistools import gf_to_dict - - >>> gf_to_dict([4, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4], 5) - {0: -1, 4: -2, 10: -1} - >>> gf_to_dict([4, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4], 5, symmetric=False) - {0: 4, 4: 3, 10: 4} - - """ - n, result = gf_degree(f), {} - - for k in range(0, n + 1): - if symmetric: - a = gf_int(f[n - k], p) - else: - a = f[n - k] - - if a: - result[k] = a - - return result - -
    -
    [docs]def gf_from_int_poly(f, p): - """ - Create a ``GF(p)[x]`` polynomial from ``Z[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_from_int_poly - - >>> gf_from_int_poly([7, -2, 3], 5) - [2, 3, 3] - - """ - return gf_trunc(f, p) - -
    -
    [docs]def gf_to_int_poly(f, p, symmetric=True): - """ - Convert a ``GF(p)[x]`` polynomial to ``Z[x]``. - - - Examples - ======== - - >>> from sympy.polys.galoistools import gf_to_int_poly - - >>> gf_to_int_poly([2, 3, 3], 5) - [2, -2, -2] - >>> gf_to_int_poly([2, 3, 3], 5, symmetric=False) - [2, 3, 3] - - """ - if symmetric: - return [ gf_int(c, p) for c in f ] - else: - return f - -
    -
    [docs]def gf_neg(f, p, K): - """ - Negate a polynomial in ``GF(p)[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_neg - - >>> gf_neg([3, 2, 1, 0], 5, ZZ) - [2, 3, 4, 0] - - """ - return [ -coeff % p for coeff in f ] - -
    -
    [docs]def gf_add_ground(f, a, p, K): - """ - Compute ``f + a`` where ``f`` in ``GF(p)[x]`` and ``a`` in ``GF(p)``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_add_ground - - >>> gf_add_ground([3, 2, 4], 2, 5, ZZ) - [3, 2, 1] - - """ - if not f: - a = a % p - else: - a = (f[-1] + a) % p - - if len(f) > 1: - return f[:-1] + [a] - - if not a: - return [] - else: - return [a] - -
    -
    [docs]def gf_sub_ground(f, a, p, K): - """ - Compute ``f - a`` where ``f`` in ``GF(p)[x]`` and ``a`` in ``GF(p)``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_sub_ground - - >>> gf_sub_ground([3, 2, 4], 2, 5, ZZ) - [3, 2, 2] - - """ - if not f: - a = -a % p - else: - a = (f[-1] - a) % p - - if len(f) > 1: - return f[:-1] + [a] - - if not a: - return [] - else: - return [a] - -
    -
    [docs]def gf_mul_ground(f, a, p, K): - """ - Compute ``f * a`` where ``f`` in ``GF(p)[x]`` and ``a`` in ``GF(p)``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_mul_ground - - >>> gf_mul_ground([3, 2, 4], 2, 5, ZZ) - [1, 4, 3] - - """ - if not a: - return [] - else: - return [ (a*b) % p for b in f ] - -
    -
    [docs]def gf_quo_ground(f, a, p, K): - """ - Compute ``f/a`` where ``f`` in ``GF(p)[x]`` and ``a`` in ``GF(p)``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_quo_ground - - >>> gf_quo_ground(ZZ.map([3, 2, 4]), ZZ(2), 5, ZZ) - [4, 1, 2] - - """ - return gf_mul_ground(f, K.invert(a, p), p, K) - -
    -@cythonized("df,dg,k") -
    [docs]def gf_add(f, g, p, K): - """ - Add polynomials in ``GF(p)[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_add - - >>> gf_add([3, 2, 4], [2, 2, 2], 5, ZZ) - [4, 1] - - """ - if not f: - return g - if not g: - return f - - df = gf_degree(f) - dg = gf_degree(g) - - if df == dg: - return gf_strip([ (a + b) % p for a, b in zip(f, g) ]) - else: - k = abs(df - dg) - - if df > dg: - h, f = f[:k], f[k:] - else: - h, g = g[:k], g[k:] - - return h + [ (a + b) % p for a, b in zip(f, g) ] - -
    -@cythonized("df,dg,k") -
    [docs]def gf_sub(f, g, p, K): - """ - Subtract polynomials in ``GF(p)[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_sub - - >>> gf_sub([3, 2, 4], [2, 2, 2], 5, ZZ) - [1, 0, 2] - - """ - if not g: - return f - if not f: - return gf_neg(g, p, K) - - df = gf_degree(f) - dg = gf_degree(g) - - if df == dg: - return gf_strip([ (a - b) % p for a, b in zip(f, g) ]) - else: - k = abs(df - dg) - - if df > dg: - h, f = f[:k], f[k:] - else: - h, g = gf_neg(g[:k], p, K), g[k:] - - return h + [ (a - b) % p for a, b in zip(f, g) ] - -
    -@cythonized("df,dg,dh,i,j") -
    [docs]def gf_mul(f, g, p, K): - """ - Multiply polynomials in ``GF(p)[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_mul - - >>> gf_mul([3, 2, 4], [2, 2, 2], 5, ZZ) - [1, 0, 3, 2, 3] - - """ - df = gf_degree(f) - dg = gf_degree(g) - - dh = df + dg - h = [0]*(dh + 1) - - for i in range(0, dh + 1): - coeff = K.zero - - for j in range(max(0, i - dg), min(i, df) + 1): - coeff += f[j]*g[i - j] - - h[i] = coeff % p - - return gf_strip(h) - -
    -@cythonized("df,dh,i,j,jmin,jmax,n") -
    [docs]def gf_sqr(f, p, K): - """ - Square polynomials in ``GF(p)[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_sqr - - >>> gf_sqr([3, 2, 4], 5, ZZ) - [4, 2, 3, 1, 1] - - """ - df = gf_degree(f) - - dh = 2*df - h = [0]*(dh + 1) - - for i in range(0, dh + 1): - coeff = K.zero - - jmin = max(0, i - df) - jmax = min(i, df) - - n = jmax - jmin + 1 - - jmax = jmin + n // 2 - 1 - - for j in range(jmin, jmax + 1): - coeff += f[j]*f[i - j] - - coeff += coeff - - if n & 1: - elem = f[jmax + 1] - coeff += elem**2 - - h[i] = coeff % p - - return gf_strip(h) - -
    -
    [docs]def gf_add_mul(f, g, h, p, K): - """ - Returns ``f + g*h`` where ``f``, ``g``, ``h`` in ``GF(p)[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_add_mul - >>> gf_add_mul([3, 2, 4], [2, 2, 2], [1, 4], 5, ZZ) - [2, 3, 2, 2] - """ - return gf_add(f, gf_mul(g, h, p, K), p, K) - -
    -
    [docs]def gf_sub_mul(f, g, h, p, K): - """ - Compute ``f - g*h`` where ``f``, ``g``, ``h`` in ``GF(p)[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_sub_mul - - >>> gf_sub_mul([3, 2, 4], [2, 2, 2], [1, 4], 5, ZZ) - [3, 3, 2, 1] - - """ - return gf_sub(f, gf_mul(g, h, p, K), p, K) - -
    -@cythonized("k") -
    [docs]def gf_expand(F, p, K): - """ - Expand results of :func:`factor` in ``GF(p)[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_expand - - >>> gf_expand([([3, 2, 4], 1), ([2, 2], 2), ([3, 1], 3)], 5, ZZ) - [4, 3, 0, 3, 0, 1, 4, 1] - - """ - if type(F) is tuple: - lc, F = F - else: - lc = K.one - - g = [lc] - - for f, k in F: - f = gf_pow(f, k, p, K) - g = gf_mul(g, f, p, K) - - return g - -
    -@cythonized("df,dg,dq,dr,i,j") -
    [docs]def gf_div(f, g, p, K): - """ - Division with remainder in ``GF(p)[x]``. - - Given univariate polynomials ``f`` and ``g`` with coefficients in a - finite field with ``p`` elements, returns polynomials ``q`` and ``r`` - (quotient and remainder) such that ``f = q*g + r``. - - Consider polynomials ``x**3 + x + 1`` and ``x**2 + x`` in GF(2):: - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_div, gf_add_mul - - >>> gf_div(ZZ.map([1, 0, 1, 1]), ZZ.map([1, 1, 0]), 2, ZZ) - ([1, 1], [1]) - - As result we obtained quotient ``x + 1`` and remainder ``1``, thus:: - - >>> gf_add_mul(ZZ.map([1]), ZZ.map([1, 1]), ZZ.map([1, 1, 0]), 2, ZZ) - [1, 0, 1, 1] - - References - ========== - - 1. [Monagan93]_ - 2. [Gathen99]_ - - """ - df = gf_degree(f) - dg = gf_degree(g) - - if not g: - raise ZeroDivisionError("polynomial division") - elif df < dg: - return [], f - - inv = K.invert(g[0], p) - - h, dq, dr = list(f), df - dg, dg - 1 - - for i in range(0, df + 1): - coeff = h[i] - - for j in range(max(0, dg - i), min(df - i, dr) + 1): - coeff -= h[i + j - dg] * g[dg - j] - - if i <= dq: - coeff *= inv - - h[i] = coeff % p - - return h[:dq + 1], gf_strip(h[dq + 1:]) - -
    -
    [docs]def gf_rem(f, g, p, K): - """ - Compute polynomial remainder in ``GF(p)[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_rem - - >>> gf_rem(ZZ.map([1, 0, 1, 1]), ZZ.map([1, 1, 0]), 2, ZZ) - [1] - - """ - return gf_div(f, g, p, K)[1] - -
    -@cythonized("df,dg,dq,dr,i,j") -
    [docs]def gf_quo(f, g, p, K): - """ - Compute exact quotient in ``GF(p)[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_quo - - >>> gf_quo(ZZ.map([1, 0, 1, 1]), ZZ.map([1, 1, 0]), 2, ZZ) - [1, 1] - >>> gf_quo(ZZ.map([1, 0, 3, 2, 3]), ZZ.map([2, 2, 2]), 5, ZZ) - [3, 2, 4] - - """ - df = gf_degree(f) - dg = gf_degree(g) - - if not g: - raise ZeroDivisionError("polynomial division") - elif df < dg: - return [] - - inv = K.invert(g[0], p) - - h, dq, dr = f[:], df - dg, dg - 1 - - for i in range(0, dq + 1): - coeff = h[i] - - for j in range(max(0, dg - i), min(df - i, dr) + 1): - coeff -= h[i + j - dg] * g[dg - j] - - h[i] = (coeff * inv) % p - - return h[:dq + 1] - -
    -
    [docs]def gf_exquo(f, g, p, K): - """ - Compute polynomial quotient in ``GF(p)[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_exquo - - >>> gf_exquo(ZZ.map([1, 0, 3, 2, 3]), ZZ.map([2, 2, 2]), 5, ZZ) - [3, 2, 4] - - >>> gf_exquo(ZZ.map([1, 0, 1, 1]), ZZ.map([1, 1, 0]), 2, ZZ) - Traceback (most recent call last): - ... - ExactQuotientFailed: [1, 1, 0] does not divide [1, 0, 1, 1] - - """ - q, r = gf_div(f, g, p, K) - - if not r: - return q - else: - raise ExactQuotientFailed(f, g) - -
    -@cythonized("n") -
    [docs]def gf_lshift(f, n, K): - """ - Efficiently multiply ``f`` by ``x**n``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_lshift - - >>> gf_lshift([3, 2, 4], 4, ZZ) - [3, 2, 4, 0, 0, 0, 0] - - """ - if not f: - return f - else: - return f + [K.zero]*n - -
    -@cythonized("n") -
    [docs]def gf_rshift(f, n, K): - """ - Efficiently divide ``f`` by ``x**n``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_rshift - - >>> gf_rshift([1, 2, 3, 4, 0], 3, ZZ) - ([1, 2], [3, 4, 0]) - - """ - if not n: - return f, [] - else: - return f[:-n], f[-n:] - -
    -
    [docs]def gf_pow(f, n, p, K): - """ - Compute ``f**n`` in ``GF(p)[x]`` using repeated squaring. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_pow - - >>> gf_pow([3, 2, 4], 3, 5, ZZ) - [2, 4, 4, 2, 2, 1, 4] - - """ - if not n: - return [K.one] - elif n == 1: - return f - elif n == 2: - return gf_sqr(f, p, K) - - h = [K.one] - - while True: - if n & 1: - h = gf_mul(h, f, p, K) - n -= 1 - - n >>= 1 - - if not n: - break - - f = gf_sqr(f, p, K) - - return h - -
    -
    [docs]def gf_pow_mod(f, n, g, p, K): - """ - Compute ``f**n`` in ``GF(p)[x]/(g)`` using repeated squaring. - - Given polynomials ``f`` and ``g`` in ``GF(p)[x]`` and a non-negative - integer ``n``, efficiently computes ``f**n (mod g)`` i.e. the remainder - of ``f**n`` from division by ``g``, using the repeated squaring algorithm. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_pow_mod - - >>> gf_pow_mod(ZZ.map([3, 2, 4]), 3, ZZ.map([1, 1]), 5, ZZ) - [] - - References - ========== - - 1. [Gathen99]_ - - """ - if not n: - return [K.one] - elif n == 1: - return gf_rem(f, g, p, K) - elif n == 2: - return gf_rem(gf_sqr(f, p, K), g, p, K) - - h = [K.one] - - while True: - if n & 1: - h = gf_mul(h, f, p, K) - h = gf_rem(h, g, p, K) - n -= 1 - - n >>= 1 - - if not n: - break - - f = gf_sqr(f, p, K) - f = gf_rem(f, g, p, K) - - return h - -
    -
    [docs]def gf_gcd(f, g, p, K): - """ - Euclidean Algorithm in ``GF(p)[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_gcd - - >>> gf_gcd(ZZ.map([3, 2, 4]), ZZ.map([2, 2, 3]), 5, ZZ) - [1, 3] - - """ - while g: - f, g = g, gf_rem(f, g, p, K) - - return gf_monic(f, p, K)[1] - -
    -
    [docs]def gf_lcm(f, g, p, K): - """ - Compute polynomial LCM in ``GF(p)[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_lcm - - >>> gf_lcm(ZZ.map([3, 2, 4]), ZZ.map([2, 2, 3]), 5, ZZ) - [1, 2, 0, 4] - - """ - if not f or not g: - return [] - - h = gf_quo(gf_mul(f, g, p, K), - gf_gcd(f, g, p, K), p, K) - - return gf_monic(h, p, K)[1] - -
    -
    [docs]def gf_cofactors(f, g, p, K): - """ - Compute polynomial GCD and cofactors in ``GF(p)[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_cofactors - - >>> gf_cofactors(ZZ.map([3, 2, 4]), ZZ.map([2, 2, 3]), 5, ZZ) - ([1, 3], [3, 3], [2, 1]) - - """ - if not f and not g: - return ([], [], []) - - h = gf_gcd(f, g, p, K) - - return (h, gf_quo(f, h, p, K), - gf_quo(g, h, p, K)) - -
    -
    [docs]def gf_gcdex(f, g, p, K): - """ - Extended Euclidean Algorithm in ``GF(p)[x]``. - - Given polynomials ``f`` and ``g`` in ``GF(p)[x]``, computes polynomials - ``s``, ``t`` and ``h``, such that ``h = gcd(f, g)`` and ``s*f + t*g = h``. - The typical application of EEA is solving polynomial diophantine equations. - - Consider polynomials ``f = (x + 7) (x + 1)``, ``g = (x + 7) (x**2 + 1)`` - in ``GF(11)[x]``. Application of Extended Euclidean Algorithm gives:: - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_gcdex, gf_mul, gf_add - - >>> s, t, g = gf_gcdex(ZZ.map([1, 8, 7]), ZZ.map([1, 7, 1, 7]), 11, ZZ) - >>> s, t, g - ([5, 6], [6], [1, 7]) - - As result we obtained polynomials ``s = 5*x + 6`` and ``t = 6``, and - additionally ``gcd(f, g) = x + 7``. This is correct because:: - - >>> S = gf_mul(s, ZZ.map([1, 8, 7]), 11, ZZ) - >>> T = gf_mul(t, ZZ.map([1, 7, 1, 7]), 11, ZZ) - - >>> gf_add(S, T, 11, ZZ) == [1, 7] - True - - References - ========== - - 1. [Gathen99]_ - - """ - if not (f or g): - return [K.one], [], [] - - p0, r0 = gf_monic(f, p, K) - p1, r1 = gf_monic(g, p, K) - - if not f: - return [], [K.invert(p1, p)], r1 - if not g: - return [K.invert(p0, p)], [], r0 - - s0, s1 = [K.invert(p0, p)], [] - t0, t1 = [], [K.invert(p1, p)] - - while True: - Q, R = gf_div(r0, r1, p, K) - - if not R: - break - - (lc, r1), r0 = gf_monic(R, p, K), r1 - - inv = K.invert(lc, p) - - s = gf_sub_mul(s0, s1, Q, p, K) - t = gf_sub_mul(t0, t1, Q, p, K) - - s1, s0 = gf_mul_ground(s, inv, p, K), s1 - t1, t0 = gf_mul_ground(t, inv, p, K), t1 - - return s1, t1, r1 - -
    -
    [docs]def gf_monic(f, p, K): - """ - Compute LC and a monic polynomial in ``GF(p)[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_monic - - >>> gf_monic(ZZ.map([3, 2, 4]), 5, ZZ) - (3, [1, 4, 3]) - - """ - if not f: - return K.zero, [] - else: - lc = f[0] - - if K.is_one(lc): - return lc, list(f) - else: - return lc, gf_quo_ground(f, lc, p, K) - -
    -@cythonized("df,n") -
    [docs]def gf_diff(f, p, K): - """ - Differentiate polynomial in ``GF(p)[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_diff - - >>> gf_diff([3, 2, 4], 5, ZZ) - [1, 2] - - """ - df = gf_degree(f) - - h, n = [K.zero]*df, df - - for coeff in f[:-1]: - coeff *= K(n) - coeff %= p - - if coeff: - h[df - n] = coeff - - n -= 1 - - return gf_strip(h) - -
    -
    [docs]def gf_eval(f, a, p, K): - """ - Evaluate ``f(a)`` in ``GF(p)`` using Horner scheme. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_eval - - >>> gf_eval([3, 2, 4], 2, 5, ZZ) - 0 - - """ - result = K.zero - - for c in f: - result *= a - result += c - result %= p - - return result - -
    -
    [docs]def gf_multi_eval(f, A, p, K): - """ - Evaluate ``f(a)`` for ``a`` in ``[a_1, ..., a_n]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_multi_eval - - >>> gf_multi_eval([3, 2, 4], [0, 1, 2, 3, 4], 5, ZZ) - [4, 4, 0, 2, 0] - - """ - return [ gf_eval(f, a, p, K) for a in A ] - -
    -
    [docs]def gf_compose(f, g, p, K): - """ - Compute polynomial composition ``f(g)`` in ``GF(p)[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_compose - - >>> gf_compose([3, 2, 4], [2, 2, 2], 5, ZZ) - [2, 4, 0, 3, 0] - - """ - if len(g) <= 1: - return gf_strip([gf_eval(f, gf_LC(g, K), p, K)]) - - if not f: - return [] - - h = [f[0]] - - for c in f[1:]: - h = gf_mul(h, g, p, K) - h = gf_add_ground(h, c, p, K) - - return h - -
    -
    [docs]def gf_compose_mod(g, h, f, p, K): - """ - Compute polynomial composition ``g(h)`` in ``GF(p)[x]/(f)``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_compose_mod - - >>> gf_compose_mod(ZZ.map([3, 2, 4]), ZZ.map([2, 2, 2]), ZZ.map([4, 3]), 5, ZZ) - [4] - - """ - if not g: - return [] - - comp = [g[0]] - - for a in g[1:]: - comp = gf_mul(comp, h, p, K) - comp = gf_add_ground(comp, a, p, K) - comp = gf_rem(comp, f, p, K) - - return comp - -
    -@cythonized("n") -
    [docs]def gf_trace_map(a, b, c, n, f, p, K): - """ - Compute polynomial trace map in ``GF(p)[x]/(f)``. - - Given a polynomial ``f`` in ``GF(p)[x]``, polynomials ``a``, ``b``, - ``c`` in the quotient ring ``GF(p)[x]/(f)`` such that ``b = c**t - (mod f)`` for some positive power ``t`` of ``p``, and a positive - integer ``n``, returns a mapping:: - - a -> a**t**n, a + a**t + a**t**2 + ... + a**t**n (mod f) - - In factorization context, ``b = x**p mod f`` and ``c = x mod f``. - This way we can efficiently compute trace polynomials in equal - degree factorization routine, much faster than with other methods, - like iterated Frobenius algorithm, for large degrees. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_trace_map - - >>> gf_trace_map([1, 2], [4, 4], [1, 1], 4, [3, 2, 4], 5, ZZ) - ([1, 3], [1, 3]) - - References - ========== - - 1. [Gathen92]_ - - """ - u = gf_compose_mod(a, b, f, p, K) - v = b - - if n & 1: - U = gf_add(a, u, p, K) - V = b - else: - U = a - V = c - - n >>= 1 - - while n: - u = gf_add(u, gf_compose_mod(u, v, f, p, K), p, K) - v = gf_compose_mod(v, v, f, p, K) - - if n & 1: - U = gf_add(U, gf_compose_mod(u, V, f, p, K), p, K) - V = gf_compose_mod(v, V, f, p, K) - - n >>= 1 - - return gf_compose_mod(a, V, f, p, K), U - -
    -@cythonized("i,n") -
    [docs]def gf_random(n, p, K): - """ - Generate a random polynomial in ``GF(p)[x]`` of degree ``n``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_random - >>> gf_random(10, 5, ZZ) #doctest: +SKIP - [1, 2, 3, 2, 1, 1, 1, 2, 0, 4, 2] - - """ - return [K.one] + [ K(int(uniform(0, p))) for i in range(0, n) ] - -
    -@cythonized("i,n") -
    [docs]def gf_irreducible(n, p, K): - """ - Generate random irreducible polynomial of degree ``n`` in ``GF(p)[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_irreducible - >>> gf_irreducible(10, 5, ZZ) #doctest: +SKIP - [1, 4, 2, 2, 3, 2, 4, 1, 4, 0, 4] - - """ - while True: - f = gf_random(n, p, K) - - if gf_irreducible_p(f, p, K): - return f - -
    -@cythonized("i,n") -def gf_irred_p_ben_or(f, p, K): - """ - Ben-Or's polynomial irreducibility test over finite fields. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_irred_p_ben_or - - >>> gf_irred_p_ben_or(ZZ.map([1, 4, 2, 2, 3, 2, 4, 1, 4, 0, 4]), 5, ZZ) - True - >>> gf_irred_p_ben_or(ZZ.map([3, 2, 4]), 5, ZZ) - False - - """ - n = gf_degree(f) - - if n <= 1: - return True - - _, f = gf_monic(f, p, K) - - H = h = gf_pow_mod([K.one, K.zero], p, f, p, K) - - for i in range(0, n//2): - g = gf_sub(h, [K.one, K.zero], p, K) - - if gf_gcd(f, g, p, K) == [K.one]: - h = gf_compose_mod(h, H, f, p, K) - else: - return False - - return True - - -@cythonized("i,n,d") -def gf_irred_p_rabin(f, p, K): - """ - Rabin's polynomial irreducibility test over finite fields. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_irred_p_rabin - - >>> gf_irred_p_rabin(ZZ.map([1, 4, 2, 2, 3, 2, 4, 1, 4, 0, 4]), 5, ZZ) - True - >>> gf_irred_p_rabin(ZZ.map([3, 2, 4]), 5, ZZ) - False - - """ - n = gf_degree(f) - - if n <= 1: - return True - - _, f = gf_monic(f, p, K) - - x = [K.one, K.zero] - - H = h = gf_pow_mod(x, p, f, p, K) - - indices = set([ n//d for d in factorint(n) ]) - - for i in range(1, n): - if i in indices: - g = gf_sub(h, x, p, K) - - if gf_gcd(f, g, p, K) != [K.one]: - return False - - h = gf_compose_mod(h, H, f, p, K) - - return h == x - -_irred_methods = { - 'ben-or': gf_irred_p_ben_or, - 'rabin': gf_irred_p_rabin, -} - - -
    [docs]def gf_irreducible_p(f, p, K): - """ - Test irreducibility of a polynomial ``f`` in ``GF(p)[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_irreducible_p - - >>> gf_irreducible_p(ZZ.map([1, 4, 2, 2, 3, 2, 4, 1, 4, 0, 4]), 5, ZZ) - True - >>> gf_irreducible_p(ZZ.map([3, 2, 4]), 5, ZZ) - False - - """ - method = query('GF_IRRED_METHOD') - - if method is not None: - irred = _irred_methods[method](f, p, K) - else: - irred = gf_irred_p_rabin(f, p, K) - - return irred - -
    -
    [docs]def gf_sqf_p(f, p, K): - """ - Return ``True`` if ``f`` is square-free in ``GF(p)[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_sqf_p - - >>> gf_sqf_p(ZZ.map([3, 2, 4]), 5, ZZ) - True - >>> gf_sqf_p(ZZ.map([2, 4, 4, 2, 2, 1, 4]), 5, ZZ) - False - - """ - _, f = gf_monic(f, p, K) - - if not f: - return True - else: - return gf_gcd(f, gf_diff(f, p, K), p, K) == [K.one] - -
    -
    [docs]def gf_sqf_part(f, p, K): - """ - Return square-free part of a ``GF(p)[x]`` polynomial. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_sqf_part - - >>> gf_sqf_part(ZZ.map([1, 1, 3, 0, 1, 0, 2, 2, 1]), 5, ZZ) - [1, 4, 3] - - """ - _, sqf = gf_sqf_list(f, p, K) - - g = [K.one] - - for f, _ in sqf: - g = gf_mul(g, f, p, K) - - return g - -
    -@cythonized("i,n,d,r") -
    [docs]def gf_sqf_list(f, p, K, all=False): - """ - Return the square-free decomposition of a ``GF(p)[x]`` polynomial. - - Given a polynomial ``f`` in ``GF(p)[x]``, returns the leading coefficient - of ``f`` and a square-free decomposition ``f_1**e_1 f_2**e_2 ... f_k**e_k`` - such that all ``f_i`` are monic polynomials and ``(f_i, f_j)`` for ``i != j`` - are co-prime and ``e_1 ... e_k`` are given in increasing order. All trivial - terms (i.e. ``f_i = 1``) aren't included in the output. - - Consider polynomial ``f = x**11 + 1`` over ``GF(11)[x]``:: - - >>> from sympy.polys.domains import ZZ - - >>> from sympy.polys.galoistools import ( - ... gf_from_dict, gf_diff, gf_sqf_list, gf_pow, - ... ) - ... # doctest: +NORMALIZE_WHITESPACE - - >>> f = gf_from_dict({11: ZZ(1), 0: ZZ(1)}, 11, ZZ) - - Note that ``f'(x) = 0``:: - - >>> gf_diff(f, 11, ZZ) - [] - - This phenomenon doesn't happen in characteristic zero. However we can - still compute square-free decomposition of ``f`` using ``gf_sqf()``:: - - >>> gf_sqf_list(f, 11, ZZ) - (1, [([1, 1], 11)]) - - We obtained factorization ``f = (x + 1)**11``. This is correct because:: - - >>> gf_pow([1, 1], 11, 11, ZZ) == f - True - - References - ========== - - 1. [Geddes92]_ - - """ - n, sqf, factors, r = 1, False, [], int(p) - - lc, f = gf_monic(f, p, K) - - if gf_degree(f) < 1: - return lc, [] - - while True: - F = gf_diff(f, p, K) - - if F != []: - g = gf_gcd(f, F, p, K) - h = gf_quo(f, g, p, K) - - i = 1 - - while h != [K.one]: - G = gf_gcd(g, h, p, K) - H = gf_quo(h, G, p, K) - - if gf_degree(H) > 0: - factors.append((H, i*n)) - - g, h, i = gf_quo(g, G, p, K), G, i + 1 - - if g == [K.one]: - sqf = True - else: - f = g - - if not sqf: - d = gf_degree(f) // r - - for i in range(0, d + 1): - f[i] = f[i*r] - - f, n = f[:d + 1], n*r - else: - break - - if all: - raise ValueError("'all=True' is not supported yet") - - return lc, factors - -
    -@cythonized("n,i,j,r") -
    [docs]def gf_Qmatrix(f, p, K): - """ - Calculate Berlekamp's ``Q`` matrix. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_Qmatrix - - >>> gf_Qmatrix([3, 2, 4], 5, ZZ) - [[1, 0], - [3, 4]] - - >>> gf_Qmatrix([1, 0, 0, 0, 1], 5, ZZ) - [[1, 0, 0, 0], - [0, 4, 0, 0], - [0, 0, 1, 0], - [0, 0, 0, 4]] - - """ - n, r = gf_degree(f), int(p) - - q = [K.one] + [K.zero]*(n - 1) - Q = [list(q)] + [[]]*(n - 1) - - for i in range(1, (n - 1)*r + 1): - qq, c = [(-q[-1]*f[-1]) % p], q[-1] - - for j in range(1, n): - qq.append((q[j - 1] - c*f[-j - 1]) % p) - - if not (i % r): - Q[i//r] = list(qq) - - q = qq - - return Q - -
    -@cythonized("n,i,j,k") -
    [docs]def gf_Qbasis(Q, p, K): - """ - Compute a basis of the kernel of ``Q``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_Qmatrix, gf_Qbasis - - >>> gf_Qbasis(gf_Qmatrix([1, 0, 0, 0, 1], 5, ZZ), 5, ZZ) - [[1, 0, 0, 0], [0, 0, 1, 0]] - - >>> gf_Qbasis(gf_Qmatrix([3, 2, 4], 5, ZZ), 5, ZZ) - [[1, 0]] - - """ - Q, n = [ list(q) for q in Q ], len(Q) - - for k in range(0, n): - Q[k][k] = (Q[k][k] - K.one) % p - - for k in range(0, n): - for i in range(k, n): - if Q[k][i]: - break - else: - continue - - inv = K.invert(Q[k][i], p) - - for j in range(0, n): - Q[j][i] = (Q[j][i]*inv) % p - - for j in range(0, n): - t = Q[j][k] - Q[j][k] = Q[j][i] - Q[j][i] = t - - for i in range(0, n): - if i != k: - q = Q[k][i] - - for j in range(0, n): - Q[j][i] = (Q[j][i] - Q[j][k]*q) % p - - for i in range(0, n): - for j in range(0, n): - if i == j: - Q[i][j] = (K.one - Q[i][j]) % p - else: - Q[i][j] = (-Q[i][j]) % p - - basis = [] - - for q in Q: - if any(q): - basis.append(q) - - return basis - -
    -@cythonized("i,k") -
    [docs]def gf_berlekamp(f, p, K): - """ - Factor a square-free ``f`` in ``GF(p)[x]`` for small ``p``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_berlekamp - - >>> gf_berlekamp([1, 0, 0, 0, 1], 5, ZZ) - [[1, 0, 2], [1, 0, 3]] - - """ - Q = gf_Qmatrix(f, p, K) - V = gf_Qbasis(Q, p, K) - - for i, v in enumerate(V): - V[i] = gf_strip(list(reversed(v))) - - factors = [f] - - for k in range(1, len(V)): - for f in list(factors): - s = K.zero - - while s < p: - g = gf_sub_ground(V[k], s, p, K) - h = gf_gcd(f, g, p, K) - - if h != [K.one] and h != f: - factors.remove(f) - - f = gf_quo(f, h, p, K) - factors.extend([f, h]) - - if len(factors) == len(V): - return _sort_factors(factors, multiple=False) - - s += K.one - - return _sort_factors(factors, multiple=False) - -
    -@cythonized("i") -def gf_ddf_zassenhaus(f, p, K): - """ - Cantor-Zassenhaus: Deterministic Distinct Degree Factorization - - Given a monic square-free polynomial ``f`` in ``GF(p)[x]``, computes - partial distinct degree factorization ``f_1 ... f_d`` of ``f`` where - ``deg(f_i) != deg(f_j)`` for ``i != j``. The result is returned as a - list of pairs ``(f_i, e_i)`` where ``deg(f_i) > 0`` and ``e_i > 0`` - is an argument to the equal degree factorization routine. - - Consider the polynomial ``x**15 - 1`` in ``GF(11)[x]``:: - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_from_dict - - >>> f = gf_from_dict({15: ZZ(1), 0: ZZ(-1)}, 11, ZZ) - - Distinct degree factorization gives:: - - >>> from sympy.polys.galoistools import gf_ddf_zassenhaus - - >>> gf_ddf_zassenhaus(f, 11, ZZ) - [([1, 0, 0, 0, 0, 10], 1), ([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2)] - - which means ``x**15 - 1 = (x**5 - 1) (x**10 + x**5 + 1)``. To obtain - factorization into irreducibles, use equal degree factorization - procedure (EDF) with each of the factors. - - References - ========== - - 1. [Gathen99]_ - 2. [Geddes92]_ - - """ - i, g, factors = 1, [K.one, K.zero], [] - - while 2*i <= gf_degree(f): - g = gf_pow_mod(g, int(p), f, p, K) - h = gf_gcd(f, gf_sub(g, [K.one, K.zero], p, K), p, K) - - if h != [K.one]: - factors.append((h, i)) - - f = gf_quo(f, h, p, K) - g = gf_rem(g, f, p, K) - - i += 1 - - if f != [K.one]: - return factors + [(f, gf_degree(f))] - else: - return factors - - -@cythonized("n,N,i") -def gf_edf_zassenhaus(f, n, p, K): - """ - Cantor-Zassenhaus: Probabilistic Equal Degree Factorization - - Given a monic square-free polynomial ``f`` in ``GF(p)[x]`` and - an integer ``n``, such that ``n`` divides ``deg(f)``, returns all - irreducible factors ``f_1,...,f_d`` of ``f``, each of degree ``n``. - EDF procedure gives complete factorization over Galois fields. - - Consider the square-free polynomial ``f = x**3 + x**2 + x + 1`` in - ``GF(5)[x]``. Let's compute its irreducible factors of degree one:: - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_edf_zassenhaus - - >>> gf_edf_zassenhaus([1,1,1,1], 1, 5, ZZ) - [[1, 1], [1, 2], [1, 3]] - - References - ========== - - 1. [Gathen99]_ - 2. [Geddes92]_ - - """ - factors, q = [f], int(p) - - if gf_degree(f) <= n: - return factors - - N = gf_degree(f) // n - - while len(factors) < N: - r = gf_random(2*n - 1, p, K) - - if p == 2: - h = r - - for i in range(0, 2**(n*N - 1)): - r = gf_pow_mod(r, 2, f, p, K) - h = gf_add(h, r, p, K) - - g = gf_gcd(f, h, p, K) - else: - h = gf_pow_mod(r, (q**n - 1) // 2, f, p, K) - g = gf_gcd(f, gf_sub_ground(h, K.one, p, K), p, K) - - if g != [K.one] and g != f: - factors = gf_edf_zassenhaus(g, n, p, K) \ - + gf_edf_zassenhaus(gf_quo(f, g, p, K), n, p, K) - - return _sort_factors(factors, multiple=False) - - -@cythonized("n,k,i,j") -def gf_ddf_shoup(f, p, K): - """ - Kaltofen-Shoup: Deterministic Distinct Degree Factorization - - Given a monic square-free polynomial ``f`` in ``GF(p)[x]``, computes - partial distinct degree factorization ``f_1,...,f_d`` of ``f`` where - ``deg(f_i) != deg(f_j)`` for ``i != j``. The result is returned as a - list of pairs ``(f_i, e_i)`` where ``deg(f_i) > 0`` and ``e_i > 0`` - is an argument to the equal degree factorization routine. - - This algorithm is an improved version of Zassenhaus algorithm for - large ``deg(f)`` and modulus ``p`` (especially for ``deg(f) ~ lg(p)``). - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_ddf_shoup, gf_from_dict - - >>> f = gf_from_dict({6: ZZ(1), 5: ZZ(-1), 4: ZZ(1), 3: ZZ(1), 1: ZZ(-1)}, 3, ZZ) - - >>> gf_ddf_shoup(f, 3, ZZ) - [([1, 1, 0], 1), ([1, 1, 0, 1, 2], 2)] - - References - ========== - - 1. [Kaltofen98]_ - 2. [Shoup95]_ - 3. [Gathen92]_ - - """ - n = gf_degree(f) - k = int(_ceil(_sqrt(n//2))) - - h = gf_pow_mod([K.one, K.zero], int(p), f, p, K) - - U = [[K.one, K.zero], h] + [K.zero]*(k - 1) - - for i in range(2, k + 1): - U[i] = gf_compose_mod(U[i - 1], h, f, p, K) - - h, U = U[k], U[:k] - V = [h] + [K.zero]*(k - 1) - - for i in range(1, k): - V[i] = gf_compose_mod(V[i - 1], h, f, p, K) - - factors = [] - - for i, v in enumerate(V): - h, j = [K.one], k - 1 - - for u in U: - g = gf_sub(v, u, p, K) - h = gf_mul(h, g, p, K) - h = gf_rem(h, f, p, K) - - g = gf_gcd(f, h, p, K) - f = gf_quo(f, g, p, K) - - for u in reversed(U): - h = gf_sub(v, u, p, K) - F = gf_gcd(g, h, p, K) - - if F != [K.one]: - factors.append((F, k*(i + 1) - j)) - - g, j = gf_quo(g, F, p, K), j - 1 - - if f != [K.one]: - factors.append((f, gf_degree(f))) - - return factors - - -@cythonized("n,N,q") -def gf_edf_shoup(f, n, p, K): - """ - Gathen-Shoup: Probabilistic Equal Degree Factorization - - Given a monic square-free polynomial ``f`` in ``GF(p)[x]`` and integer - ``n`` such that ``n`` divides ``deg(f)``, returns all irreducible factors - ``f_1,...,f_d`` of ``f``, each of degree ``n``. This is a complete - factorization over Galois fields. - - This algorithm is an improved version of Zassenhaus algorithm for - large ``deg(f)`` and modulus ``p`` (especially for ``deg(f) ~ lg(p)``). - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_edf_shoup - - >>> gf_edf_shoup(ZZ.map([1, 2837, 2277]), 1, 2917, ZZ) - [[1, 852], [1, 1985]] - - References - ========== - - 1. [Shoup91]_ - 2. [Gathen92]_ - - """ - N, q = gf_degree(f), int(p) - - if not N: - return [] - if N <= n: - return [f] - - factors, x = [f], [K.one, K.zero] - - r = gf_random(N - 1, p, K) - - h = gf_pow_mod(x, q, f, p, K) - H = gf_trace_map(r, h, x, n - 1, f, p, K)[1] - - if p == 2: - h1 = gf_gcd(f, H, p, K) - h2 = gf_quo(f, h1, p, K) - - factors = gf_edf_shoup(h1, n, p, K) \ - + gf_edf_shoup(h2, n, p, K) - else: - h = gf_pow_mod(H, (q - 1)//2, f, p, K) - - h1 = gf_gcd(f, h, p, K) - h2 = gf_gcd(f, gf_sub_ground(h, K.one, p, K), p, K) - h3 = gf_quo(f, gf_mul(h1, h2, p, K), p, K) - - factors = gf_edf_shoup(h1, n, p, K) \ - + gf_edf_shoup(h2, n, p, K) \ - + gf_edf_shoup(h3, n, p, K) - - return _sort_factors(factors, multiple=False) - - -@cythonized("n") -
    [docs]def gf_zassenhaus(f, p, K): - """ - Factor a square-free ``f`` in ``GF(p)[x]`` for medium ``p``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_zassenhaus - - >>> gf_zassenhaus(ZZ.map([1, 4, 3]), 5, ZZ) - [[1, 1], [1, 3]] - - """ - factors = [] - - for factor, n in gf_ddf_zassenhaus(f, p, K): - factors += gf_edf_zassenhaus(factor, n, p, K) - - return _sort_factors(factors, multiple=False) - -
    -@cythonized("n") -
    [docs]def gf_shoup(f, p, K): - """ - Factor a square-free ``f`` in ``GF(p)[x]`` for large ``p``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_shoup - - >>> gf_shoup(ZZ.map([1, 4, 3]), 5, ZZ) - [[1, 1], [1, 3]] - - """ - factors = [] - - for factor, n in gf_ddf_shoup(f, p, K): - factors += gf_edf_shoup(factor, n, p, K) - - return _sort_factors(factors, multiple=False) -
    -_factor_methods = { - 'berlekamp': gf_berlekamp, # ``p`` : small - 'zassenhaus': gf_zassenhaus, # ``p`` : medium - 'shoup': gf_shoup, # ``p`` : large -} - - -
    [docs]def gf_factor_sqf(f, p, K, method=None): - """ - Factor a square-free polynomial ``f`` in ``GF(p)[x]``. - - Examples - ======== - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_factor_sqf - - >>> gf_factor_sqf(ZZ.map([3, 2, 4]), 5, ZZ) - (3, [[1, 1], [1, 3]]) - - """ - lc, f = gf_monic(f, p, K) - - if gf_degree(f) < 1: - return lc, [] - - method = method or query('GF_FACTOR_METHOD') - - if method is not None: - factors = _factor_methods[method](f, p, K) - else: - factors = gf_zassenhaus(f, p, K) - - return lc, factors - -
    -@cythonized("n") -
    [docs]def gf_factor(f, p, K): - """ - Factor (non square-free) polynomials in ``GF(p)[x]``. - - Given a possibly non square-free polynomial ``f`` in ``GF(p)[x]``, - returns its complete factorization into irreducibles:: - - f_1(x)**e_1 f_2(x)**e_2 ... f_d(x)**e_d - - where each ``f_i`` is a monic polynomial and ``gcd(f_i, f_j) == 1``, - for ``i != j``. The result is given as a tuple consisting of the - leading coefficient of ``f`` and a list of factors of ``f`` with - their multiplicities. - - The algorithm proceeds by first computing square-free decomposition - of ``f`` and then iteratively factoring each of square-free factors. - - Consider a non square-free polynomial ``f = (7*x + 1) (x + 2)**2`` in - ``GF(11)[x]``. We obtain its factorization into irreducibles as follows:: - - >>> from sympy.polys.domains import ZZ - >>> from sympy.polys.galoistools import gf_factor - - >>> gf_factor(ZZ.map([5, 2, 7, 2]), 11, ZZ) - (5, [([1, 2], 1), ([1, 8], 2)]) - - We arrived with factorization ``f = 5 (x + 2) (x + 8)**2``. We didn't - recover the exact form of the input polynomial because we requested to - get monic factors of ``f`` and its leading coefficient separately. - - Square-free factors of ``f`` can be factored into irreducibles over - ``GF(p)`` using three very different methods: - - Berlekamp - efficient for very small values of ``p`` (usually ``p < 25``) - Cantor-Zassenhaus - efficient on average input and with "typical" ``p`` - Shoup-Kaltofen-Gathen - efficient with very large inputs and modulus - - If you want to use a specific factorization method, instead of the default - one, set ``GF_FACTOR_METHOD`` with one of ``berlekamp``, ``zassenhaus`` or - ``shoup`` values. - - References - ========== - - 1. [Gathen99]_ - - """ - lc, f = gf_monic(f, p, K) - - if gf_degree(f) < 1: - return lc, [] - - factors = [] - - for g, n in gf_sqf_list(f, p, K)[1]: - for h in gf_factor_sqf(g, p, K)[1]: - factors.append((h, n)) - - return lc, _sort_factors(factors) - -
    -
    [docs]def gf_value(f, a): - """ - Value of polynomial 'f' at 'a' in field R. - - Examples - ======== - - >>> from sympy.polys.galoistools import gf_value - - >>> gf_value([1, 7, 2, 4], 11) - 2204 - - """ - result = 0 - for c in f: - result *= a - result += c - return result - -
    -def linear_congruence(a, b, m): - """ - Returns the values of x satisfying a*x congruent b mod(m) - - Here m is positive integer and a, b are natural numbers. - This function returns only those values of x which are distinct mod(m). - - Examples - ======== - - >>> from sympy.polys.galoistools import linear_congruence - - >>> linear_congruence(3, 12, 15) - [4, 9, 14] - - There are 3 solutions distinct mod(15) since gcd(a, m) = gcd(3, 15) = 3. - - **Reference** - 1) Wikipedia http://en.wikipedia.org/wiki/Linear_congruence_theorem - - """ - from sympy.polys.polytools import gcdex - if a % m == 0: - if b % m == 0: - return list(range(m)) - else: - return [] - r, _, g = gcdex(a, m) - if b % g != 0: - return [] - return [(r * b // g + t * m // g) % m for t in range(g)] - - -def _raise_mod_power(x, s, p, f): - """ - Used in gf_csolve to generate solutions of f(x) cong 0 mod(p**(s + 1)) - from the solutions of f(x) cong 0 mod(p**s). - - Examples - ======== - - >>> from sympy.polys.galoistools import _raise_mod_power - >>> from sympy.polys.galoistools import csolve_prime - - These is the solutions of f(x) = x**2 + x + 7 cong 0 mod(3) - - >>> f = [1, 1, 7] - >>> csolve_prime(f, 3) - [1] - >>> [ i for i in range(3) if not (i**2 + i + 7) % 3] - [1] - - The solutions of f(x) cong 0 mod(9) are constructed from the - values returned from _raise_mod_power: - - >>> x, s, p = 1, 1, 3 - >>> V = _raise_mod_power(x, s, p, f) - >>> [x + v * p**s for v in V] - [1, 4, 7] - - And these are confirmed with the following: - - >>> [ i for i in range(3**2) if not (i**2 + i + 7) % 3**2] - [1, 4, 7] - - """ - from sympy.polys.domains import ZZ - f_f = gf_diff(f, p, ZZ) - alpha = gf_value(f_f, x) - beta = - gf_value(f, x) // p**s - return linear_congruence(alpha, beta, p) - - -def csolve_prime(f, p, e=1): - """ - Solutions of f(x) congruent 0 mod(p**e). - - Examples - ======== - - >>> from sympy.polys.galoistools import csolve_prime - - >>> csolve_prime([1, 1, 7], 3, 1) - [1] - >>> csolve_prime([1, 1, 7], 3, 2) - [1, 4, 7] - - Solutions [7, 4, 1] (mod 3**2) are generated by ``_raise_mod_power()`` - from solution [1] (mod 3). - """ - from sympy.polys.domains import ZZ - X1 = [i for i in range(p) if gf_eval(f, i, p, ZZ) == 0] - if e == 1: - return X1 - X = [] - S = list(zip(X1, [1]*len(X1))) - while S: - x, s = S.pop() - if s == e: - X.append(x) - else: - s1 = s + 1 - ps = p**s - S.extend([(x + v*ps, s1) for v in _raise_mod_power(x, s, p, f)]) - return sorted(X) - - -
    [docs]def gf_csolve(f, n): - """ - To solve f(x) congruent 0 mod(n). - - n is divided into canonical factors and f(x) cong 0 mod(p**e) will be - solved for each factor. Applying the Chinese Remainder Theorem to the - results returns the final answers. - - Examples - ======== - - Solve [1, 1, 7] congruent 0 mod(189): - - >>> from sympy.polys.galoistools import gf_csolve - >>> gf_csolve([1, 1, 7], 189) - [13, 49, 76, 112, 139, 175] - - References - ========== - - [1] 'An introduction to the Theory of Numbers' 5th Edition by Ivan Niven, - Zuckerman and Montgomery. - - """ - from sympy.polys.domains import ZZ - P = factorint(n) - X = [csolve_prime(f, p, e) for p, e in P.items()] - pools = list(map(tuple, X)) - perms = [[]] - for pool in pools: - perms = [x + [y] for x in perms for y in pool] - dist_factors = [pow(p, e) for p, e in P.items()] - return sorted([gf_crt(per, dist_factors, ZZ) for per in perms])
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/polys/groebnertools.html b/dev-py3k/_modules/sympy/polys/groebnertools.html deleted file mode 100644 index 702191b51b8..00000000000 --- a/dev-py3k/_modules/sympy/polys/groebnertools.html +++ /dev/null @@ -1,1092 +0,0 @@ - - - - - - - - - - sympy.polys.groebnertools — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.polys.groebnertools

    -"""Groebner bases algorithms. """
    -
    -from sympy.polys.monomialtools import (
    -    monomial_mul, monomial_div, monomial_lcm,
    -)
    -
    -from sympy.polys.distributedpolys import (
    -    sdp_LC, sdp_LM, sdp_LT, sdp_mul_term,
    -    sdp_sub, sdp_mul_term, sdp_monic,
    -    sdp_rem, sdp_strip, sdp_sort,
    -    _term_ff_div, _term_rr_div,
    -)
    -
    -from sympy.polys.polyerrors import (
    -    DomainError,
    -)
    -
    -from sympy.polys.polyconfig import query
    -
    -
    -
    [docs]def sdp_groebner(f, u, O, K, gens='', verbose=False, method=None): - """ - Computes Groebner basis for a set of polynomials in `K[X]`. - - Wrapper around the (default) improved Buchberger and the other algorithms - for computing Groebner bases. The choice of algorithm can be changed via - ``method`` argument or :func:`setup` from :mod:`sympy.polys.polyconfig`, - where ``method`` can be either ``buchberger`` or ``f5b``. - - """ - if method is None: - method = query('GB_METHOD') - - _groebner_methods = { - 'buchberger': buchberger, - 'f5b': f5b, - } - - try: - func = _groebner_methods[method] - except KeyError: - raise ValueError("'%s' is not a valid Groebner bases algorithm (valid are 'buchberger' and 'f5b')" % method) - else: - return func(f, u, O, K, gens, verbose) - -# Buchberger algorithm - -
    -
    [docs]def buchberger(f, u, O, K, gens='', verbose=False): - """ - Computes Groebner basis for a set of polynomials in `K[X]`. - - Given a set of multivariate polynomials `F`, finds another - set `G`, such that Ideal `F = Ideal G` and `G` is a reduced - Groebner basis. - - The resulting basis is unique and has monic generators if the - ground domains is a field. Otherwise the result is non-unique - but Groebner bases over e.g. integers can be computed (if the - input polynomials are monic). - - Groebner bases can be used to choose specific generators for a - polynomial ideal. Because these bases are unique you can check - for ideal equality by comparing the Groebner bases. To see if - one polynomial lies in an ideal, divide by the elements in the - base and see if the remainder vanishes. - - They can also be used to solve systems of polynomial equations - as, by choosing lexicographic ordering, you can eliminate one - variable at a time, provided that the ideal is zero-dimensional - (finite number of solutions). - - References - ========== - - 1. [Bose03]_ - 2. [Giovini91]_ - 3. [Ajwa95]_ - 4. [Cox97]_ - - Algorithm used: an improved version of Buchberger's algorithm - as presented in T. Becker, V. Weispfenning, Groebner Bases: A - Computational Approach to Commutative Algebra, Springer, 1993, - page 232. - - Added optional ``gens`` argument to apply :func:`sdp_str` for - the purpose of debugging the algorithm. - - """ - if not K.has_Field: - raise DomainError("can't compute a Groebner basis over %s" % K) - - def select(P): - # normal selection strategy - # select the pair with minimum LCM(LM(f), LM(g)) - pr = min(P, key=lambda pair: O( - monomial_lcm(sdp_LM(f[pair[0]], u), sdp_LM(f[pair[1]], u)))) - return pr - - def normal(g, J): - h = sdp_rem(g, [ f[j] for j in J ], u, O, K) - - if not h: - return None - else: - h = sdp_monic(h, K) - h = tuple(h) - - if not h in I: - I[h] = len(f) - f.append(h) - - return sdp_LM(h, u), I[h] - - def update(G, B, ih): - # update G using the set of critical pairs B and h - # [BW] page 230 - h = f[ih] - mh = sdp_LM(h, u) - - # filter new pairs (h, g), g in G - C = G.copy() - D = set() - - while C: - # select a pair (h, g) by popping an element from C - ig = C.pop() - g = f[ig] - mg = sdp_LM(g, u) - LCMhg = monomial_lcm(mh, mg) - - def lcm_divides(ip): - # LCM(LM(h), LM(p)) divides LCM(LM(h), LM(g)) - m = monomial_lcm(mh, sdp_LM(f[ip], u)) - return monomial_div(LCMhg, m) - - # HT(h) and HT(g) disjoint: mh*mg == LCMhg - if monomial_mul(mh, mg) == LCMhg or ( - not any(lcm_divides(ipx) for ipx in C) and - not any(lcm_divides(pr[1]) for pr in D)): - D.add((ih, ig)) - - E = set() - - while D: - # select h, g from D (h the same as above) - ih, ig = D.pop() - mg = sdp_LM(f[ig], u) - LCMhg = monomial_lcm(mh, mg) - - if not monomial_mul(mh, mg) == LCMhg: - E.add((ih, ig)) - - # filter old pairs - B_new = set() - - while B: - # select g1, g2 from B (-> CP) - ig1, ig2 = B.pop() - mg1 = sdp_LM(f[ig1], u) - mg2 = sdp_LM(f[ig2], u) - LCM12 = monomial_lcm(mg1, mg2) - - # if HT(h) does not divide lcm(HT(g1), HT(g2)) - if not monomial_div(LCM12, mh) or \ - monomial_lcm(mg1, mh) == LCM12 or \ - monomial_lcm(mg2, mh) == LCM12: - B_new.add((ig1, ig2)) - - B_new |= E - - # filter polynomials - G_new = set() - - while G: - ig = G.pop() - mg = sdp_LM(f[ig], u) - - if not monomial_div(mg, mh): - G_new.add(ig) - - G_new.add(ih) - - return G_new, B_new - # end of update ################################ - - if not f: - return [] - - # replace f with a reduced list of initial polynomials; see [BW] page 203 - f1 = f[:] - - while True: - f = f1[:] - f1 = [] - - for i in range(len(f)): - p = f[i] - r = sdp_rem(p, f[:i], u, O, K) - - if r: - f1.append(sdp_monic(r, K)) - - if f == f1: - break - - f = [tuple(p) for p in f] - I = {} # ip = I[p]; p = f[ip] - F = set() # set of indices of polynomials - G = set() # set of indices of intermediate would-be Groebner basis - CP = set() # set of pairs of indices of critical pairs - - for i, h in enumerate(f): - I[h] = i - F.add(i) - - ##################################### - # algorithm GROEBNERNEWS2 in [BW] page 232 - while F: - # select p with minimum monomial according to the monomial ordering O - h = min([f[x] for x in F], key=lambda f: O(sdp_LM(f, u))) - ih = I[h] - F.remove(ih) - G, CP = update(G, CP, ih) - - # count the number of critical pairs which reduce to zero - reductions_to_zero = 0 - - while CP: - ig1, ig2 = select(CP) - CP.remove((ig1, ig2)) - - h = sdp_spoly(f[ig1], f[ig2], u, O, K) - # ordering divisors is on average more efficient [Cox] page 111 - G1 = sorted(G, key=lambda g: O(sdp_LM(f[g], u))) - ht = normal(h, G1) - - if ht: - G, CP = update(G, CP, ht[1]) - else: - reductions_to_zero += 1 - - ###################################### - # now G is a Groebner basis; reduce it - Gr = set() - - for ig in G: - ht = normal(f[ig], G - set([ig])) - - if ht: - Gr.add(ht[1]) - - Gr = [list(f[ig]) for ig in Gr] - - # order according to the monomial ordering - Gr = sorted(Gr, key=lambda f: O(sdp_LM(f, u)), reverse=True) - - if verbose: - print('reductions_to_zero = %d' % reductions_to_zero) - - return Gr - -
    -def sdp_str(f, gens): - if isinstance(gens, str): - gens = gens.split(',') - ngens = len(gens) - s = '' - for expv, c in f: - if c > 0: - s += ' +' - else: - s += ' -' - if c < 0: - c = -c - if c != 1: # and expv != z: - cnt1 = str(c) - else: - cnt1 = '' - sa = [] - for i in range(ngens): - exp = expv[i] - if exp > 1: - sa.append('%s^%d' % (gens[i], exp)) - if exp == 1: - sa.append('%s' % gens[i]) - if cnt1: - sa = [cnt1] + sa - s += '*'.join(sa) - return s - - -
    [docs]def sdp_spoly(p1, p2, u, O, K): - """ - Compute LCM(LM(p1), LM(p2))/LM(p1)*p1 - LCM(LM(p1), LM(p2))/LM(p2)*p2 - This is the S-poly provided p1 and p2 are monic - """ - LM1 = sdp_LM(p1, u) - LM2 = sdp_LM(p2, u) - LCM12 = monomial_lcm(LM1, LM2) - m1 = monomial_div(LCM12, LM1) - m2 = monomial_div(LCM12, LM2) - s1 = sdp_mul_term(p1, (m1, K.one), u, O, K) - s2 = sdp_mul_term(p2, (m2, K.one), u, O, K) - s = sdp_sub(s1, s2, u, O, K) - return s - -# F5B - -# convenience functions - -
    -def Sign(f): - return f[0] - - -def Polyn(f): - return f[1] - - -def Num(f): - return f[2] - - -def sig(monomial, index): - return (monomial, index) - - -def lbp(signature, polynomial, number): - return (signature, polynomial, number) - -# signature functions - - -def sig_cmp(u, v, O): - """ - Compare two signatures by extending the term order to K[X]^n. - - u < v iff - - the index of v is greater than the index of u - or - - the index of v is equal to the index of u and u[0] < v[0] w.r.t. O - - u > v otherwise - """ - if u[1] > v[1]: - return -1 - if u[1] == v[1]: - #if u[0] == v[0]: - # return 0 - if O(u[0]) < O(v[0]): - return -1 - return 1 - - -def sig_key(s, O): - """ - Key for comparing two signatures. - - s = (m, k), t = (n, l) - - s < t iff [k > l] or [k == l and m < n] - s > t otherwise - """ - return (-s[1], O(s[0])) - - -def sig_mult(s, m): - """ - Multiply a signature by a monomial. - - The product of a signature (m, i) and a monomial n is defined as - (m * t, i). - """ - return sig(monomial_mul(s[0], m), s[1]) - -# labeled polynomial functions - - -def lbp_sub(f, g, u, O, K): - """ - Subtract labeled polynomial g from f. - - The signature and number of the difference of f and g are signature - and number of the maximum of f and g, w.r.t. lbp_cmp. - """ - if sig_cmp(Sign(f), Sign(g), O) < 0: - max_poly = g - else: - max_poly = f - - ret = sdp_sub(Polyn(f), Polyn(g), u, O, K) - - return lbp(Sign(max_poly), ret, Num(max_poly)) - - -def lbp_mul_term(f, cx, u, O, K): - """ - Multiply a labeled polynomial with a term. - - The product of a labeled polynomial (s, p, k) by a monomial is - defined as (m * s, m * p, k). - """ - return lbp(sig_mult(Sign(f), cx[0]), sdp_mul_term(Polyn(f), cx, u, O, K), Num(f)) - - -def lbp_cmp(f, g, O): - """ - Compare two labeled polynomials. - - f < g iff - - Sign(f) < Sign(g) - or - - Sign(f) == Sign(g) and Num(f) > Num(g) - - f > g otherwise - """ - if sig_cmp(Sign(f), Sign(g), O) == -1: - return -1 - if Sign(f) == Sign(g): - if Num(f) > Num(g): - return -1 - #if Num(f) == Num(g): - # return 0 - return 1 - - -def lbp_key(f, O): - """ - Key for comparing two labeled polynomials. - """ - return (sig_key(Sign(f), O), -Num(f)) - -# algorithm and helper functions - - -def critical_pair(f, g, u, O, K): - """ - Compute the critical pair corresponding to two labeled polynomials. - - A critical pair is a tuple (um, f, vm, g), where um and vm are - terms such that um * f - vm * g is the S-polynomial of f and g (so, - wlog assume um * f > vm * g). - For performance sake, a critical pair is represented as a tuple - (Sign(um * f), um, f, Sign(vm * g), vm, g), since um * f creates - a new, relatively expensive object in memory, whereas Sign(um * - f) and um are lightweight and f (in the tuple) is a reference to - an already existing object in memory. - """ - ltf = sdp_LT(Polyn(f), u, K) - ltg = sdp_LT(Polyn(g), u, K) - lt = (monomial_lcm(ltf[0], ltg[0]), K.one) - - if K.has_Field: - term_div = _term_ff_div - else: - term_div = _term_rr_div - - um = term_div(lt, ltf, K) - vm = term_div(lt, ltg, K) - - # The full information is not needed (now), so only the product - # with the leading term is considered: - fr = lbp_mul_term( - lbp(Sign(f), [sdp_LT(Polyn(f), u, K)], Num(f)), um, u, O, K) - gr = lbp_mul_term( - lbp(Sign(g), [sdp_LT(Polyn(g), u, K)], Num(g)), vm, u, O, K) - - # return in proper order, such that the S-polynomial is just - # u_first * f_first - u_second * f_second: - if lbp_cmp(fr, gr, O) == -1: - return (Sign(gr), vm, g, Sign(fr), um, f) - else: - return (Sign(fr), um, f, Sign(gr), vm, g) - - -def cp_cmp(c, d, O): - """ - Compare two critical pairs c and d. - - c < d iff - - lbp(c[0], _, Num(c[2]) < lbp(d[0], _, Num(d[2])) (this - corresponds to um_c * f_c and um_d * f_d) - or - - lbp(c[0], _, Num(c[2]) >< lbp(d[0], _, Num(d[2])) and - lbp(c[3], _, Num(c[5])) < lbp(d[3], _, Num(d[5])) (this - corresponds to vm_c * g_c and vm_d * g_d) - - c > d otherwise - """ - c0 = lbp(c[0], [], Num(c[2])) - d0 = lbp(d[0], [], Num(d[2])) - - r = lbp_cmp(c0, d0, O) - - if r == -1: - return -1 - if r == 0: - c1 = lbp(c[3], [], Num(c[5])) - d1 = lbp(d[3], [], Num(d[5])) - - r = lbp_cmp(c1, d1, O) - - if r == -1: - return -1 - #if r == 0: - # return 0 - return 1 - - -def cp_key(c, O): - """ - Key for comparing critical pairs. - """ - return (lbp_key(lbp(c[0], [], Num(c[2])), O), lbp_key(lbp(c[3], [], Num(c[5])), O)) - - -def s_poly(cp, u, O, K): - """ - Compute the S-polynomial of a critical pair. - - The S-polynomial of a critical pair cp is cp[1] * cp[2] - cp[4] * cp[5]. - """ - return lbp_sub(lbp_mul_term(cp[2], cp[1], u, O, K), lbp_mul_term(cp[5], cp[4], u, O, K), u, O, K) - - -def is_rewritable_or_comparable(sign, num, B, u, K): - """ - Check if a labeled polynomial is redundant by checking if its - signature and number imply rewritability or comparability. - - (sign, num) is comparable if there exists a labeled polynomial - h in B, such that sign[1] (the index) is less than Sign(h)[1] - and sign[0] is divisible by the leading monomial of h. - - (sign, num) is rewritable if there exists a labeled polynomial - h in B, such thatsign[1] is equal to Sign(h)[1], num < Num(h) - and sign[0] is divisible by Sign(h)[0]. - """ - for h in B: - # comparable - if sign[1] < Sign(h)[1]: - if monomial_divides(sign[0], sdp_LM(Polyn(h), u)): - return True - - # rewritable - if sign[1] == Sign(h)[1]: - if num < Num(h): - if monomial_divides(sign[0], Sign(h)[0]): - return True - return False - - -def f5_reduce(f, B, u, O, K): - """ - F5-reduce a labeled polynomial f by B. - - Continously searches for non-zero labeled polynomial h in B, such - that the leading term lt_h of h divides the leading term lt_f of - f and Sign(lt_h * h) < Sign(f). If such a labeled polynomial h is - found, f gets replaced by f - lt_f / lt_h * h. If no such h can be - found or f is 0, f is no further F5-reducible and f gets returned. - - A polynomial that is reducible in the usual sense (sdp_rem) - need not be F5-reducible, e.g.: - - >>> from sympy.polys.groebnertools import lbp, sig, f5_reduce, Polyn - >>> from sympy.polys.distributedpolys import sdp_rem - >>> from sympy.polys.monomialtools import lex - >>> from sympy import QQ - - >>> f = lbp(sig((1, 1, 1), 4), [((1, 0, 0), QQ(1))], 3) - >>> g = lbp(sig((0, 0, 0), 2), [((1, 0, 0), QQ(1))], 2) - - >>> sdp_rem(Polyn(f), [Polyn(g)], 2, lex, QQ) - [] - >>> f5_reduce(f, [g], 2, lex, QQ) - (((1, 1, 1), 4), [((1, 0, 0), 1/1)], 3) - - """ - if Polyn(f) == []: - return f - - if K.has_Field: - term_div = _term_ff_div - else: - term_div = _term_rr_div - - while True: - g = f - - for h in B: - if Polyn(h) != []: - if monomial_divides(sdp_LM(Polyn(f), u), sdp_LM(Polyn(h), u)): - t = term_div( - sdp_LT(Polyn(f), u, K), sdp_LT(Polyn(h), u, K), K) - if sig_cmp(sig_mult(Sign(h), t[0]), Sign(f), O) < 0: - # The following check need not be done and is in general slower than without. - #if not is_rewritable_or_comparable(Sign(gp), Num(gp), B, u, K): - hp = lbp_mul_term(h, t, u, O, K) - f = lbp_sub(f, hp, u, O, K) - break - - if g == f or Polyn(f) == []: - return f - - -
    [docs]def f5b(F, u, O, K, gens='', verbose=False): - """ - Computes a reduced Groebner basis for the ideal generated by F. - - f5b is an implementation of the F5B algorithm by Yao Sun and - Dingkang Wang. Similarly to Buchberger's algorithm, the algorithm - proceeds by computing critical pairs, computing the S-polynomial, - reducing it and adjoining the reduced S-polynomial if it is not 0. - - Unlike Buchberger's algorithm, each polynomial contains additional - information, namely a signature and a number. The signature - specifies the path of computation (i.e. from which polynomial in - the original basis was it derived and how), the number says when - the polynomial was added to the basis. With this information it - is (often) possible to decide if an S-polynomial will reduce to - 0 and can be discarded. - - Optimizations include: Reducing the generators before computing - a Groebner basis, removing redundant critical pairs when a new - polynomial enters the basis and sorting the critical pairs and - the current basis. - - Once a Groebner basis has been found, it gets reduced. - - ** References ** - Yao Sun, Dingkang Wang: "A New Proof for the Correctness of F5 - (F5-Like) Algorithm", http://arxiv.org/abs/1004.0084 (specifically - v4) - - Thomas Becker, Volker Weispfenning, Groebner bases: A computational - approach to commutative algebra, 1993, p. 203, 216 - """ - if not K.has_Field: - raise DomainError("can't compute a Groebner basis over %s" % K) - - # reduce polynomials (like in Mario Pernici's implementation) (Becker, Weispfenning, p. 203) - B = F - while True: - F = B - B = [] - - for i in range(len(F)): - p = F[i] - r = sdp_rem(p, F[:i], u, O, K) - - if r != []: - B.append(r) - - if F == B: - break - - # basis - B = [lbp(sig((0,) * (u + 1), i + 1), F[i], i + 1) for i in range(len(F))] - B.sort(key=lambda f: O(sdp_LM(Polyn(f), u)), reverse=True) - - # critical pairs - CP = [critical_pair(B[i], B[j], u, O, K) for i in range( - len(B)) for j in range(i + 1, len(B))] - CP.sort(key=lambda cp: cp_key(cp, O), reverse=True) - - k = len(B) - - reductions_to_zero = 0 - - while len(CP): - cp = CP.pop() - - # discard redundant critical pairs: - if is_rewritable_or_comparable(cp[0], Num(cp[2]), B, u, K): - continue - if is_rewritable_or_comparable(cp[3], Num(cp[5]), B, u, K): - continue - - s = s_poly(cp, u, O, K) - - p = f5_reduce(s, B, u, O, K) - - p = lbp(Sign(p), sdp_monic(Polyn(p), K), k + 1) - - if Polyn(p) != []: - # remove old critical pairs, that become redundant when adding p: - indices = [] - for i, cp in enumerate(CP): - if is_rewritable_or_comparable(cp[0], Num(cp[2]), [p], u, K): - indices.append(i) - elif is_rewritable_or_comparable(cp[3], Num(cp[5]), [p], u, K): - indices.append(i) - - for i in reversed(indices): - del CP[i] - - # only add new critical pairs that are not made redundant by p: - for g in B: - if Polyn(g) != []: - cp = critical_pair(p, g, u, O, K) - if is_rewritable_or_comparable(cp[0], Num(cp[2]), [p], u, K): - continue - elif is_rewritable_or_comparable(cp[3], Num(cp[5]), [p], u, K): - continue - - CP.append(cp) - - # sort (other sorting methods/selection strategies were not as successful) - CP.sort(key=lambda cp: cp_key(cp, O), reverse=True) - - # insert p into B: - m = sdp_LM(Polyn(p), u) - if O(m) <= O(sdp_LM(Polyn(B[-1]), u)): - B.append(p) - else: - for i, q in enumerate(B): - if O(m) > O(sdp_LM(Polyn(q), u)): - B.insert(i, p) - break - - k += 1 - - #print(len(B), len(CP), "%d critical pairs removed" % len(indices)) - else: - reductions_to_zero += 1 - - if verbose: - print(("%d reductions to zero" % reductions_to_zero)) - - # reduce Groebner basis: - H = [sdp_monic(Polyn(g), K) for g in B] - H = red_groebner(H, u, O, K) - - return sorted(H, key=lambda f: O(sdp_LM(f, u)), reverse=True) - -
    -
    [docs]def red_groebner(G, u, O, K): - """ - Compute reduced Groebner basis, from BeckerWeispfenning93, p. 216 - - Selects a subset of generators, that already generate the ideal - and computes a reduced Groebner basis for them. - """ - def reduction(P, u, O, K): - """ - The actual reduction algorithm. - """ - Q = [] - for i, p in enumerate(P): - h = sdp_rem(p, P[:i] + P[i + 1:], u, O, K) - if h != []: - Q.append(h) - - return [sdp_monic(p, K) for p in Q] - - F = G - H = [] - - while F: - f0 = F.pop() - - if not any(monomial_divides(sdp_LM(f0, u), sdp_LM(f, u)) for f in F + H): - H.append(f0) - - # Becker, Weispfenning, p. 217: H is Groebner basis of the ideal generated by G. - return reduction(H, u, O, K) - -
    -
    [docs]def is_groebner(G, u, O, K): - """ - Check if G is a Groebner basis. - """ - for i in range(len(G)): - for j in range(i + 1, len(G)): - s = sdp_spoly(G[i], G[j], u, O, K) - s = sdp_rem(s, G, u, O, K) - if s != []: - return False - - return True - -
    -
    [docs]def is_minimal(G, u, O, K): - """ - Checks if G is a minimal Groebner basis. - """ - G.sort(key=lambda g: O(sdp_LM(g, u))) - for i, g in enumerate(G): - if sdp_LC(g, K) != K.one: - return False - - for h in G[:i] + G[i + 1:]: - if monomial_divides(sdp_LM(g, u), sdp_LM(h, u)): - return False - - return True - -
    -
    [docs]def is_reduced(G, u, O, K): - """ - Checks if G is a reduced Groebner basis. - """ - G.sort(key=lambda g: O(sdp_LM(g, u))) - for i, g in enumerate(G): - if sdp_LC(g, K) != K.one: - return False - - for term in g: - for h in G[:i] + G[i + 1:]: - if monomial_divides(term[0], sdp_LM(h, u)): - return False - - return True - -
    -def monomial_divides(m1, m2): - """ - Returns True if m2 divides m1, False otherwise. Does not create - the quotient. Does not check if both are have the same length. - """ - for i in range(len(m1)): - if m1[i] < m2[i]: - return False - - return True - -# FGLM - - -
    [docs]def matrix_fglm(F, u, O_from, O_to, K): - """ - Converts the reduced Groebner basis ``F`` of a zero-dimensional - ideal w.r.t. ``O_from`` to a reduced Groebner basis - w.r.t. ``O_to``. - - References - ========== - - J.C. Faugere, P. Gianni, D. Lazard, T. Mora (1994). Efficient - Computation of Zero-dimensional Groebner Bases by Change of - Ordering - """ - old_basis = _basis(F, u, O_from, K) - M = _representing_matrices(old_basis, F, u, O_from, K) - - # V contains the normalforms (wrt O_from) of S - S = [(0,) * (u + 1)] - V = [[K.one] + [K.zero] * (len(old_basis) - 1)] - G = [] - - L = [(i, 0) for i in range(u + 1)] # (i, j) corresponds to x_i * S[j] - L.sort(key=lambda k_l1: O_to(_incr_k(S[k_l1[1]], k_l1[0])), reverse=True) - t = L.pop() - - P = _identity_matrix(len(old_basis), K) - - while True: - s = len(S) - v = _matrix_mul(M[t[0]], V[t[1]], K) - _lambda = _matrix_mul(P, v, K) - - if all(_lambda[i] == K.zero for i in range(s, len(old_basis))): - # there is a linear combination of v by V - - lt = [(_incr_k(S[t[1]], t[0]), K.one)] - rest = sdp_strip( - sdp_sort([(S[i], _lambda[i]) for i in range(s)], O_to)) - g = sdp_sub(lt, rest, u, O_to, K) - - if g != []: - G.append(g) - - else: - # v is linearly independant from V - P = _update(s, _lambda, P, K) - S.append(_incr_k(S[t[1]], t[0])) - V.append(v) - - L.extend([(i, s) for i in range(u + 1)]) - L = list(set(L)) - L.sort(key=lambda k_l: O_to(_incr_k(S[k_l[1]], k_l[0])), reverse=True) - - L = [(k, l) for (k, l) in L if - all(monomial_div(_incr_k(S[l], k), sdp_LM(g, u)) is None for g in G)] - - if not L: - G = [ sdp_monic(g, K) for g in G ] - return sorted(G, key=lambda g: O_to(sdp_LM(g, u)), reverse=True) - - t = L.pop() - -
    -def _incr_k(m, k): - return tuple(list(m[:k]) + [m[k] + 1] + list(m[k + 1:])) - - -def _identity_matrix(n, K): - M = [[K.zero] * n for _ in range(n)] - - for i in range(n): - M[i][i] = K.one - - return M - - -def _matrix_mul(M, v, K): - return [sum([row[i] * v[i] for i in range(len(v))]) for row in M] - - -def _update(s, _lambda, P, K): - """ - Update ``P`` such that for the updated `P'` `P' v = e_{s}`. - """ - k = min([j for j in range(s, len(_lambda)) if _lambda[j] != 0]) - - for r in range(len(_lambda)): - if r != k: - P[r] = [P[r][j] - ( - P[k][j] * _lambda[r]) / _lambda[k] for j in range(len(P[r]))] - - P[k] = [P[k][j] / _lambda[k] for j in range(len(P[k]))] - - P[k], P[s] = P[s], P[k] - - return P - - -def _representing_matrices(basis, G, u, O, K): - """ - Compute the matrices corresponding to the linear maps `m \mapsto - x_i m` for all variables `x_i`. - """ - def var(i): - return tuple([0] * i + [1] + [0] * (u - i)) - - def representing_matrix(m): - M = [[K.zero] * len(basis) for _ in range(len(basis))] - - for i, v in enumerate(basis): - r = sdp_rem([(monomial_mul(m, v), K.one)], G, u, O, K) - - for term in r: - j = basis.index(term[0]) - M[j][i] = term[1] - - return M - - return [representing_matrix(var(i)) for i in range(u + 1)] - - -def _basis(G, u, O, K): - """ - Computes a list of monomials which are not divisible by the leading - monomials wrt to ``O`` of ``G``. These monomials are a basis of - `K[X_1, \ldots, X_n]/(G)`. - """ - leading_monomials = [sdp_LM(g, u) for g in G] - candidates = [(0,) * (u + 1)] - basis = [] - - while candidates: - t = candidates.pop() - basis.append(t) - - new_candidates = [_incr_k(t, k) for k in range(u + 1) - if all(monomial_div(_incr_k(t, k), lmg) is None - for lmg in leading_monomials)] - candidates.extend(new_candidates) - candidates.sort(key=lambda m: O(m), reverse=True) - - basis = list(set(basis)) - - return sorted(basis, key=lambda m: O(m)) -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/polys/monomialtools.html b/dev-py3k/_modules/sympy/polys/monomialtools.html deleted file mode 100644 index 4240cb487a5..00000000000 --- a/dev-py3k/_modules/sympy/polys/monomialtools.html +++ /dev/null @@ -1,753 +0,0 @@ - - - - - - - - - - sympy.polys.monomialtools — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.polys.monomialtools

    -"""Tools and arithmetics for monomials of distributed polynomials. """
    -
    -from sympy.core import S, C, Symbol, Mul, Tuple
    -from sympy.polys.polyutils import PicklableWithSlots
    -from sympy.utilities import cythonized
    -from sympy.polys.polyerrors import ExactQuotientFailed
    -
    -
    -
    [docs]def monomials(variables, degree): - r""" - Generate a set of monomials of the given total degree or less. - - Given a set of variables `V` and a total degree `N` generate - a set of monomials of degree at most `N`. The total number of - monomials is huge and is given by the following formula: - - .. math:: - - \frac{(\#V + N)!}{\#V! N!} - - For example if we would like to generate a dense polynomial of - a total degree `N = 50` in 5 variables, assuming that exponents - and all of coefficients are 32-bit long and stored in an array we - would need almost 80 GiB of memory! Fortunately most polynomials, - that we will encounter, are sparse. - - Examples - ======== - - Consider monomials in variables `x` and `y`:: - - >>> from sympy import monomials - >>> from sympy.abc import x, y - - >>> sorted(monomials([x, y], 2)) - [1, x, y, x**2, y**2, x*y] - - >>> sorted(monomials([x, y], 3)) - [1, x, y, x**2, x**3, y**2, y**3, x*y, x*y**2, x**2*y] - - """ - if not variables: - return set([S.One]) - else: - x, tail = variables[0], variables[1:] - - monoms = monomials(tail, degree) - - for i in range(1, degree + 1): - monoms |= set([ x**i * m for m in monomials(tail, degree - i) ]) - - return monoms - -
    -
    [docs]def monomial_count(V, N): - r""" - Computes the number of monomials. - - The number of monomials is given by the following formula: - - .. math:: - - \frac{(\#V + N)!}{\#V! N!} - - where `N` is a total degree and `V` is a set of variables. - - Examples - ======== - - >>> from sympy import monomials, monomial_count - >>> from sympy.abc import x, y - - >>> monomial_count(2, 2) - 6 - - >>> M = monomials([x, y], 2) - - >>> sorted(M) - [1, x, y, x**2, y**2, x*y] - >>> len(M) - 6 - - """ - return C.factorial(V + N) / C.factorial(V) / C.factorial(N) - -
    -class MonomialOrder(object): - """Base class for monomial orderings. """ - - alias = None - is_global = None - - def key(self, monomial): - raise NotImplementedError - - def __str__(self): - return self.alias - - def __call__(self, monomial): - return self.key(monomial) - - def __eq__(self, other): - return self.__class__ == other.__class__ - - def __hash__(self): - return hash(self.__class__) - - def __ne__(self, other): - return not (self == other) - - -
    [docs]class LexOrder(MonomialOrder): - """Lexicographic order of monomials. """ - - alias = 'lex' - is_global = True - - def key(self, monomial): - return monomial - -
    -
    [docs]class GradedLexOrder(MonomialOrder): - """Graded lexicographic order of monomials. """ - - alias = 'grlex' - is_global = True - - def key(self, monomial): - return (sum(monomial), monomial) - -
    -
    [docs]class ReversedGradedLexOrder(MonomialOrder): - """Reversed graded lexicographic order of monomials. """ - - alias = 'grevlex' - is_global = True - - def key(self, monomial): - return (sum(monomial), tuple(reversed([-m for m in monomial]))) - -
    -class ProductOrder(MonomialOrder): - """ - A product order built from other monomial orders. - - Given (not necessarily total) orders O1, O2, ..., On, their product order - P is defined as M1 > M2 iff there exists i such that O1(M1) = O2(M2), - ..., Oi(M1) = Oi(M2), O{i+1}(M1) > O{i+1}(M2). - - Product orders are typically built from monomial orders on different sets - of variables. - - ProductOrder is constructed by passing a list of pairs - [(O1, L1), (O2, L2), ...] where Oi are MonomialOrders and Li are callables. - Upon comparison, the Li are passed the total monomial, and should filter - out the part of the monomial to pass to Oi. - - Examples - ======== - - We can use a lexicographic order on x_1, x_2 and also on - y_1, y_2, y_3, and their product on {x_i, y_i} as follows: - - >>> from sympy.polys.monomialtools import lex, grlex, ProductOrder - >>> P = ProductOrder( - ... (lex, lambda m: m[:2]), # lex order on x_1 and x_2 of monomial - ... (grlex, lambda m: m[2:]) # grlex on y_1, y_2, y_3 - ... ) - >>> P((2, 1, 1, 0, 0)) > P((1, 10, 0, 2, 0)) - True - - Here the exponent `2` of `x_1` in the first monomial - (`x_1^2 x_2 y_1`) is bigger than the exponent `1` of `x_1` in the - second monomial (`x_1 x_2^10 y_2^2`), so the first monomial is greater - in the product ordering. - - >>> P((2, 1, 1, 0, 0)) < P((2, 1, 0, 2, 0)) - True - - Here the exponents of `x_1` and `x_2` agree, so the grlex order on - `y_1, y_2, y_3` is used to decide the ordering. In this case the monomial - `y_2^2` is ordered larger than `y_1`, since for the grlex order the degree - of the monomial is most important. - """ - - def __init__(self, *args): - self.args = args - - def key(self, monomial): - return tuple(O(lamda(monomial)) for (O, lamda) in self.args) - - def __str__(self): - from sympy.core import Tuple - return "ProductOrder" + str(Tuple(*[x[0] for x in self.args])) - - def __eq__(self, other): - if not isinstance(other, ProductOrder): - return False - return self.args == other.args - - def __hash__(self): - return hash((self.__class__, self.args)) - - @property - def is_global(self): - if all(o.is_global is True for o, _ in self.args): - return True - if all(o.is_global is False for o, _ in self.args): - return False - return None - - -class InverseOrder(MonomialOrder): - """ - The "inverse" of another monomial order. - - If O is any monomial order, we can construct another monomial order iO - such that `A >_{iO} B` if and only if `B >_O A`. This is useful for - constructing local orders. - - Note that many algorithms only work with *global* orders. - - For example, in the inverse lexicographic order on a single variable `x`, - high powers of `x` count as small: - - >>> from sympy.polys.monomialtools import lex, InverseOrder - >>> ilex = InverseOrder(lex) - >>> ilex((5,)) < ilex((0,)) - True - """ - - def __init__(self, O): - self.O = O - - def __str__(self): - return "i" + str(self.O) - - def key(self, monomial): - from sympy.core.compatibility import iterable - - def inv(l): - if iterable(l): - return tuple(inv(x) for x in l) - return -l - return inv(self.O.key(monomial)) - - @property - def is_global(self): - if self.O.is_global is True: - return False - if self.O.is_global is False: - return True - return None - - def __eq__(self, other): - return isinstance(other, InverseOrder) and other.O == self.O - - def __hash__(self, other): - return hash((self.__class__, self.O)) - -lex = LexOrder() -grlex = GradedLexOrder() -grevlex = ReversedGradedLexOrder() -ilex = InverseOrder(lex) -igrlex = InverseOrder(grlex) -igrevlex = InverseOrder(grevlex) - -_monomial_key = { - 'lex': lex, - 'grlex': grlex, - 'grevlex': grevlex, - 'ilex': ilex, - 'igrlex': igrlex, - 'igrevlex': igrevlex -} - - -def monomial_key(order=None): - """ - Return a function defining admissible order on monomials. - - The result of a call to :func:`monomial_key` is a function which should - be used as a key to :func:`sorted` built-in function, to provide order - in a set of monomials of the same length. - - Currently supported monomial orderings are: - - 1. lex - lexicographic order (default) - 2. grlex - graded lexicographic order - 3. grevlex - reversed graded lexicographic order - 4. ilex, igrlex, igrevlex - the corresponding inverse orders - - If the input argument is not a string but has ``__call__`` attribute, - then it will pass through with an assumption that the callable object - defines an admissible order on monomials. - - """ - if order is None: - return lex - - if isinstance(order, Symbol): - order = str(order) - - if isinstance(order, str): - try: - return _monomial_key[order] - except KeyError: - raise ValueError("supported monomial orderings are 'lex', 'grlex' and 'grevlex', got %r" % order) - elif hasattr(order, '__call__'): - return order - else: - raise ValueError("monomial ordering specification must be a string or a callable, got %s" % order) - - -class _ItemGetter(object): - """Helper class to return a subsequence of values.""" - - def __init__(self, seq): - self.seq = tuple(seq) - - def __call__(self, m): - return tuple(m[idx] for idx in self.seq) - - def __eq__(self, other): - if not isinstance(other, _ItemGetter): - return False - return self.seq == other.seq - - -def build_product_order(arg, gens): - """ - Build a monomial order on ``gens``. - - ``arg`` should be a tuple of iterables. The first element of each iterable - should be a string or monomial order (will be passed to monomial_key), - the others should be subsets of the generators. This function will build - the corresponding product order. - - For example, build a product of two grlex orders: - - >>> from sympy.polys.monomialtools import grlex, build_product_order - >>> from sympy.abc import x, y, z, t - >>> O = build_product_order((("grlex", x, y), ("grlex", z, t)), [x, y, z, t]) - >>> O((1, 2, 3, 4)) - ((3, (1, 2)), (7, (3, 4))) - """ - gens2idx = {} - for i, g in enumerate(gens): - gens2idx[g] = i - order = [] - for expr in arg: - name = expr[0] - var = expr[1:] - - def makelambda(var): - return _ItemGetter(gens2idx[g] for g in var) - order.append((monomial_key(name), makelambda(var))) - return ProductOrder(*order) - - -@cythonized("a,b") -def monomial_mul(A, B): - """ - Multiplication of tuples representing monomials. - - Lets multiply `x**3*y**4*z` with `x*y**2`:: - - >>> from sympy.polys.monomialtools import monomial_mul - - >>> monomial_mul((3, 4, 1), (1, 2, 0)) - (4, 6, 1) - - which gives `x**4*y**5*z`. - - """ - return tuple([ a + b for a, b in zip(A, B) ]) - - -@cythonized("a,b,c") -def monomial_div(A, B): - """ - Division of tuples representing monomials. - - Lets divide `x**3*y**4*z` by `x*y**2`:: - - >>> from sympy.polys.monomialtools import monomial_div - - >>> monomial_div((3, 4, 1), (1, 2, 0)) - (2, 2, 1) - - which gives `x**2*y**2*z`. However:: - - >>> monomial_div((3, 4, 1), (1, 2, 2)) is None - True - - `x*y**2*z**2` does not divide `x**3*y**4*z`. - - """ - C = [ a - b for a, b in zip(A, B) ] - - if all(c >= 0 for c in C): - return tuple(C) - else: - return None - - -@cythonized("a,b") -def monomial_gcd(A, B): - """ - Greatest common divisor of tuples representing monomials. - - Lets compute GCD of `x*y**4*z` and `x**3*y**2`:: - - >>> from sympy.polys.monomialtools import monomial_gcd - - >>> monomial_gcd((1, 4, 1), (3, 2, 0)) - (1, 2, 0) - - which gives `x*y**2`. - - """ - return tuple([ min(a, b) for a, b in zip(A, B) ]) - - -@cythonized("a,b") -def monomial_lcm(A, B): - """ - Least common multiple of tuples representing monomials. - - Lets compute LCM of `x*y**4*z` and `x**3*y**2`:: - - >>> from sympy.polys.monomialtools import monomial_lcm - - >>> monomial_lcm((1, 4, 1), (3, 2, 0)) - (3, 4, 1) - - which gives `x**3*y**4*z`. - - """ - return tuple([ max(a, b) for a, b in zip(A, B) ]) - -# TODO cythonize - - -def monomial_divides(A, B): - """ - Does there exist a monomial X such that XA == B? - - >>> from sympy.polys.monomialtools import monomial_divides - >>> monomial_divides((1, 2), (3, 4)) - True - >>> monomial_divides((1, 2), (0, 2)) - False - """ - return all(a <= b for a, b in zip(A, B)) - - -@cythonized("i,n") -def monomial_max(*monoms): - """ - Returns maximal degree for each variable in a set of monomials. - - Consider monomials `x**3*y**4*z**5`, `y**5*z` and `x**6*y**3*z**9`. - We wish to find out what is the maximal degree for each of `x`, `y` - and `z` variables:: - - >>> from sympy.polys.monomialtools import monomial_max - - >>> monomial_max((3,4,5), (0,5,1), (6,3,9)) - (6, 5, 9) - - """ - M = list(monoms[0]) - - for N in monoms[1:]: - for i, n in enumerate(N): - M[i] = max(M[i], n) - - return tuple(M) - - -@cythonized("i,n") -def monomial_min(*monoms): - """ - Returns minimal degree for each variable in a set of monomials. - - Consider monomials `x**3*y**4*z**5`, `y**5*z` and `x**6*y**3*z**9`. - We wish to find out what is the minimal degree for each of `x`, `y` - and `z` variables:: - - >>> from sympy.polys.monomialtools import monomial_min - - >>> monomial_min((3,4,5), (0,5,1), (6,3,9)) - (0, 3, 1) - - """ - M = list(monoms[0]) - - for N in monoms[1:]: - for i, n in enumerate(N): - M[i] = min(M[i], n) - - return tuple(M) - - -def monomial_deg(M): - """ - Returns the total degree of a monomial. - - For example, the total degree of `xy^2` is 3: - - >>> from sympy.polys.monomialtools import monomial_deg - >>> monomial_deg((1, 2)) - 3 - """ - return sum(M) - - -
    [docs]class Monomial(PicklableWithSlots): - """Class representing a monomial, i.e. a product of powers. """ - - __slots__ = ['exponents', 'gens'] - - def __init__(self, exponents, gens=None): - self.exponents = tuple(exponents) - self.gens = gens - - def rebuild(self, exponents, gens=None): - return self.__class__(exponents, gens or self.gens) - - def __len__(self): - return len(self.exponents) - - def __iter__(self): - return iter(self.exponents) - - def __getitem__(self, item): - return self.exponents[item] - - def __hash__(self): - return hash((self.__class__.__name__, self.exponents, self.gens)) - - def __str__(self): - if self.gens: - return "*".join([ "%s**%s" % (gen, exp) for gen, exp in zip(self.gens, self.exponents) ]) - else: - return "%s(%s)" % (self.__class__.__name__, self.exponents) - - def as_expr(self, *gens): - """Convert a monomial instance to a SymPy expression. """ - gens = gens or self.gens - - if not gens: - raise ValueError( - "can't convert %s to an expression without generators" % self) - - return Mul(*[ gen**exp for gen, exp in zip(gens, self.exponents) ]) - - def __eq__(self, other): - if isinstance(other, Monomial): - exponents = other.exponents - elif isinstance(other, (tuple, Tuple)): - exponents = other - else: - return False - - return self.exponents == exponents - - def __ne__(self, other): - return not self.__eq__(other) - - def __mul__(self, other): - if isinstance(other, Monomial): - exponents = other.exponents - elif isinstance(other, (tuple, Tuple)): - exponents = other - else: - return NotImplementedError - - return self.rebuild(monomial_mul(self.exponents, exponents)) - - def __div__(self, other): - if isinstance(other, Monomial): - exponents = other.exponents - elif isinstance(other, (tuple, Tuple)): - exponents = other - else: - return NotImplementedError - - result = monomial_div(self.exponents, exponents) - - if result is not None: - return self.rebuild(result) - else: - raise ExactQuotientFailed(self, Monomial(other)) - - __floordiv__ = __truediv__ = __div__ - - def __pow__(self, other): - n = int(other) - - if not n: - return self.rebuild([0]*len(self)) - elif n > 0: - exponents = self.exponents - - for i in range(1, n): - exponents = monomial_mul(exponents, self.exponents) - - return self.rebuild(exponents) - else: - raise ValueError("a non-negative integer expected, got %s" % other) - - def gcd(self, other): - """Greatest common divisor of monomials. """ - if isinstance(other, Monomial): - exponents = other.exponents - elif isinstance(other, (tuple, Tuple)): - exponents = other - else: - raise TypeError( - "an instance of Monomial class expected, got %s" % other) - - return self.rebuild(monomial_gcd(self.exponents, exponents)) - - def lcm(self, other): - """Least common multiple of monomials. """ - if isinstance(other, Monomial): - exponents = other.exponents - elif isinstance(other, (tuple, Tuple)): - exponents = other - else: - raise TypeError( - "an instance of Monomial class expected, got %s" % other) - - return self.rebuild(monomial_lcm(self.exponents, exponents))
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/polys/numberfields.html b/dev-py3k/_modules/sympy/polys/numberfields.html deleted file mode 100644 index 4b9d7b44c63..00000000000 --- a/dev-py3k/_modules/sympy/polys/numberfields.html +++ /dev/null @@ -1,672 +0,0 @@ - - - - - - - - - - sympy.polys.numberfields — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.polys.numberfields

    -"""Computational algebraic number field theory. """
    -
    -from sympy import (
    -    S, Expr, Rational,
    -    Symbol, Add, Mul, sympify, Q, ask, Dummy, Tuple
    -)
    -
    -from sympy.polys.polytools import (
    -    Poly, PurePoly, sqf_norm, invert, factor_list, groebner,
    -)
    -
    -from sympy.polys.polyclasses import (
    -    ANP, DMP,
    -)
    -
    -from sympy.polys.polyerrors import (
    -    IsomorphismFailed,
    -    CoercionFailed,
    -    NotAlgebraic,
    -)
    -
    -from sympy.printing.lambdarepr import LambdaPrinter
    -
    -from sympy.utilities import (
    -    numbered_symbols, variations, lambdify,
    -)
    -
    -from sympy.ntheory import sieve
    -from sympy.mpmath import pslq, mp
    -
    -
    -
    [docs]def minimal_polynomial(ex, x=None, **args): - """ - Computes the minimal polynomial of an algebraic number. - - Examples - ======== - - >>> from sympy import minimal_polynomial, sqrt - >>> from sympy.abc import x - - >>> minimal_polynomial(sqrt(2), x) - x**2 - 2 - >>> minimal_polynomial(sqrt(2) + sqrt(3), x) - x**4 - 10*x**2 + 1 - - """ - generator = numbered_symbols('a', cls=Dummy) - mapping, symbols, replace = {}, {}, [] - - ex = sympify(ex) - - if x is not None: - x, cls = sympify(x), Poly - else: - x, cls = Dummy('x'), PurePoly - - def update_mapping(ex, exp, base=None): - a = next(generator) - symbols[ex] = a - - if base is not None: - mapping[ex] = a**exp + base - else: - mapping[ex] = exp.as_expr(a) - - return a - - def bottom_up_scan(ex): - if ex.is_Atom: - if ex is S.ImaginaryUnit: - if ex not in mapping: - return update_mapping(ex, 2, 1) - else: - return symbols[ex] - elif ex.is_Rational: - return ex - elif ex.is_Add: - return Add(*[ bottom_up_scan(g) for g in ex.args ]) - elif ex.is_Mul: - return Mul(*[ bottom_up_scan(g) for g in ex.args ]) - elif ex.is_Pow: - if ex.exp.is_Rational: - if ex.exp < 0 and ex.base.is_Add: - coeff, terms = ex.base.as_coeff_add() - elt, _ = primitive_element(terms, polys=True) - - alg = ex.base - coeff - - # XXX: turn this into eval() - inverse = invert(elt.gen + coeff, elt).as_expr() - base = inverse.subs(elt.gen, alg).expand() - - if ex.exp == -1: - return bottom_up_scan(base) - else: - ex = base**(-ex.exp) - - if not ex.exp.is_Integer: - base, exp = ( - ex.base**ex.exp.p).expand(), Rational(1, ex.exp.q) - else: - base, exp = ex.base, ex.exp - - base = bottom_up_scan(base) - expr = base**exp - - if expr not in mapping: - return update_mapping(expr, 1/exp, -base) - else: - return symbols[expr] - elif ex.is_AlgebraicNumber: - if ex.root not in mapping: - return update_mapping(ex.root, ex.minpoly) - else: - return symbols[ex.root] - - raise NotAlgebraic("%s doesn't seem to be an algebraic number" % ex) - - polys = args.get('polys', False) - - if ex.is_AlgebraicNumber: - if not polys: - return ex.minpoly.as_expr(x) - else: - return ex.minpoly.replace(x) - elif ex.is_Rational: - result = ex.q*x - ex.p - else: - F = [x - bottom_up_scan(ex)] + list(mapping.values()) - G = groebner(F, list(symbols.values()) + [x], order='lex') - - _, factors = factor_list(G[-1]) - - if len(factors) == 1: - ((result, _),) = factors - else: - for result, _ in factors: - if result.subs(x, ex).evalf(chop=True) == 0: - break - else: # pragma: no cover - raise NotImplementedError("multiple candidates for the minimal polynomial of %s" % ex) - - if polys: - return cls(result, x, field=True) - else: - return result -
    -minpoly = minimal_polynomial - - -def _coeffs_generator(n): - """Generate coefficients for `primitive_element()`. """ - for coeffs in variations([1, -1], n, repetition=True): - yield list(coeffs) - - -
    [docs]def primitive_element(extension, x=None, **args): - """Construct a common number field for all extensions. """ - if not extension: - raise ValueError("can't compute primitive element for empty extension") - - if x is not None: - x, cls = sympify(x), Poly - else: - x, cls = Dummy('x'), PurePoly - - if not args.get('ex', False): - extension = [ AlgebraicNumber(ext, gen=x) for ext in extension ] - - g, coeffs = extension[0].minpoly.replace(x), [1] - - for ext in extension[1:]: - s, _, g = sqf_norm(g, x, extension=ext) - coeffs = [ s*c for c in coeffs ] + [1] - - if not args.get('polys', False): - return g.as_expr(), coeffs - else: - return cls(g), coeffs - - generator = numbered_symbols('y', cls=Dummy) - - F, Y = [], [] - - for ext in extension: - y = next(generator) - - if ext.is_Poly: - if ext.is_univariate: - f = ext.as_expr(y) - else: - raise ValueError("expected minimal polynomial, got %s" % ext) - else: - f = minpoly(ext, y) - - F.append(f) - Y.append(y) - - coeffs_generator = args.get('coeffs', _coeffs_generator) - - for coeffs in coeffs_generator(len(Y)): - f = x - sum([ c*y for c, y in zip(coeffs, Y)]) - G = groebner(F + [f], Y + [x], order='lex', field=True) - - H, g = G[:-1], cls(G[-1], x, domain='QQ') - - for i, (h, y) in enumerate(list(zip(H, Y))): - try: - H[i] = Poly(y - h, x, - domain='QQ').all_coeffs() # XXX: composite=False - except CoercionFailed: # pragma: no cover - break # G is not a triangular set - else: - break - else: # pragma: no cover - raise RuntimeError("run out of coefficient configurations") - - _, g = g.clear_denoms() - - if not args.get('polys', False): - return g.as_expr(), coeffs, H - else: - return g, coeffs, H - -
    -def is_isomorphism_possible(a, b): - """Returns `True` if there is a chance for isomorphism. """ - n = a.minpoly.degree() - m = b.minpoly.degree() - - if m % n != 0: - return False - - if n == m: - return True - - da = a.minpoly.discriminant() - db = b.minpoly.discriminant() - - i, k, half = 1, m//n, db//2 - - while True: - p = sieve[i] - P = p**k - - if P > half: - break - - if ((da % p) % 2) and not (db % P): - return False - - i += 1 - - return True - - -def field_isomorphism_pslq(a, b): - """Construct field isomorphism using PSLQ algorithm. """ - if not a.root.is_real or not b.root.is_real: - raise NotImplementedError("PSLQ doesn't support complex coefficients") - - f = a.minpoly - g = b.minpoly.replace(f.gen) - - n, m, prev = 100, b.minpoly.degree(), None - - for i in range(1, 5): - A = a.root.evalf(n) - B = b.root.evalf(n) - - basis = [1, B] + [ B**i for i in range(2, m) ] + [A] - - dps, mp.dps = mp.dps, n - coeffs = pslq(basis, maxcoeff=int(1e10), maxsteps=1000) - mp.dps = dps - - if coeffs is None: - break - - if coeffs != prev: - prev = coeffs - else: - break - - coeffs = [S(c)/coeffs[-1] for c in coeffs[:-1]] - - while not coeffs[-1]: - coeffs.pop() - - coeffs = list(reversed(coeffs)) - h = Poly(coeffs, f.gen, domain='QQ') - - if f.compose(h).rem(g).is_zero: - d, approx = len(coeffs) - 1, 0 - - for i, coeff in enumerate(coeffs): - approx += coeff*B**(d - i) - - if A*approx < 0: - return [ -c for c in coeffs ] - else: - return coeffs - elif f.compose(-h).rem(g).is_zero: - return [ -c for c in coeffs ] - else: - n *= 2 - - return None - - -def field_isomorphism_factor(a, b): - """Construct field isomorphism via factorization. """ - _, factors = factor_list(a.minpoly, extension=b) - - for f, _ in factors: - if f.degree() == 1: - coeffs = f.rep.TC().to_sympy_list() - d, terms = len(coeffs) - 1, [] - - for i, coeff in enumerate(coeffs): - terms.append(coeff*b.root**(d - i)) - - root = Add(*terms) - - if (a.root - root).evalf(chop=True) == 0: - return coeffs - - if (a.root + root).evalf(chop=True) == 0: - return [ -c for c in coeffs ] - else: - return None - - -
    [docs]def field_isomorphism(a, b, **args): - """Construct an isomorphism between two number fields. """ - a, b = sympify(a), sympify(b) - - if not a.is_AlgebraicNumber: - a = AlgebraicNumber(a) - - if not b.is_AlgebraicNumber: - b = AlgebraicNumber(b) - - if a == b: - return a.coeffs() - - n = a.minpoly.degree() - m = b.minpoly.degree() - - if n == 1: - return [a.root] - - if m % n != 0: - return None - - if args.get('fast', True): - try: - result = field_isomorphism_pslq(a, b) - - if result is not None: - return result - except NotImplementedError: - pass - - return field_isomorphism_factor(a, b) - -
    -
    [docs]def to_number_field(extension, theta=None, **args): - """Express `extension` in the field generated by `theta`. """ - gen = args.get('gen') - - if hasattr(extension, '__iter__'): - extension = list(extension) - else: - extension = [extension] - - if len(extension) == 1 and type(extension[0]) is tuple: - return AlgebraicNumber(extension[0]) - - minpoly, coeffs = primitive_element(extension, gen, polys=True) - root = sum([ coeff*ext for coeff, ext in zip(coeffs, extension) ]) - - if theta is None: - return AlgebraicNumber((minpoly, root)) - else: - theta = sympify(theta) - - if not theta.is_AlgebraicNumber: - theta = AlgebraicNumber(theta, gen=gen) - - coeffs = field_isomorphism(root, theta) - - if coeffs is not None: - return AlgebraicNumber(theta, coeffs) - else: - raise IsomorphismFailed( - "%s is not in a subfield of %s" % (root, theta.root)) - -
    -
    [docs]class AlgebraicNumber(Expr): - """Class for representing algebraic numbers in SymPy. """ - - __slots__ = ['rep', 'root', 'alias', 'minpoly'] - - is_AlgebraicNumber = True - - def __new__(cls, expr, coeffs=Tuple(), alias=None, **args): - """Construct a new algebraic number. """ - expr = sympify(expr) - - if isinstance(expr, (tuple, Tuple)): - minpoly, root = expr - - if not minpoly.is_Poly: - minpoly = Poly(minpoly) - elif expr.is_AlgebraicNumber: - minpoly, root = expr.minpoly, expr.root - else: - minpoly, root = minimal_polynomial( - expr, args.get('gen'), polys=True), expr - - dom = minpoly.get_domain() - - if coeffs != Tuple(): - if not isinstance(coeffs, ANP): - rep = DMP.from_sympy_list(sympify(coeffs), 0, dom) - scoeffs = Tuple(*coeffs) - else: - rep = DMP.from_list(coeffs.to_list(), 0, dom) - scoeffs = Tuple(*coeffs.to_list()) - - if rep.degree() >= minpoly.degree(): - rep = rep.rem(minpoly.rep) - - sargs = (root, scoeffs) - - else: - rep = DMP.from_list([1, 0], 0, dom) - - if ask(Q.negative(root)): - rep = -rep - - sargs = (root, coeffs) - - if alias is not None: - if not isinstance(alias, Symbol): - alias = Symbol(alias) - sargs = sargs + (alias,) - - obj = Expr.__new__(cls, *sargs) - - obj.rep = rep - obj.root = root - obj.alias = alias - obj.minpoly = minpoly - - return obj - - def __hash__(self): - return super(AlgebraicNumber, self).__hash__() - - def _eval_evalf(self, prec): - return self.as_expr()._evalf(prec) - - @property -
    [docs] def is_aliased(self): - """Returns ``True`` if ``alias`` was set. """ - return self.alias is not None -
    -
    [docs] def as_poly(self, x=None): - """Create a Poly instance from ``self``. """ - if x is not None: - return Poly.new(self.rep, x) - else: - if self.alias is not None: - return Poly.new(self.rep, self.alias) - else: - return PurePoly.new(self.rep, Dummy('x')) -
    -
    [docs] def as_expr(self, x=None): - """Create a Basic expression from ``self``. """ - return self.as_poly(x or self.root).as_expr().expand() -
    -
    [docs] def coeffs(self): - """Returns all SymPy coefficients of an algebraic number. """ - return [ self.rep.dom.to_sympy(c) for c in self.rep.all_coeffs() ] -
    -
    [docs] def native_coeffs(self): - """Returns all native coefficients of an algebraic number. """ - return self.rep.all_coeffs() -
    -
    [docs] def to_algebraic_integer(self): - """Convert ``self`` to an algebraic integer. """ - f = self.minpoly - - if f.LC() == 1: - return self - - coeff = f.LC()**(f.degree() - 1) - poly = f.compose(Poly(f.gen/f.LC())) - - minpoly = poly*coeff - root = f.LC()*self.root - - return AlgebraicNumber((minpoly, root), self.coeffs()) - -
    -class IntervalPrinter(LambdaPrinter): - """Use ``lambda`` printer but print numbers as ``mpi`` intervals. """ - - def _print_Integer(self, expr): - return "mpi('%s')" % super(IntervalPrinter, self)._print_Integer(expr) - - def _print_Rational(self, expr): - return "mpi('%s')" % super(IntervalPrinter, self)._print_Rational(expr) - - def _print_Pow(self, expr): - return super(IntervalPrinter, self)._print_Pow(expr, rational=True) - - -
    [docs]def isolate(alg, eps=None, fast=False): - """Give a rational isolating interval for an algebraic number. """ - alg = sympify(alg) - - if alg.is_Rational: - return (alg, alg) - elif not ask(Q.real(alg)): - raise NotImplementedError( - "complex algebraic numbers are not supported") - - func = lambdify((), alg, modules="mpmath", printer=IntervalPrinter()) - - poly = minpoly(alg, polys=True) - intervals = poly.intervals(sqf=True) - - dps, done = mp.dps, False - - try: - while not done: - alg = func() - - for a, b in intervals: - if a <= alg.a and alg.b <= b: - done = True - break - else: - mp.dps *= 2 - finally: - mp.dps = dps - - if eps is not None: - a, b = poly.refine_root(a, b, eps=eps, fast=fast) - - return (a, b)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/polys/orthopolys.html b/dev-py3k/_modules/sympy/polys/orthopolys.html deleted file mode 100644 index b8bb56461b8..00000000000 --- a/dev-py3k/_modules/sympy/polys/orthopolys.html +++ /dev/null @@ -1,441 +0,0 @@ - - - - - - - - - - sympy.polys.orthopolys — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.polys.orthopolys

    -"""Efficient functions for generating orthogonal polynomials. """
    -
    -from sympy import Dummy
    -
    -from sympy.utilities import cythonized
    -
    -from sympy.polys.constructor import construct_domain
    -from sympy.polys.polytools import Poly, PurePoly
    -from sympy.polys.polyclasses import DMP
    -
    -from sympy.polys.densearith import (
    -    dup_mul, dup_mul_ground, dup_lshift, dup_sub, dup_add
    -)
    -
    -from sympy.polys.domains import ZZ, QQ
    -
    -
    -@cythonized("n,i")
    -def dup_jacobi(n, a, b, K):
    -    """Low-level implementation of Jacobi polynomials. """
    -    seq = [[K.one], [(a + b + K(2))/K(2), (a - b)/K(2)]]
    -
    -    for i in range(2, n + 1):
    -        den = K(i)*(a + b + i)*(a + b + K(2)*i - K(2))
    -        f0 = (a + b + K(2)*i - K.one) * (a*a - b*b) / (K(2)*den)
    -        f1 = (a + b + K(2)*i - K.one) * (a + b + K(2)*i - K(2)) * (a + b + K(2)*i) / (K(2)*den)
    -        f2 = (a + i - K.one)*(b + i - K.one)*(a + b + K(2)*i) / den
    -        p0 = dup_mul_ground(seq[-1], f0, K)
    -        p1 = dup_mul_ground(dup_lshift(seq[-1], 1, K), f1, K)
    -        p2 = dup_mul_ground(seq[-2], f2, K)
    -        seq.append(dup_sub(dup_add(p0, p1, K), p2, K))
    -
    -    return seq[n]
    -
    -
    -
    [docs]def jacobi_poly(n, a, b, x=None, **args): - """Generates Jacobi polynomial of degree `n` in `x`. """ - if n < 0: - raise ValueError("can't generate Jacobi polynomial of degree %s" % n) - - K, v = construct_domain([a, b], field=True) - poly = DMP(dup_jacobi(int(n), v[0], v[1], K), K) - - if x is not None: - poly = Poly.new(poly, x) - else: - poly = PurePoly.new(poly, Dummy('x')) - - if not args.get('polys', False): - return poly.as_expr() - else: - return poly - -
    -@cythonized("n,i") -def dup_gegenbauer(n, a, K): - """Low-level implementation of Gegenbauer polynomials. """ - seq = [[K.one], [K(2)*a, K.zero]] - - for i in range(2, n + 1): - f1 = K(2) * (i + a - K.one) / i - f2 = (i + K(2)*a - K(2)) / i - p1 = dup_mul_ground(dup_lshift(seq[-1], 1, K), f1, K) - p2 = dup_mul_ground(seq[-2], f2, K) - seq.append(dup_sub(p1, p2, K)) - - return seq[n] - - -
    [docs]def gegenbauer_poly(n, a, x=None, **args): - """Generates Gegenbauer polynomial of degree `n` in `x`. """ - if n < 0: - raise ValueError( - "can't generate Gegenbauer polynomial of degree %s" % n) - - K, a = construct_domain(a, field=True) - poly = DMP(dup_gegenbauer(int(n), a, K), K) - - if x is not None: - poly = Poly.new(poly, x) - else: - poly = PurePoly.new(poly, Dummy('x')) - - if not args.get('polys', False): - return poly.as_expr() - else: - return poly - -
    -@cythonized("n,i") -def dup_chebyshevt(n, K): - """Low-level implementation of Chebyshev polynomials of the 1st kind. """ - seq = [[K.one], [K.one, K.zero]] - - for i in range(2, n + 1): - a = dup_mul_ground(dup_lshift(seq[-1], 1, K), K(2), K) - seq.append(dup_sub(a, seq[-2], K)) - - return seq[n] - - -
    [docs]def chebyshevt_poly(n, x=None, **args): - """Generates Chebyshev polynomial of the first kind of degree `n` in `x`. """ - if n < 0: - raise ValueError( - "can't generate 1st kind Chebyshev polynomial of degree %s" % n) - - poly = DMP(dup_chebyshevt(int(n), ZZ), ZZ) - - if x is not None: - poly = Poly.new(poly, x) - else: - poly = PurePoly.new(poly, Dummy('x')) - - if not args.get('polys', False): - return poly.as_expr() - else: - return poly - -
    -@cythonized("n,i") -def dup_chebyshevu(n, K): - """Low-level implementation of Chebyshev polynomials of the 2nd kind. """ - seq = [[K.one], [K(2), K.zero]] - - for i in range(2, n + 1): - a = dup_mul_ground(dup_lshift(seq[-1], 1, K), K(2), K) - seq.append(dup_sub(a, seq[-2], K)) - - return seq[n] - - -
    [docs]def chebyshevu_poly(n, x=None, **args): - """Generates Chebyshev polynomial of the second kind of degree `n` in `x`. """ - if n < 0: - raise ValueError( - "can't generate 2nd kind Chebyshev polynomial of degree %s" % n) - - poly = DMP(dup_chebyshevu(int(n), ZZ), ZZ) - - if x is not None: - poly = Poly.new(poly, x) - else: - poly = PurePoly.new(poly, Dummy('x')) - - if not args.get('polys', False): - return poly.as_expr() - else: - return poly - -
    -@cythonized("n,i") -def dup_hermite(n, K): - """Low-level implementation of Hermite polynomials. """ - seq = [[K.one], [K(2), K.zero]] - - for i in range(2, n + 1): - a = dup_lshift(seq[-1], 1, K) - b = dup_mul_ground(seq[-2], K(i - 1), K) - - c = dup_mul_ground(dup_sub(a, b, K), K(2), K) - - seq.append(c) - - return seq[n] - - -
    [docs]def hermite_poly(n, x=None, **args): - """Generates Hermite polynomial of degree `n` in `x`. """ - if n < 0: - raise ValueError("can't generate Hermite polynomial of degree %s" % n) - - poly = DMP(dup_hermite(int(n), ZZ), ZZ) - - if x is not None: - poly = Poly.new(poly, x) - else: - poly = PurePoly.new(poly, Dummy('x')) - - if not args.get('polys', False): - return poly.as_expr() - else: - return poly - -
    -@cythonized("n,i") -def dup_legendre(n, K): - """Low-level implementation of Legendre polynomials. """ - seq = [[K.one], [K.one, K.zero]] - - for i in range(2, n + 1): - a = dup_mul_ground(dup_lshift(seq[-1], 1, K), K(2*i - 1, i), K) - b = dup_mul_ground(seq[-2], K(i - 1, i), K) - - seq.append(dup_sub(a, b, K)) - - return seq[n] - - -
    [docs]def legendre_poly(n, x=None, **args): - """Generates Legendre polynomial of degree `n` in `x`. """ - if n < 0: - raise ValueError("can't generate Legendre polynomial of degree %s" % n) - - poly = DMP(dup_legendre(int(n), QQ), QQ) - - if x is not None: - poly = Poly.new(poly, x) - else: - poly = PurePoly.new(poly, Dummy('x')) - - if not args.get('polys', False): - return poly.as_expr() - else: - return poly - -
    -@cythonized("n,i") -def dup_laguerre(n, alpha, K): - """Low-level implementation of Laguerre polynomials. """ - seq = [[K.zero], [K.one]] - - for i in range(1, n + 1): - a = dup_mul(seq[-1], [-K.one/i, alpha/i + K(2*i - 1)/i], K) - b = dup_mul_ground(seq[-2], alpha/i + K(i - 1)/i, K) - - seq.append(dup_sub(a, b, K)) - - return seq[-1] - - -
    [docs]def laguerre_poly(n, x=None, alpha=None, **args): - """Generates Laguerre polynomial of degree `n` in `x`. """ - if n < 0: - raise ValueError("can't generate Laguerre polynomial of degree %s" % n) - - if alpha is not None: - K, alpha = construct_domain( - alpha, field=True) # XXX: ground_field=True - else: - K, alpha = QQ, QQ(0) - - poly = DMP(dup_laguerre(int(n), alpha, K), K) - - if x is not None: - poly = Poly.new(poly, x) - else: - poly = PurePoly.new(poly, Dummy('x')) - - if not args.get('polys', False): - return poly.as_expr() - else: - return poly - -
    -@cythonized("n,i") -def dup_spherical_bessel_fn(n, K): - """ Low-level implementation of fn(n, x) """ - seq = [[K.one], [K.one, K.zero]] - - for i in range(2, n + 1): - a = dup_mul_ground(dup_lshift(seq[-1], 1, K), K(2*i - 1), K) - seq.append(dup_sub(a, seq[-2], K)) - - return dup_lshift(seq[n], 1, K) - - -@cythonized("n,i") -def dup_spherical_bessel_fn_minus(n, K): - """ Low-level implementation of fn(-n, x) """ - seq = [[K.one, K.zero], [K.zero]] - - for i in range(2, n + 1): - a = dup_mul_ground(dup_lshift(seq[-1], 1, K), K(3 - 2*i), K) - seq.append(dup_sub(a, seq[-2], K)) - - return seq[n] - - -def spherical_bessel_fn(n, x=None, **args): - """ - Coefficients for the spherical Bessel functions. - - Those are only needed in the jn() function. - - The coefficients are calculated from: - - fn(0, z) = 1/z - fn(1, z) = 1/z**2 - fn(n-1, z) + fn(n+1, z) == (2*n+1)/z * fn(n, z) - - Examples - ======== - - >>> from sympy.polys.orthopolys import spherical_bessel_fn as fn - >>> from sympy import Symbol - >>> z = Symbol("z") - >>> fn(1, z) - z**(-2) - >>> fn(2, z) - -1/z + 3/z**3 - >>> fn(3, z) - -6/z**2 + 15/z**4 - >>> fn(4, z) - 1/z - 45/z**3 + 105/z**5 - - """ - - if n < 0: - dup = dup_spherical_bessel_fn_minus(-int(n), ZZ) - else: - dup = dup_spherical_bessel_fn(int(n), ZZ) - - poly = DMP(dup, ZZ) - - if x is not None: - poly = Poly.new(poly, 1/x) - else: - poly = PurePoly.new(poly, 1/Dummy('x')) - - if not args.get('polys', False): - return poly.as_expr() - else: - return poly -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/polys/partfrac.html b/dev-py3k/_modules/sympy/polys/partfrac.html deleted file mode 100644 index 50363872bca..00000000000 --- a/dev-py3k/_modules/sympy/polys/partfrac.html +++ /dev/null @@ -1,290 +0,0 @@ - - - - - - - - - - sympy.polys.partfrac — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.polys.partfrac

    -"""Algorithms for partial fraction decomposition of rational functions. """
    -
    -from sympy.polys import Poly, RootSum, cancel, factor
    -from sympy.polys.polytools import parallel_poly_from_expr
    -from sympy.polys.polyoptions import allowed_flags, set_defaults
    -
    -from sympy.core import S, Add, sympify, Function, Lambda, Dummy
    -from sympy.utilities import numbered_symbols, take, threaded
    -
    -
    -@threaded
    -
    [docs]def apart(f, x=None, full=False, **options): - """ - Compute partial fraction decomposition of a rational function. - - Given a rational function ``f`` compute partial fraction decomposition - of ``f``. Two algorithms are available: one is based on undetermined - coefficients method and the other is Bronstein's full partial fraction - decomposition algorithm. - - Examples - ======== - - >>> from sympy.polys.partfrac import apart - >>> from sympy.abc import x, y - - >>> apart(y/(x + 2)/(x + 1), x) - -y/(x + 2) + y/(x + 1) - - """ - allowed_flags(options, []) - - f = sympify(f) - - if f.is_Atom: - return f - else: - P, Q = f.as_numer_denom() - - options = set_defaults(options, extension=True) - (P, Q), opt = parallel_poly_from_expr((P, Q), x, **options) - - if P.is_multivariate: - raise NotImplementedError( - "multivariate partial fraction decomposition") - - common, P, Q = P.cancel(Q) - - poly, P = P.div(Q, auto=True) - P, Q = P.rat_clear_denoms(Q) - - if Q.degree() <= 1: - partial = P/Q - else: - if not full: - partial = apart_undetermined_coeffs(P, Q) - else: - partial = apart_full_decomposition(P, Q) - - terms = S.Zero - - for term in Add.make_args(partial): - if term.has(RootSum): - terms += term - else: - terms += factor(term) - - return common*(poly.as_expr() + terms) - -
    -def apart_undetermined_coeffs(P, Q): - """Partial fractions via method of undetermined coefficients. """ - X = numbered_symbols(cls=Dummy) - partial, symbols = [], [] - - _, factors = Q.factor_list() - - for f, k in factors: - n, q = f.degree(), Q - - for i in range(1, k + 1): - coeffs, q = take(X, n), q.quo(f) - partial.append((coeffs, q, f, i)) - symbols.extend(coeffs) - - dom = Q.get_domain().inject(*symbols) - F = Poly(0, Q.gen, domain=dom) - - for i, (coeffs, q, f, k) in enumerate(partial): - h = Poly(coeffs, Q.gen, domain=dom) - partial[i] = (h, f, k) - q = q.set_domain(dom) - F += h*q - - system, result = [], S(0) - - for (k,), coeff in F.terms(): - system.append(coeff - P.nth(k)) - - from sympy.solvers import solve - solution = solve(system, symbols) - - for h, f, k in partial: - h = h.as_expr().subs(solution) - result += h/f.as_expr()**k - - return result - - -def apart_full_decomposition(P, Q): - """ - Bronstein's full partial fraction decomposition algorithm. - - Given a univariate rational function ``f``, performing only GCD - operations over the algebraic closure of the initial ground domain - of definition, compute full partial fraction decomposition with - fractions having linear denominators. - - Note that no factorization of the initial denominator of ``f`` is - performed. The final decomposition is formed in terms of a sum of - :class:`RootSum` instances. - - References - ========== - - 1. [Bronstein93]_ - - """ - f, x, U = P/Q, P.gen, [] - - u = Function('u')(x) - a = Dummy('a') - - partial = S(0) - - for d, n in Q.sqf_list_include(all=True): - b = d.as_expr() - U += [ u.diff(x, n - 1) ] - - h = cancel(f*b**n) / u**n - - H, subs = [h], [] - - for j in range(1, n): - H += [ H[-1].diff(x) / j ] - - for j in range(1, n + 1): - subs += [ (U[j - 1], b.diff(x, j) / j) ] - - for j in range(0, n): - P, Q = cancel(H[j]).as_numer_denom() - - for i in range(0, j + 1): - P = P.subs(*subs[j - i]) - - Q = Q.subs(*subs[0]) - - P = Poly(P, x) - Q = Poly(Q, x) - - G = P.gcd(d) - D = d.quo(G) - - B, g = Q.half_gcdex(D) - b = (P * B.quo(g)).rem(D) - - numer = b.as_expr() - denom = (x - a)**(n - j) - - func = Lambda(a, numer.subs(x, a)/denom) - partial += RootSum(D, func, auto=False) - - return partial -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/polys/polyclasses.html b/dev-py3k/_modules/sympy/polys/polyclasses.html deleted file mode 100644 index 4f01ca14403..00000000000 --- a/dev-py3k/_modules/sympy/polys/polyclasses.html +++ /dev/null @@ -1,1822 +0,0 @@ - - - - - - - - - - sympy.polys.polyclasses — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.polys.polyclasses

    -"""OO layer for several polynomial representations. """
    -
    -from sympy.polys.polyutils import PicklableWithSlots
    -from sympy.polys.polyerrors import CoercionFailed, NotReversible
    -
    -
    -class GenericPoly(PicklableWithSlots):
    -    """Base class for low-level polynomial representations. """
    -
    -    def ground_to_ring(f):
    -        """Make the ground domain a ring. """
    -        return f.set_domain(f.dom.get_ring())
    -
    -    def ground_to_field(f):
    -        """Make the ground domain a field. """
    -        return f.set_domain(f.dom.get_field())
    -
    -    def ground_to_exact(f):
    -        """Make the ground domain exact. """
    -        return f.set_domain(f.dom.get_exact())
    -
    -    @classmethod
    -    def _perify_factors(per, result, include):
    -        if include:
    -            coeff, factors = result
    -        else:
    -            coeff = result
    -
    -        factors = [ (per(g), k) for g, k in factors ]
    -
    -        if include:
    -            return coeff, factors
    -        else:
    -            return factors
    -
    -from sympy.polys.densebasic import (
    -    dmp_validate,
    -    dup_normal, dmp_normal,
    -    dup_convert, dmp_convert,
    -    dmp_from_sympy,
    -    dup_strip,
    -    dup_degree, dmp_degree_in,
    -    dmp_degree_list,
    -    dmp_negative_p,
    -    dup_LC, dmp_ground_LC,
    -    dup_TC, dmp_ground_TC,
    -    dmp_ground_nth,
    -    dmp_one, dmp_ground,
    -    dmp_zero_p, dmp_one_p, dmp_ground_p,
    -    dup_from_dict, dmp_from_dict,
    -    dmp_to_dict,
    -    dmp_deflate,
    -    dmp_inject, dmp_eject,
    -    dmp_terms_gcd,
    -    dmp_list_terms, dmp_exclude,
    -    dmp_slice_in, dmp_permute,
    -    dmp_to_tuple,)
    -
    -from sympy.polys.densearith import (
    -    dmp_add_ground,
    -    dmp_sub_ground,
    -    dmp_mul_ground,
    -    dmp_quo_ground,
    -    dmp_exquo_ground,
    -    dmp_abs,
    -    dup_neg, dmp_neg,
    -    dup_add, dmp_add,
    -    dup_sub, dmp_sub,
    -    dup_mul, dmp_mul,
    -    dmp_sqr,
    -    dup_pow, dmp_pow,
    -    dmp_pdiv,
    -    dmp_prem,
    -    dmp_pquo,
    -    dmp_pexquo,
    -    dmp_div,
    -    dup_rem, dmp_rem,
    -    dmp_quo,
    -    dmp_exquo,
    -    dmp_add_mul, dmp_sub_mul,
    -    dmp_max_norm,
    -    dmp_l1_norm)
    -
    -from sympy.polys.densetools import (
    -    dmp_clear_denoms,
    -    dmp_integrate_in,
    -    dmp_diff_in,
    -    dmp_eval_in,
    -    dup_revert,
    -    dmp_ground_trunc,
    -    dmp_ground_content,
    -    dmp_ground_primitive,
    -    dmp_ground_monic,
    -    dmp_compose,
    -    dup_decompose,
    -    dup_shift,
    -    dmp_lift)
    -
    -from sympy.polys.euclidtools import (
    -    dup_half_gcdex, dup_gcdex, dup_invert,
    -    dmp_subresultants,
    -    dmp_resultant,
    -    dmp_discriminant,
    -    dmp_inner_gcd,
    -    dmp_gcd,
    -    dmp_lcm,
    -    dmp_cancel)
    -
    -from sympy.polys.sqfreetools import (
    -    dup_gff_list,
    -    dmp_sqf_p,
    -    dmp_sqf_norm,
    -    dmp_sqf_part,
    -    dmp_sqf_list, dmp_sqf_list_include)
    -
    -from sympy.polys.factortools import (
    -    dup_cyclotomic_p, dmp_irreducible_p,
    -    dmp_factor_list, dmp_factor_list_include)
    -
    -from sympy.polys.rootisolation import (
    -    dup_isolate_real_roots_sqf,
    -    dup_isolate_real_roots,
    -    dup_isolate_all_roots_sqf,
    -    dup_isolate_all_roots,
    -    dup_refine_real_root,
    -    dup_count_real_roots,
    -    dup_count_complex_roots,
    -    dup_sturm)
    -
    -from sympy.polys.polyerrors import (
    -    UnificationFailed,
    -    PolynomialError)
    -
    -
    -def init_normal_DMP(rep, lev, dom):
    -    return DMP(dmp_normal(rep, lev, dom), dom, lev)
    -
    -
    -
    [docs]class DMP(PicklableWithSlots): - """Dense Multivariate Polynomials over `K`. """ - - __slots__ = ['rep', 'lev', 'dom', 'ring'] - - def __init__(self, rep, dom, lev=None, ring=None): - if lev is not None: - if type(rep) is dict: - rep = dmp_from_dict(rep, lev, dom) - elif type(rep) is not list: - rep = dmp_ground(dom.convert(rep), lev) - else: - rep, lev = dmp_validate(rep) - - self.rep = rep - self.lev = lev - self.dom = dom - self.ring = ring - - def __repr__(f): - return "%s(%s, %s, %s)" % (f.__class__.__name__, f.rep, f.dom, f.ring) - - def __hash__(f): - return hash((f.__class__.__name__, f.to_tuple(), f.lev, f.dom, f.ring)) - -
    [docs] def unify(f, g): - """Unify representations of two multivariate polynomials. """ - if not isinstance(g, DMP) or f.lev != g.lev: - raise UnificationFailed("can't unify %s with %s" % (f, g)) - - if f.dom == g.dom and f.ring == g.ring: - return f.lev, f.dom, f.per, f.rep, g.rep - else: - lev, dom = f.lev, f.dom.unify(g.dom) - ring = f.ring - if g.ring is not None: - if ring is not None: - ring = ring.unify(g.ring) - else: - ring = g.ring - - F = dmp_convert(f.rep, lev, f.dom, dom) - G = dmp_convert(g.rep, lev, g.dom, dom) - - def per(rep, dom=dom, lev=lev, kill=False): - if kill: - if not lev: - return rep - else: - lev -= 1 - - return DMP(rep, dom, lev, ring) - - return lev, dom, per, F, G -
    -
    [docs] def per(f, rep, dom=None, kill=False, ring=None): - """Create a DMP out of the given representation. """ - lev = f.lev - - if kill: - if not lev: - return rep - else: - lev -= 1 - - if dom is None: - dom = f.dom - - if ring is None: - ring = f.ring - - return DMP(rep, dom, lev, ring) -
    - @classmethod - def zero(cls, lev, dom, ring=None): - return DMP(0, dom, lev, ring) - - @classmethod - def one(cls, lev, dom, ring=None): - return DMP(1, dom, lev, ring) - - @classmethod -
    [docs] def from_list(cls, rep, lev, dom): - """Create an instance of ``cls`` given a list of native coefficients. """ - return cls(dmp_convert(rep, lev, None, dom), dom, lev) -
    - @classmethod -
    [docs] def from_sympy_list(cls, rep, lev, dom): - """Create an instance of ``cls`` given a list of SymPy coefficients. """ - return cls(dmp_from_sympy(rep, lev, dom), dom, lev) -
    -
    [docs] def to_dict(f, zero=False): - """Convert ``f`` to a dict representation with native coefficients. """ - return dmp_to_dict(f.rep, f.lev, f.dom, zero=zero) -
    -
    [docs] def to_sympy_dict(f, zero=False): - """Convert ``f`` to a dict representation with SymPy coefficients. """ - rep = dmp_to_dict(f.rep, f.lev, f.dom, zero=zero) - - for k, v in rep.items(): - rep[k] = f.dom.to_sympy(v) - - return rep -
    -
    [docs] def to_tuple(f): - """ - Convert ``f`` to a tuple representation with native coefficients. - - This is needed for hashing. - """ - return dmp_to_tuple(f.rep, f.lev) -
    - @classmethod -
    [docs] def from_dict(cls, rep, lev, dom): - """Construct and instance of ``cls`` from a ``dict`` representation. """ - return cls(dmp_from_dict(rep, lev, dom), dom, lev) -
    - @classmethod - def from_monoms_coeffs(cls, monoms, coeffs, lev, dom, ring=None): - return DMP(dict(list(zip(monoms, coeffs))), dom, lev, ring) - -
    [docs] def to_ring(f): - """Make the ground domain a ring. """ - return f.convert(f.dom.get_ring()) -
    -
    [docs] def to_field(f): - """Make the ground domain a field. """ - return f.convert(f.dom.get_field()) -
    -
    [docs] def to_exact(f): - """Make the ground domain exact. """ - return f.convert(f.dom.get_exact()) -
    -
    [docs] def convert(f, dom): - """Convert the ground domain of ``f``. """ - if f.dom == dom: - return f - else: - return DMP(dmp_convert(f.rep, f.lev, f.dom, dom), dom, f.lev) -
    -
    [docs] def slice(f, m, n, j=0): - """Take a continuous subsequence of terms of ``f``. """ - return f.per(dmp_slice_in(f.rep, m, n, j, f.lev, f.dom)) -
    -
    [docs] def coeffs(f, order=None): - """Returns all non-zero coefficients from ``f`` in lex order. """ - return [ c for _, c in dmp_list_terms(f.rep, f.lev, f.dom, order=order) ] -
    -
    [docs] def monoms(f, order=None): - """Returns all non-zero monomials from ``f`` in lex order. """ - return [ m for m, _ in dmp_list_terms(f.rep, f.lev, f.dom, order=order) ] -
    -
    [docs] def terms(f, order=None): - """Returns all non-zero terms from ``f`` in lex order. """ - return dmp_list_terms(f.rep, f.lev, f.dom, order=order) -
    -
    [docs] def all_coeffs(f): - """Returns all coefficients from ``f``. """ - if not f.lev: - if not f: - return [f.dom.zero] - else: - return [ c for c in f.rep ] - else: - raise PolynomialError('multivariate polynomials not supported') -
    -
    [docs] def all_monoms(f): - """Returns all monomials from ``f``. """ - if not f.lev: - n = dup_degree(f.rep) - - if n < 0: - return [(0,)] - else: - return [ (n - i,) for i, c in enumerate(f.rep) ] - else: - raise PolynomialError('multivariate polynomials not supported') -
    -
    [docs] def all_terms(f): - """Returns all terms from a ``f``. """ - if not f.lev: - n = dup_degree(f.rep) - - if n < 0: - return [((0,), f.dom.zero)] - else: - return [ ((n - i,), c) for i, c in enumerate(f.rep) ] - else: - raise PolynomialError('multivariate polynomials not supported') -
    -
    [docs] def lift(f): - """Convert algebraic coefficients to rationals. """ - return f.per(dmp_lift(f.rep, f.lev, f.dom), dom=f.dom.dom) -
    -
    [docs] def deflate(f): - """Reduce degree of `f` by mapping `x_i^m` to `y_i`. """ - J, F = dmp_deflate(f.rep, f.lev, f.dom) - return J, f.per(F) -
    -
    [docs] def inject(f, front=False): - """Inject ground domain generators into ``f``. """ - F, lev = dmp_inject(f.rep, f.lev, f.dom, front=front) - return f.__class__(F, f.dom.dom, lev) -
    -
    [docs] def eject(f, dom, front=False): - """Eject selected generators into the ground domain. """ - F = dmp_eject(f.rep, f.lev, dom, front=front) - return f.__class__(F, dom, f.lev - len(dom.gens)) -
    -
    [docs] def exclude(f): - r""" - Remove useless generators from ``f``. - - Returns the removed generators and the new excluded ``f``. - - Examples - ======== - - >>> from sympy.polys.polyclasses import DMP - >>> from sympy.polys.domains import ZZ - - >>> DMP([[[ZZ(1)]], [[ZZ(1)], [ZZ(2)]]], ZZ).exclude() - ([2], DMP([[1], [1, 2]], ZZ, None)) - - """ - J, F, u = dmp_exclude(f.rep, f.lev, f.dom) - return J, f.__class__(F, f.dom, u) -
    -
    [docs] def permute(f, P): - r""" - Returns a polynomial in `K[x_{P(1)}, ..., x_{P(n)}]`. - - Examples - ======== - - >>> from sympy.polys.polyclasses import DMP - >>> from sympy.polys.domains import ZZ - - >>> DMP([[[ZZ(2)], [ZZ(1), ZZ(0)]], [[]]], ZZ).permute([1, 0, 2]) - DMP([[[2], []], [[1, 0], []]], ZZ, None) - - >>> DMP([[[ZZ(2)], [ZZ(1), ZZ(0)]], [[]]], ZZ).permute([1, 2, 0]) - DMP([[[1], []], [[2, 0], []]], ZZ, None) - - """ - return f.per(dmp_permute(f.rep, P, f.lev, f.dom)) -
    -
    [docs] def terms_gcd(f): - """Remove GCD of terms from the polynomial ``f``. """ - J, F = dmp_terms_gcd(f.rep, f.lev, f.dom) - return J, f.per(F) -
    -
    [docs] def add_ground(f, c): - """Add an element of the ground domain to ``f``. """ - return f.per(dmp_add_ground(f.rep, f.dom.convert(c), f.lev, f.dom)) -
    -
    [docs] def sub_ground(f, c): - """Subtract an element of the ground domain from ``f``. """ - return f.per(dmp_sub_ground(f.rep, f.dom.convert(c), f.lev, f.dom)) -
    -
    [docs] def mul_ground(f, c): - """Multiply ``f`` by a an element of the ground domain. """ - return f.per(dmp_mul_ground(f.rep, f.dom.convert(c), f.lev, f.dom)) -
    -
    [docs] def quo_ground(f, c): - """Quotient of ``f`` by a an element of the ground domain. """ - return f.per(dmp_quo_ground(f.rep, f.dom.convert(c), f.lev, f.dom)) -
    -
    [docs] def exquo_ground(f, c): - """Exact quotient of ``f`` by a an element of the ground domain. """ - return f.per(dmp_exquo_ground(f.rep, f.dom.convert(c), f.lev, f.dom)) -
    -
    [docs] def abs(f): - """Make all coefficients in ``f`` positive. """ - return f.per(dmp_abs(f.rep, f.lev, f.dom)) -
    -
    [docs] def neg(f): - """Negate all cefficients in ``f``. """ - return f.per(dmp_neg(f.rep, f.lev, f.dom)) -
    -
    [docs] def add(f, g): - """Add two multivariate polynomials ``f`` and ``g``. """ - lev, dom, per, F, G = f.unify(g) - return per(dmp_add(F, G, lev, dom)) -
    -
    [docs] def sub(f, g): - """Subtract two multivariate polynomials ``f`` and ``g``. """ - lev, dom, per, F, G = f.unify(g) - return per(dmp_sub(F, G, lev, dom)) -
    -
    [docs] def mul(f, g): - """Multiply two multivariate polynomials ``f`` and ``g``. """ - lev, dom, per, F, G = f.unify(g) - return per(dmp_mul(F, G, lev, dom)) -
    -
    [docs] def sqr(f): - """Square a multivariate polynomial ``f``. """ - return f.per(dmp_sqr(f.rep, f.lev, f.dom)) -
    -
    [docs] def pow(f, n): - """Raise ``f`` to a non-negative power ``n``. """ - if isinstance(n, int): - return f.per(dmp_pow(f.rep, n, f.lev, f.dom)) - else: - raise TypeError("``int`` expected, got %s" % type(n)) -
    -
    [docs] def pdiv(f, g): - """Polynomial pseudo-division of ``f`` and ``g``. """ - lev, dom, per, F, G = f.unify(g) - q, r = dmp_pdiv(F, G, lev, dom) - return per(q), per(r) -
    -
    [docs] def prem(f, g): - """Polynomial pseudo-remainder of ``f`` and ``g``. """ - lev, dom, per, F, G = f.unify(g) - return per(dmp_prem(F, G, lev, dom)) -
    -
    [docs] def pquo(f, g): - """Polynomial pseudo-quotient of ``f`` and ``g``. """ - lev, dom, per, F, G = f.unify(g) - return per(dmp_pquo(F, G, lev, dom)) -
    -
    [docs] def pexquo(f, g): - """Polynomial exact pseudo-quotient of ``f`` and ``g``. """ - lev, dom, per, F, G = f.unify(g) - return per(dmp_pexquo(F, G, lev, dom)) -
    -
    [docs] def div(f, g): - """Polynomial division with remainder of ``f`` and ``g``. """ - lev, dom, per, F, G = f.unify(g) - q, r = dmp_div(F, G, lev, dom) - return per(q), per(r) -
    -
    [docs] def rem(f, g): - """Computes polynomial remainder of ``f`` and ``g``. """ - lev, dom, per, F, G = f.unify(g) - return per(dmp_rem(F, G, lev, dom)) -
    -
    [docs] def quo(f, g): - """Computes polynomial quotient of ``f`` and ``g``. """ - lev, dom, per, F, G = f.unify(g) - return per(dmp_quo(F, G, lev, dom)) -
    -
    [docs] def exquo(f, g): - """Computes polynomial exact quotient of ``f`` and ``g``. """ - lev, dom, per, F, G = f.unify(g) - res = per(dmp_exquo(F, G, lev, dom)) - if f.ring and res not in f.ring: - from sympy.polys.polyerrors import ExactQuotientFailed - raise ExactQuotientFailed(f, g, f.ring) - return res -
    -
    [docs] def degree(f, j=0): - """Returns the leading degree of ``f`` in ``x_j``. """ - if isinstance(j, int): - return dmp_degree_in(f.rep, j, f.lev) - else: - raise TypeError("``int`` expected, got %s" % type(j)) -
    -
    [docs] def degree_list(f): - """Returns a list of degrees of ``f``. """ - return dmp_degree_list(f.rep, f.lev) -
    -
    [docs] def total_degree(f): - """Returns the total degree of ``f``. """ - return max([sum(m) for m in f.monoms()]) -
    -
    [docs] def homogeneous_order(f): - """Returns the homogeneous order of ``f``. """ - if f.is_zero: - return -1 - - monoms = f.monoms() - tdeg = sum(monoms[0]) - - for monom in monoms: - _tdeg = sum(monom) - - if _tdeg != tdeg: - return None - - return tdeg -
    -
    [docs] def LC(f): - """Returns the leading coefficient of ``f``. """ - return dmp_ground_LC(f.rep, f.lev, f.dom) -
    -
    [docs] def TC(f): - """Returns the trailing coefficient of ``f``. """ - return dmp_ground_TC(f.rep, f.lev, f.dom) -
    -
    [docs] def nth(f, *N): - """Returns the ``n``-th coefficient of ``f``. """ - if all(isinstance(n, int) for n in N): - return dmp_ground_nth(f.rep, N, f.lev, f.dom) - else: - raise TypeError("a sequence of integers expected") -
    -
    [docs] def max_norm(f): - """Returns maximum norm of ``f``. """ - return dmp_max_norm(f.rep, f.lev, f.dom) -
    -
    [docs] def l1_norm(f): - """Returns l1 norm of ``f``. """ - return dmp_l1_norm(f.rep, f.lev, f.dom) -
    -
    [docs] def clear_denoms(f): - """Clear denominators, but keep the ground domain. """ - coeff, F = dmp_clear_denoms(f.rep, f.lev, f.dom) - return coeff, f.per(F) -
    -
    [docs] def integrate(f, m=1, j=0): - """Computes the ``m``-th order indefinite integral of ``f`` in ``x_j``. """ - if not isinstance(m, int): - raise TypeError("``int`` expected, got %s" % type(m)) - - if not isinstance(j, int): - raise TypeError("``int`` expected, got %s" % type(j)) - - return f.per(dmp_integrate_in(f.rep, m, j, f.lev, f.dom)) -
    -
    [docs] def diff(f, m=1, j=0): - """Computes the ``m``-th order derivative of ``f`` in ``x_j``. """ - if not isinstance(m, int): - raise TypeError("``int`` expected, got %s" % type(m)) - - if not isinstance(j, int): - raise TypeError("``int`` expected, got %s" % type(j)) - - return f.per(dmp_diff_in(f.rep, m, j, f.lev, f.dom)) -
    -
    [docs] def eval(f, a, j=0): - """Evaluates ``f`` at the given point ``a`` in ``x_j``. """ - if not isinstance(j, int): - raise TypeError("``int`` expected, got %s" % type(j)) - - return f.per(dmp_eval_in(f.rep, - f.dom.convert(a), j, f.lev, f.dom), kill=True) -
    -
    [docs] def half_gcdex(f, g): - """Half extended Euclidean algorithm, if univariate. """ - lev, dom, per, F, G = f.unify(g) - - if not lev: - s, h = dup_half_gcdex(F, G, dom) - return per(s), per(h) - else: - raise ValueError('univariate polynomial expected') -
    -
    [docs] def gcdex(f, g): - """Extended Euclidean algorithm, if univariate. """ - lev, dom, per, F, G = f.unify(g) - - if not lev: - s, t, h = dup_gcdex(F, G, dom) - return per(s), per(t), per(h) - else: - raise ValueError('univariate polynomial expected') -
    -
    [docs] def invert(f, g): - """Invert ``f`` modulo ``g``, if possible. """ - lev, dom, per, F, G = f.unify(g) - - if not lev: - return per(dup_invert(F, G, dom)) - else: - raise ValueError('univariate polynomial expected') -
    -
    [docs] def revert(f, n): - """Compute ``f**(-1)`` mod ``x**n``. """ - if not f.lev: - return f.per(dup_revert(f.rep, n, f.dom)) - else: - raise ValueError('univariate polynomial expected') -
    -
    [docs] def subresultants(f, g): - """Computes subresultant PRS sequence of ``f`` and ``g``. """ - lev, dom, per, F, G = f.unify(g) - R = dmp_subresultants(F, G, lev, dom) - return list(map(per, R)) -
    -
    [docs] def resultant(f, g, includePRS=False): - """Computes resultant of ``f`` and ``g`` via PRS. """ - lev, dom, per, F, G = f.unify(g) - if includePRS: - res, R = dmp_resultant(F, G, lev, dom, includePRS=includePRS) - return per(res, kill=True), list(map(per, R)) - return per(dmp_resultant(F, G, lev, dom), kill=True) -
    -
    [docs] def discriminant(f): - """Computes discriminant of ``f``. """ - return f.per(dmp_discriminant(f.rep, f.lev, f.dom), kill=True) -
    -
    [docs] def cofactors(f, g): - """Returns GCD of ``f`` and ``g`` and their cofactors. """ - lev, dom, per, F, G = f.unify(g) - h, cff, cfg = dmp_inner_gcd(F, G, lev, dom) - return per(h), per(cff), per(cfg) -
    -
    [docs] def gcd(f, g): - """Returns polynomial GCD of ``f`` and ``g``. """ - lev, dom, per, F, G = f.unify(g) - return per(dmp_gcd(F, G, lev, dom)) -
    -
    [docs] def lcm(f, g): - """Returns polynomial LCM of ``f`` and ``g``. """ - lev, dom, per, F, G = f.unify(g) - return per(dmp_lcm(F, G, lev, dom)) -
    -
    [docs] def cancel(f, g, include=True): - """Cancel common factors in a rational function ``f/g``. """ - lev, dom, per, F, G = f.unify(g) - - if include: - F, G = dmp_cancel(F, G, lev, dom, include=True) - else: - cF, cG, F, G = dmp_cancel(F, G, lev, dom, include=False) - - F, G = per(F), per(G) - - if include: - return F, G - else: - return cF, cG, F, G -
    -
    [docs] def trunc(f, p): - """Reduce ``f`` modulo a constant ``p``. """ - return f.per(dmp_ground_trunc(f.rep, f.dom.convert(p), f.lev, f.dom)) -
    -
    [docs] def monic(f): - """Divides all coefficients by ``LC(f)``. """ - return f.per(dmp_ground_monic(f.rep, f.lev, f.dom)) -
    -
    [docs] def content(f): - """Returns GCD of polynomial coefficients. """ - return dmp_ground_content(f.rep, f.lev, f.dom) -
    -
    [docs] def primitive(f): - """Returns content and a primitive form of ``f``. """ - cont, F = dmp_ground_primitive(f.rep, f.lev, f.dom) - return cont, f.per(F) -
    -
    [docs] def compose(f, g): - """Computes functional composition of ``f`` and ``g``. """ - lev, dom, per, F, G = f.unify(g) - return per(dmp_compose(F, G, lev, dom)) -
    -
    [docs] def decompose(f): - """Computes functional decomposition of ``f``. """ - if not f.lev: - return list(map(f.per, dup_decompose(f.rep, f.dom))) - else: - raise ValueError('univariate polynomial expected') -
    -
    [docs] def shift(f, a): - """Efficiently compute Taylor shift ``f(x + a)``. """ - if not f.lev: - return f.per(dup_shift(f.rep, f.dom.convert(a), f.dom)) - else: - raise ValueError('univariate polynomial expected') -
    -
    [docs] def sturm(f): - """Computes the Sturm sequence of ``f``. """ - if not f.lev: - return list(map(f.per, dup_sturm(f.rep, f.dom))) - else: - raise ValueError('univariate polynomial expected') -
    -
    [docs] def gff_list(f): - """Computes greatest factorial factorization of ``f``. """ - if not f.lev: - return [ (f.per(g), k) for g, k in dup_gff_list(f.rep, f.dom) ] - else: - raise ValueError('univariate polynomial expected') -
    -
    [docs] def sqf_norm(f): - """Computes square-free norm of ``f``. """ - s, g, r = dmp_sqf_norm(f.rep, f.lev, f.dom) - return s, f.per(g), f.per(r, dom=f.dom.dom) -
    -
    [docs] def sqf_part(f): - """Computes square-free part of ``f``. """ - return f.per(dmp_sqf_part(f.rep, f.lev, f.dom)) -
    -
    [docs] def sqf_list(f, all=False): - """Returns a list of square-free factors of ``f``. """ - coeff, factors = dmp_sqf_list(f.rep, f.lev, f.dom, all) - return coeff, [ (f.per(g), k) for g, k in factors ] -
    -
    [docs] def sqf_list_include(f, all=False): - """Returns a list of square-free factors of ``f``. """ - factors = dmp_sqf_list_include(f.rep, f.lev, f.dom, all) - return [ (f.per(g), k) for g, k in factors ] -
    -
    [docs] def factor_list(f): - """Returns a list of irreducible factors of ``f``. """ - coeff, factors = dmp_factor_list(f.rep, f.lev, f.dom) - return coeff, [ (f.per(g), k) for g, k in factors ] -
    -
    [docs] def factor_list_include(f): - """Returns a list of irreducible factors of ``f``. """ - factors = dmp_factor_list_include(f.rep, f.lev, f.dom) - return [ (f.per(g), k) for g, k in factors ] -
    -
    [docs] def intervals(f, all=False, eps=None, inf=None, sup=None, fast=False, sqf=False): - """Compute isolating intervals for roots of ``f``. """ - if not f.lev: - if not all: - if not sqf: - return dup_isolate_real_roots(f.rep, f.dom, eps=eps, inf=inf, sup=sup, fast=fast) - else: - return dup_isolate_real_roots_sqf(f.rep, f.dom, eps=eps, inf=inf, sup=sup, fast=fast) - else: - if not sqf: - return dup_isolate_all_roots(f.rep, f.dom, eps=eps, inf=inf, sup=sup, fast=fast) - else: - return dup_isolate_all_roots_sqf(f.rep, f.dom, eps=eps, inf=inf, sup=sup, fast=fast) - else: - raise PolynomialError( - "can't isolate roots of a multivariate polynomial") -
    -
    [docs] def refine_root(f, s, t, eps=None, steps=None, fast=False): - """ - Refine an isolating interval to the given precision. - - ``eps`` should be a rational number. - - """ - if not f.lev: - return dup_refine_real_root(f.rep, s, t, f.dom, eps=eps, steps=steps, fast=fast) - else: - raise PolynomialError( - "can't refine a root of a multivariate polynomial") -
    -
    [docs] def count_real_roots(f, inf=None, sup=None): - """Return the number of real roots of ``f`` in ``[inf, sup]``. """ - return dup_count_real_roots(f.rep, f.dom, inf=inf, sup=sup) -
    -
    [docs] def count_complex_roots(f, inf=None, sup=None): - """Return the number of complex roots of ``f`` in ``[inf, sup]``. """ - return dup_count_complex_roots(f.rep, f.dom, inf=inf, sup=sup) -
    - @property -
    [docs] def is_zero(f): - """Returns ``True`` if ``f`` is a zero polynomial. """ - return dmp_zero_p(f.rep, f.lev) -
    - @property -
    [docs] def is_one(f): - """Returns ``True`` if ``f`` is a unit polynomial. """ - return dmp_one_p(f.rep, f.lev, f.dom) -
    - @property -
    [docs] def is_ground(f): - """Returns ``True`` if ``f`` is an element of the ground domain. """ - return dmp_ground_p(f.rep, None, f.lev) -
    - @property -
    [docs] def is_sqf(f): - """Returns ``True`` if ``f`` is a square-free polynomial. """ - return dmp_sqf_p(f.rep, f.lev, f.dom) -
    - @property -
    [docs] def is_monic(f): - """Returns ``True`` if the leading coefficient of ``f`` is one. """ - return f.dom.is_one(dmp_ground_LC(f.rep, f.lev, f.dom)) -
    - @property -
    [docs] def is_primitive(f): - """Returns ``True`` if the GCD of the coefficients of ``f`` is one. """ - return f.dom.is_one(dmp_ground_content(f.rep, f.lev, f.dom)) -
    - @property -
    [docs] def is_linear(f): - """Returns ``True`` if ``f`` is linear in all its variables. """ - return all(sum(monom) <= 1 for monom in list(dmp_to_dict(f.rep, f.lev, f.dom).keys())) -
    - @property -
    [docs] def is_quadratic(f): - """Returns ``True`` if ``f`` is quadratic in all its variables. """ - return all(sum(monom) <= 2 for monom in list(dmp_to_dict(f.rep, f.lev, f.dom).keys())) -
    - @property -
    [docs] def is_monomial(f): - """Returns ``True`` if ``f`` is zero or has only one term. """ - return len(f.to_dict()) <= 1 -
    - @property -
    [docs] def is_homogeneous(f): - """Returns ``True`` if ``f`` is a homogeneous polynomial. """ - return f.homogeneous_order() is not None -
    - @property -
    [docs] def is_irreducible(f): - """Returns ``True`` if ``f`` has no factors over its domain. """ - return dmp_irreducible_p(f.rep, f.lev, f.dom) -
    - @property -
    [docs] def is_cyclotomic(f): - """Returns ``True`` if ``f`` is a cyclotomic polynomial. """ - if not f.lev: - return dup_cyclotomic_p(f.rep, f.dom) - else: - return False -
    - def __abs__(f): - return f.abs() - - def __neg__(f): - return f.neg() - - def __add__(f, g): - if not isinstance(g, DMP): - try: - g = f.per(dmp_ground(f.dom.convert(g), f.lev)) - except TypeError: - return NotImplemented - except (CoercionFailed, NotImplementedError): - if f.ring is not None: - try: - g = f.ring.convert(g) - except (CoercionFailed, NotImplementedError): - return NotImplemented - - return f.add(g) - - def __radd__(f, g): - return f.__add__(g) - - def __sub__(f, g): - if not isinstance(g, DMP): - try: - g = f.per(dmp_ground(f.dom.convert(g), f.lev)) - except TypeError: - return NotImplemented - except (CoercionFailed, NotImplementedError): - if f.ring is not None: - try: - g = f.ring.convert(g) - except (CoercionFailed, NotImplementedError): - return NotImplemented - - return f.sub(g) - - def __rsub__(f, g): - return (-f).__add__(g) - - def __mul__(f, g): - if isinstance(g, DMP): - return f.mul(g) - else: - try: - return f.mul_ground(g) - except TypeError: - return NotImplemented - except (CoercionFailed, NotImplementedError): - if f.ring is not None: - try: - return f.mul(f.ring.convert(g)) - except (CoercionFailed, NotImplementedError): - pass - return NotImplemented - - def __div__(f, g): - if isinstance(g, DMP): - return f.exquo(g) - else: - try: - return f.mul_ground(g) - except TypeError: - return NotImplemented - except (CoercionFailed, NotImplementedError): - if f.ring is not None: - try: - return f.exquo(f.ring.convert(g)) - except (CoercionFailed, NotImplementedError): - pass - return NotImplemented - - def __rdiv__(f, g): - if isinstance(g, DMP): - return g.exquo(f) - elif f.ring is not None: - try: - return f.ring.convert(g).exquo(f) - except (CoercionFailed, NotImplementedError): - pass - return NotImplemented - - __truediv__ = __div__ - __rtruediv__ = __rdiv__ - - def __rmul__(f, g): - return f.__mul__(g) - - def __pow__(f, n): - return f.pow(n) - - def __divmod__(f, g): - return f.div(g) - - def __mod__(f, g): - return f.rem(g) - - def __floordiv__(f, g): - if isinstance(g, DMP): - return f.quo(g) - else: - try: - return f.quo_ground(g) - except TypeError: - return NotImplemented - - def __eq__(f, g): - try: - _, _, _, F, G = f.unify(g) - - if f.lev == g.lev: - return F == G - except UnificationFailed: - pass - - return False - - def __ne__(f, g): - return not f.__eq__(g) - - def eq(f, g, strict=False): - if not strict: - return f.__eq__(g) - else: - return f._strict_eq(g) - - def ne(f, g, strict=False): - return not f.eq(g, strict=strict) - - def _strict_eq(f, g): - return isinstance(g, f.__class__) and f.lev == g.lev \ - and f.dom == g.dom \ - and f.rep == g.rep - - def __lt__(f, g): - _, _, _, F, G = f.unify(g) - return F.__lt__(G) - - def __le__(f, g): - _, _, _, F, G = f.unify(g) - return F.__le__(G) - - def __gt__(f, g): - _, _, _, F, G = f.unify(g) - return F.__gt__(G) - - def __ge__(f, g): - _, _, _, F, G = f.unify(g) - return F.__ge__(G) - - def __bool__(f): - return not dmp_zero_p(f.rep, f.lev) - -
    -def init_normal_DMF(num, den, lev, dom): - return DMF(dmp_normal(num, lev, dom), - dmp_normal(den, lev, dom), dom, lev) - - -
    [docs]class DMF(PicklableWithSlots): - """Dense Multivariate Fractions over `K`. """ - - __slots__ = ['num', 'den', 'lev', 'dom', 'ring'] - - def __init__(self, rep, dom, lev=None, ring=None): - num, den, lev = self._parse(rep, dom, lev) - num, den = dmp_cancel(num, den, lev, dom) - - self.num = num - self.den = den - self.lev = lev - self.dom = dom - self.ring = ring - - @classmethod - def new(cls, rep, dom, lev=None, ring=None): - num, den, lev = cls._parse(rep, dom, lev) - - obj = object.__new__(cls) - - obj.num = num - obj.den = den - obj.lev = lev - obj.dom = dom - obj.ring = ring - - return obj - - @classmethod - def _parse(cls, rep, dom, lev=None): - if type(rep) is tuple: - num, den = rep - - if lev is not None: - if type(num) is dict: - num = dmp_from_dict(num, lev, dom) - - if type(den) is dict: - den = dmp_from_dict(den, lev, dom) - else: - num, num_lev = dmp_validate(num) - den, den_lev = dmp_validate(den) - - if num_lev == den_lev: - lev = num_lev - else: - raise ValueError('inconsistent number of levels') - - if dmp_zero_p(den, lev): - raise ZeroDivisionError('fraction denominator') - - if dmp_zero_p(num, lev): - den = dmp_one(lev, dom) - else: - if dmp_negative_p(den, lev, dom): - num = dmp_neg(num, lev, dom) - den = dmp_neg(den, lev, dom) - else: - num = rep - - if lev is not None: - if type(num) is dict: - num = dmp_from_dict(num, lev, dom) - elif type(num) is not list: - num = dmp_ground(dom.convert(num), lev) - else: - num, lev = dmp_validate(num) - - den = dmp_one(lev, dom) - - return num, den, lev - - def __repr__(f): - return "%s((%s, %s), %s, %s)" % (f.__class__.__name__, f.num, f.den, - f.dom, f.ring) - - def __hash__(f): - return hash((f.__class__.__name__, dmp_to_tuple(f.num, f.lev), - dmp_to_tuple(f.den, f.lev), f.lev, f.dom, f.ring)) - -
    [docs] def poly_unify(f, g): - """Unify a multivariate fraction and a polynomial. """ - if not isinstance(g, DMP) or f.lev != g.lev: - raise UnificationFailed("can't unify %s with %s" % (f, g)) - - if f.dom == g.dom and f.ring == g.ring: - return (f.lev, f.dom, f.per, (f.num, f.den), g.rep) - else: - lev, dom = f.lev, f.dom.unify(g.dom) - ring = f.ring - if g.ring is not None: - if ring is not None: - ring = ring.unify(g.ring) - else: - ring = g.ring - - F = (dmp_convert(f.num, lev, f.dom, dom), - dmp_convert(f.den, lev, f.dom, dom)) - - G = dmp_convert(g.rep, lev, g.dom, dom) - - def per(num, den, cancel=True, kill=False, lev=lev): - if kill: - if not lev: - return num/den - else: - lev = lev - 1 - - if cancel: - num, den = dmp_cancel(num, den, lev, dom) - - return f.__class__.new((num, den), dom, lev, ring=ring) - - return lev, dom, per, F, G -
    -
    [docs] def frac_unify(f, g): - """Unify representations of two multivariate fractions. """ - if not isinstance(g, DMF) or f.lev != g.lev: - raise UnificationFailed("can't unify %s with %s" % (f, g)) - - if f.dom == g.dom and f.ring == g.ring: - return (f.lev, f.dom, f.per, (f.num, f.den), - (g.num, g.den)) - else: - lev, dom = f.lev, f.dom.unify(g.dom) - ring = f.ring - if g.ring is not None: - if ring is not None: - ring = ring.unify(g.ring) - else: - ring = g.ring - - F = (dmp_convert(f.num, lev, f.dom, dom), - dmp_convert(f.den, lev, f.dom, dom)) - - G = (dmp_convert(g.num, lev, g.dom, dom), - dmp_convert(g.den, lev, g.dom, dom)) - - def per(num, den, cancel=True, kill=False, lev=lev): - if kill: - if not lev: - return num/den - else: - lev = lev - 1 - - if cancel: - num, den = dmp_cancel(num, den, lev, dom) - - return f.__class__.new((num, den), dom, lev, ring=ring) - - return lev, dom, per, F, G -
    -
    [docs] def per(f, num, den, cancel=True, kill=False, ring=None): - """Create a DMF out of the given representation. """ - lev, dom = f.lev, f.dom - - if kill: - if not lev: - return num/den - else: - lev -= 1 - - if cancel: - num, den = dmp_cancel(num, den, lev, dom) - - if ring is None: - ring = f.ring - - return f.__class__.new((num, den), dom, lev, ring=ring) -
    -
    [docs] def half_per(f, rep, kill=False): - """Create a DMP out of the given representation. """ - lev = f.lev - - if kill: - if not lev: - return rep - else: - lev -= 1 - - return DMP(rep, f.dom, lev) -
    - @classmethod - def zero(cls, lev, dom, ring=None): - return cls.new(0, dom, lev, ring=ring) - - @classmethod - def one(cls, lev, dom, ring=None): - return cls.new(1, dom, lev, ring=ring) - -
    [docs] def numer(f): - """Returns the numerator of ``f``. """ - return f.half_per(f.num) -
    -
    [docs] def denom(f): - """Returns the denominator of ``f``. """ - return f.half_per(f.den) -
    -
    [docs] def cancel(f): - """Remove common factors from ``f.num`` and ``f.den``. """ - return f.per(f.num, f.den) -
    -
    [docs] def neg(f): - """Negate all cefficients in ``f``. """ - return f.per(dmp_neg(f.num, f.lev, f.dom), f.den, cancel=False) -
    -
    [docs] def add(f, g): - """Add two multivariate fractions ``f`` and ``g``. """ - if isinstance(g, DMP): - lev, dom, per, (F_num, F_den), G = f.poly_unify(g) - num, den = dmp_add_mul(F_num, F_den, G, lev, dom), F_den - else: - lev, dom, per, F, G = f.frac_unify(g) - (F_num, F_den), (G_num, G_den) = F, G - - num = dmp_add(dmp_mul(F_num, G_den, lev, dom), - dmp_mul(F_den, G_num, lev, dom), lev, dom) - den = dmp_mul(F_den, G_den, lev, dom) - - return per(num, den) -
    -
    [docs] def sub(f, g): - """Subtract two multivariate fractions ``f`` and ``g``. """ - if isinstance(g, DMP): - lev, dom, per, (F_num, F_den), G = f.poly_unify(g) - num, den = dmp_sub_mul(F_num, F_den, G, lev, dom), F_den - else: - lev, dom, per, F, G = f.frac_unify(g) - (F_num, F_den), (G_num, G_den) = F, G - - num = dmp_sub(dmp_mul(F_num, G_den, lev, dom), - dmp_mul(F_den, G_num, lev, dom), lev, dom) - den = dmp_mul(F_den, G_den, lev, dom) - - return per(num, den) -
    -
    [docs] def mul(f, g): - """Multiply two multivariate fractions ``f`` and ``g``. """ - if isinstance(g, DMP): - lev, dom, per, (F_num, F_den), G = f.poly_unify(g) - num, den = dmp_mul(F_num, G, lev, dom), F_den - else: - lev, dom, per, F, G = f.frac_unify(g) - (F_num, F_den), (G_num, G_den) = F, G - - num = dmp_mul(F_num, G_num, lev, dom) - den = dmp_mul(F_den, G_den, lev, dom) - - return per(num, den) -
    -
    [docs] def pow(f, n): - """Raise ``f`` to a non-negative power ``n``. """ - if isinstance(n, int): - return f.per(dmp_pow(f.num, n, f.lev, f.dom), - dmp_pow(f.den, n, f.lev, f.dom), cancel=False) - else: - raise TypeError("``int`` expected, got %s" % type(n)) -
    -
    [docs] def quo(f, g): - """Computes quotient of fractions ``f`` and ``g``. """ - if isinstance(g, DMP): - lev, dom, per, (F_num, F_den), G = f.poly_unify(g) - num, den = F_num, dmp_mul(F_den, G, lev, dom) - else: - lev, dom, per, F, G = f.frac_unify(g) - (F_num, F_den), (G_num, G_den) = F, G - - num = dmp_mul(F_num, G_den, lev, dom) - den = dmp_mul(F_den, G_num, lev, dom) - - res = per(num, den) - if f.ring is not None and res not in f.ring: - from sympy.polys.polyerrors import ExactQuotientFailed - raise ExactQuotientFailed(f, g, f.ring) - return res -
    - exquo = quo - -
    [docs] def invert(f, check=True): - """Computes inverse of a fraction ``f``. """ - if check and f.ring is not None and not f.ring.is_unit(f): - raise NotReversible(f, f.ring) - res = f.per(f.den, f.num, cancel=False) - return res -
    - @property -
    [docs] def is_zero(f): - """Returns ``True`` if ``f`` is a zero fraction. """ - return dmp_zero_p(f.num, f.lev) -
    - @property -
    [docs] def is_one(f): - """Returns ``True`` if ``f`` is a unit fraction. """ - return dmp_one_p(f.num, f.lev, f.dom) and \ - dmp_one_p(f.den, f.lev, f.dom) -
    - def __neg__(f): - return f.neg() - - def __add__(f, g): - if isinstance(g, (DMP, DMF)): - return f.add(g) - - try: - return f.add(f.half_per(g)) - except TypeError: - return NotImplemented - except (CoercionFailed, NotImplementedError): - if f.ring is not None: - try: - return f.add(f.ring.convert(g)) - except (CoercionFailed, NotImplementedError): - pass - return NotImplemented - - def __radd__(f, g): - return f.__add__(g) - - def __sub__(f, g): - if isinstance(g, (DMP, DMF)): - return f.sub(g) - - try: - return f.sub(f.half_per(g)) - except TypeError: - return NotImplemented - except (CoercionFailed, NotImplementedError): - if f.ring is not None: - try: - return f.sub(f.ring.convert(g)) - except (CoercionFailed, NotImplementedError): - pass - return NotImplemented - - def __rsub__(f, g): - return (-f).__add__(g) - - def __mul__(f, g): - if isinstance(g, (DMP, DMF)): - return f.mul(g) - - try: - return f.mul(f.half_per(g)) - except TypeError: - return NotImplemented - except (CoercionFailed, NotImplementedError): - if f.ring is not None: - try: - return f.mul(f.ring.convert(g)) - except (CoercionFailed, NotImplementedError): - pass - return NotImplemented - - def __rmul__(f, g): - return f.__mul__(g) - - def __pow__(f, n): - return f.pow(n) - - def __div__(f, g): - if isinstance(g, (DMP, DMF)): - return f.quo(g) - - try: - return f.quo(f.half_per(g)) - except TypeError: - return NotImplemented - except (CoercionFailed, NotImplementedError): - if f.ring is not None: - try: - return f.quo(f.ring.convert(g)) - except (CoercionFailed, NotImplementedError): - pass - return NotImplemented - - def __rdiv__(self, g): - r = self.invert(check=False)*g - if self.ring and r not in self.ring: - from sympy.polys.polyerrors import ExactQuotientFailed - raise ExactQuotientFailed(g, self, self.ring) - return r - - __truediv__ = __div__ - __rtruediv__ = __rdiv__ - - def __eq__(f, g): - try: - if isinstance(g, DMP): - _, _, _, (F_num, F_den), G = f.poly_unify(g) - - if f.lev == g.lev: - return dmp_one_p(F_den, f.lev, f.dom) and F_num == G - else: - _, _, _, F, G = f.frac_unify(g) - - if f.lev == g.lev: - return F == G - except UnificationFailed: - pass - - return False - - def __ne__(f, g): - try: - if isinstance(g, DMP): - _, _, _, (F_num, F_den), G = f.poly_unify(g) - - if f.lev == g.lev: - return not (dmp_one_p(F_den, f.lev, f.dom) and F_num == G) - else: - _, _, _, F, G = f.frac_unify(g) - - if f.lev == g.lev: - return F != G - except UnificationFailed: - pass - - return True - - def __lt__(f, g): - _, _, _, F, G = f.frac_unify(g) - return F.__lt__(G) - - def __le__(f, g): - _, _, _, F, G = f.frac_unify(g) - return F.__le__(G) - - def __gt__(f, g): - _, _, _, F, G = f.frac_unify(g) - return F.__gt__(G) - - def __ge__(f, g): - _, _, _, F, G = f.frac_unify(g) - return F.__ge__(G) - - def __bool__(f): - return not dmp_zero_p(f.num, f.lev) - -
    -def init_normal_ANP(rep, mod, dom): - return ANP(dup_normal(rep, dom), - dup_normal(mod, dom), dom) - - -
    [docs]class ANP(PicklableWithSlots): - """Dense Algebraic Number Polynomials over a field. """ - - __slots__ = ['rep', 'mod', 'dom'] - - def __init__(self, rep, mod, dom): - if type(rep) is dict: - self.rep = dup_from_dict(rep, dom) - else: - if type(rep) is not list: - rep = [dom.convert(rep)] - - self.rep = dup_strip(rep) - - if isinstance(mod, DMP): - self.mod = mod.rep - else: - if type(mod) is dict: - self.mod = dup_from_dict(mod, dom) - else: - self.mod = dup_strip(mod) - - self.dom = dom - - def __repr__(f): - return "%s(%s, %s, %s)" % (f.__class__.__name__, f.rep, f.mod, f.dom) - - def __hash__(f): - return hash((f.__class__.__name__, f.to_tuple(), dmp_to_tuple(f.mod, 0), f.dom)) - -
    [docs] def unify(f, g): - """Unify representations of two algebraic numbers. """ - if not isinstance(g, ANP) or f.mod != g.mod: - raise UnificationFailed("can't unify %s with %s" % (f, g)) - - if f.dom == g.dom: - return f.dom, f.per, f.rep, g.rep, f.mod - else: - dom = f.dom.unify(g.dom) - - F = dup_convert(f.rep, f.dom, dom) - G = dup_convert(g.rep, g.dom, dom) - - if dom != f.dom and dom != g.dom: - mod = dup_convert(f.mod, f.dom, dom) - else: - if dom == f.dom: - mod = f.mod - else: - mod = g.mod - - per = lambda rep: ANP(rep, mod, dom) - - return dom, per, F, G, mod -
    - def per(f, rep, mod=None, dom=None): - return ANP(rep, mod or f.mod, dom or f.dom) - - @classmethod - def zero(cls, mod, dom): - return ANP(0, mod, dom) - - @classmethod - def one(cls, mod, dom): - return ANP(1, mod, dom) - -
    [docs] def to_dict(f): - """Convert ``f`` to a dict representation with native coefficients. """ - return dmp_to_dict(f.rep, 0, f.dom) -
    -
    [docs] def to_sympy_dict(f): - """Convert ``f`` to a dict representation with SymPy coefficients. """ - rep = dmp_to_dict(f.rep, 0, f.dom) - - for k, v in rep.items(): - rep[k] = f.dom.to_sympy(v) - - return rep -
    -
    [docs] def to_list(f): - """Convert ``f`` to a list representation with native coefficients. """ - return f.rep -
    -
    [docs] def to_sympy_list(f): - """Convert ``f`` to a list representation with SymPy coefficients. """ - return [ f.dom.to_sympy(c) for c in f.rep ] -
    -
    [docs] def to_tuple(f): - """ - Convert ``f`` to a tuple representation with native coefficients. - - This is needed for hashing. - """ - return dmp_to_tuple(f.rep, 0) -
    - @classmethod - def from_list(cls, rep, mod, dom): - return ANP(dup_strip(list(map(dom.convert, rep))), mod, dom) - - def neg(f): - return f.per(dup_neg(f.rep, f.dom)) - - def add(f, g): - dom, per, F, G, mod = f.unify(g) - return per(dup_add(F, G, dom)) - - def sub(f, g): - dom, per, F, G, mod = f.unify(g) - return per(dup_sub(F, G, dom)) - - def mul(f, g): - dom, per, F, G, mod = f.unify(g) - return per(dup_rem(dup_mul(F, G, dom), mod, dom)) - -
    [docs] def pow(f, n): - """Raise ``f`` to a non-negative power ``n``. """ - if isinstance(n, int): - if n < 0: - F, n = dup_invert(f.rep, f.mod, f.dom), -n - else: - F = f.rep - - return f.per(dup_rem(dup_pow(F, n, f.dom), f.mod, f.dom)) - else: - raise TypeError("``int`` expected, got %s" % type(n)) -
    - def div(f, g): - dom, per, F, G, mod = f.unify(g) - return (per(dup_rem(dup_mul(F, dup_invert(G, mod, dom), dom), mod, dom)), self.zero(mod, dom)) - - def rem(f, g): - dom, _, _, _, mod = f.unify(g) - return self.zero(mod, dom) - - def quo(f, g): - dom, per, F, G, mod = f.unify(g) - return per(dup_rem(dup_mul(F, dup_invert(G, mod, dom), dom), mod, dom)) - - exquo = quo - -
    [docs] def LC(f): - """Returns the leading coefficient of ``f``. """ - return dup_LC(f.rep, f.dom) -
    -
    [docs] def TC(f): - """Returns the trailing coefficient of ``f``. """ - return dup_TC(f.rep, f.dom) -
    - @property -
    [docs] def is_zero(f): - """Returns ``True`` if ``f`` is a zero algebraic number. """ - return not f -
    - @property -
    [docs] def is_one(f): - """Returns ``True`` if ``f`` is a unit algebraic number. """ - return f.rep == [f.dom.one] -
    - @property -
    [docs] def is_ground(f): - """Returns ``True`` if ``f`` is an element of the ground domain. """ - return not f.rep or len(f.rep) == 1 -
    - def __neg__(f): - return f.neg() - - def __add__(f, g): - if isinstance(g, ANP): - return f.add(g) - else: - try: - return f.add(f.per(g)) - except TypeError: - return NotImplemented - - def __radd__(f, g): - return f.__add__(g) - - def __sub__(f, g): - if isinstance(g, ANP): - return f.sub(g) - else: - try: - return f.sub(f.per(g)) - except TypeError: - return NotImplemented - - def __rsub__(f, g): - return (-f).__add__(g) - - def __mul__(f, g): - if isinstance(g, ANP): - return f.mul(g) - else: - try: - return f.mul(f.per(g)) - except TypeError: - return NotImplemented - - def __rmul__(f, g): - return f.__mul__(g) - - def __pow__(f, n): - return f.pow(n) - - def __divmod__(f, g): - return f.div(g) - - def __mod__(f, g): - return f.rem(g) - - def __div__(f, g): - if isinstance(g, ANP): - return f.quo(g) - else: - try: - return f.quo(f.per(g)) - except TypeError: - return NotImplemented - - __truediv__ = __div__ - - def __eq__(f, g): - try: - _, _, F, G, _ = f.unify(g) - - return F == G - except UnificationFailed: - return False - - def __ne__(f, g): - try: - _, _, F, G, _ = f.unify(g) - - return F != G - except UnificationFailed: - return True - - def __lt__(f, g): - _, _, F, G, _ = f.unify(g) - return F.__lt__(G) - - def __le__(f, g): - _, _, F, G, _ = f.unify(g) - return F.__le__(G) - - def __gt__(f, g): - _, _, F, G, _ = f.unify(g) - return F.__gt__(G) - - def __ge__(f, g): - _, _, F, G, _ = f.unify(g) - return F.__ge__(G) - - def __bool__(f): - return bool(f.rep)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/polys/polyerrors.html b/dev-py3k/_modules/sympy/polys/polyerrors.html deleted file mode 100644 index 6a19f83db42..00000000000 --- a/dev-py3k/_modules/sympy/polys/polyerrors.html +++ /dev/null @@ -1,261 +0,0 @@ - - - - - - - - - - sympy.polys.polyerrors — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.polys.polyerrors

    -"""Definitions of common exceptions for `polys` module. """
    -
    -
    -
    [docs]class BasePolynomialError(Exception): - """Base class for polynomial related exceptions. """ - - def new(self, *args): - raise NotImplementedError("abstract base class") - -
    -
    [docs]class ExactQuotientFailed(BasePolynomialError): - - def __init__(self, f, g, dom=None): - self.f, self.g, self.dom = f, g, dom - - def __str__(self): # pragma: no cover - from sympy.printing.str import sstr - - if self.dom is None: - return "%s does not divide %s" % (sstr(self.g), sstr(self.f)) - else: - return "%s does not divide %s in %s" % (sstr(self.g), sstr(self.f), sstr(self.dom)) - - def new(self, f, g): - return self.__class__(f, g, self.dom) - -
    -
    [docs]class OperationNotSupported(BasePolynomialError): - - def __init__(self, poly, func): - self.poly = poly - self.func = func - - def __str__(self): # pragma: no cover - return "`%s` operation not supported by %s representation" % (self.func, self.poly.rep.__class__.__name__) - -
    -
    [docs]class HeuristicGCDFailed(BasePolynomialError): - pass - -
    -
    [docs]class HomomorphismFailed(BasePolynomialError): - pass - -
    -
    [docs]class IsomorphismFailed(BasePolynomialError): - pass - -
    -
    [docs]class ExtraneousFactors(BasePolynomialError): - pass - -
    -
    [docs]class EvaluationFailed(BasePolynomialError): - pass - -
    -
    [docs]class RefinementFailed(BasePolynomialError): - pass - -
    -
    [docs]class CoercionFailed(BasePolynomialError): - pass - -
    -
    [docs]class NotInvertible(BasePolynomialError): - pass - -
    -
    [docs]class NotReversible(BasePolynomialError): - pass - -
    -
    [docs]class NotAlgebraic(BasePolynomialError): - pass - -
    -
    [docs]class DomainError(BasePolynomialError): - pass - -
    -
    [docs]class PolynomialError(BasePolynomialError): - pass - -
    -
    [docs]class UnificationFailed(BasePolynomialError): - pass - -
    -
    [docs]class GeneratorsNeeded(BasePolynomialError): - pass - -
    -
    [docs]class ComputationFailed(BasePolynomialError): - - def __init__(self, func, nargs, exc): - self.func = func - self.nargs = nargs - self.exc = exc - - def __str__(self): - return "%s(%s) failed without generators" % (self.func, ', '.join(map(str, self.exc.exprs[:self.nargs]))) - -
    -
    [docs]class GeneratorsError(BasePolynomialError): - pass - -
    -
    [docs]class UnivariatePolynomialError(PolynomialError): - pass - -
    -
    [docs]class MultivariatePolynomialError(PolynomialError): - pass - -
    -
    [docs]class PolificationFailed(PolynomialError): - - def __init__(self, opt, origs, exprs, seq=False): - if not seq: - self.orig = origs - self.expr = exprs - self.origs = [origs] - self.exprs = [exprs] - else: - self.origs = origs - self.exprs = exprs - - self.opt = opt - self.seq = seq - - def __str__(self): # pragma: no cover - if not self.seq: - return "can't construct a polynomial from %s" % str(self.orig) - else: - return "can't construct polynomials from %s" % ', '.join(map(str, self.origs)) - -
    -
    [docs]class OptionError(BasePolynomialError): - pass - -
    -
    [docs]class FlagError(OptionError): - pass
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/polys/polyfuncs.html b/dev-py3k/_modules/sympy/polys/polyfuncs.html deleted file mode 100644 index b841dd0a6f9..00000000000 --- a/dev-py3k/_modules/sympy/polys/polyfuncs.html +++ /dev/null @@ -1,420 +0,0 @@ - - - - - - - - - - sympy.polys.polyfuncs — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.polys.polyfuncs

    -"""High-level polynomials manipulation functions. """
    -
    -from sympy.polys.polytools import (
    -    poly_from_expr, parallel_poly_from_expr, Poly)
    -from sympy.polys.polyoptions import allowed_flags
    -
    -from sympy.polys.specialpolys import (
    -    symmetric_poly, interpolating_poly)
    -
    -from sympy.polys.polyerrors import (
    -    PolificationFailed, ComputationFailed,
    -    MultivariatePolynomialError)
    -
    -from sympy.utilities import numbered_symbols, take
    -
    -from sympy.core import S, Basic, Add, Mul
    -
    -
    -
    [docs]def symmetrize(F, *gens, **args): - """ - Rewrite a polynomial in terms of elementary symmetric polynomials. - - A symmetric polynomial is a multivariate polynomial that remains invariant - under any variable permutation, i.e., if ``f = f(x_1, x_2, ..., x_n)``, - then ``f = f(x_{i_1}, x_{i_2}, ..., x_{i_n})``, where - ``(i_1, i_2, ..., i_n)`` is a permutation of ``(1, 2, ..., n)`` (an - element of the group ``S_n``). - - Returns a tuple of symmetric polynomials ``(f1, f2, ..., fn)`` such that - ``f = f1 + f2 + ... + fn``. - - Examples - ======== - - >>> from sympy.polys.polyfuncs import symmetrize - >>> from sympy.abc import x, y - - >>> symmetrize(x**2 + y**2) - (-2*x*y + (x + y)**2, 0) - - >>> symmetrize(x**2 + y**2, formal=True) - (s1**2 - 2*s2, 0, [(s1, x + y), (s2, x*y)]) - - >>> symmetrize(x**2 - y**2) - (-2*x*y + (x + y)**2, -2*y**2) - - >>> symmetrize(x**2 - y**2, formal=True) - (s1**2 - 2*s2, -2*y**2, [(s1, x + y), (s2, x*y)]) - - """ - allowed_flags(args, ['formal', 'symbols']) - - iterable = True - - if not hasattr(F, '__iter__'): - iterable = False - F = [F] - - try: - F, opt = parallel_poly_from_expr(F, *gens, **args) - except PolificationFailed as exc: - result = [] - - for expr in exc.exprs: - if expr.is_Number: - result.append((expr, S.Zero)) - else: - raise ComputationFailed('symmetrize', len(F), exc) - else: - if not iterable: - result, = result - - if not exc.opt.formal: - return result - else: - if iterable: - return result, [] - else: - return result + ([],) - - polys, symbols = [], opt.symbols - gens, dom = opt.gens, opt.domain - - for i in range(0, len(gens)): - poly = symmetric_poly(i + 1, gens, polys=True) - polys.append((next(symbols), poly.set_domain(dom))) - - indices = list(range(0, len(gens) - 1)) - weights = list(range(len(gens), 0, -1)) - - result = [] - - for f in F: - symmetric = [] - - if not f.is_homogeneous: - symmetric.append(f.TC()) - f -= f.TC() - - while f: - _height, _monom, _coeff = -1, None, None - - for i, (monom, coeff) in enumerate(f.terms()): - if all(monom[i] >= monom[i + 1] for i in indices): - height = max([ n*m for n, m in zip(weights, monom) ]) - - if height > _height: - _height, _monom, _coeff = height, monom, coeff - - if _height != -1: - monom, coeff = _monom, _coeff - else: - break - - exponents = [] - - for m1, m2 in zip(monom, monom[1:] + (0,)): - exponents.append(m1 - m2) - - term = [ s**n for (s, _), n in zip(polys, exponents) ] - poly = [ p**n for (_, p), n in zip(polys, exponents) ] - - symmetric.append(Mul(coeff, *term)) - product = poly[0].mul(coeff) - - for p in poly[1:]: - product = product.mul(p) - - f -= product - - result.append((Add(*symmetric), f.as_expr())) - - polys = [ (s, p.as_expr()) for s, p in polys ] - - if not opt.formal: - for i, (sym, non_sym) in enumerate(result): - result[i] = (sym.subs(polys), non_sym) - - if not iterable: - result, = result - - if not opt.formal: - return result - else: - if iterable: - return result, polys - else: - return result + (polys,) - -
    -
    [docs]def horner(f, *gens, **args): - """ - Rewrite a polynomial in Horner form. - - Among other applications, evaluation of a polynomial at a point is optimal - when it is applied using the Horner scheme ([1]). - - Examples - ======== - - >>> from sympy.polys.polyfuncs import horner - >>> from sympy.abc import x, y, a, b, c, d, e - - >>> horner(9*x**4 + 8*x**3 + 7*x**2 + 6*x + 5) - x*(x*(x*(9*x + 8) + 7) + 6) + 5 - - >>> horner(a*x**4 + b*x**3 + c*x**2 + d*x + e) - e + x*(d + x*(c + x*(a*x + b))) - - >>> f = 4*x**2*y**2 + 2*x**2*y + 2*x*y**2 + x*y - - >>> horner(f, wrt=x) - x*(x*y*(4*y + 2) + y*(2*y + 1)) - - >>> horner(f, wrt=y) - y*(x*y*(4*x + 2) + x*(2*x + 1)) - - References - ========== - [1] - http://en.wikipedia.org/wiki/Horner_scheme - - """ - allowed_flags(args, []) - - try: - F, opt = poly_from_expr(f, *gens, **args) - except PolificationFailed as exc: - return exc.expr - - form, gen = S.Zero, F.gen - - if F.is_univariate: - for coeff in F.all_coeffs(): - form = form*gen + coeff - else: - F, gens = Poly(F, gen), gens[1:] - - for coeff in F.all_coeffs(): - form = form*gen + horner(coeff, *gens, **args) - - return form - -
    -
    [docs]def interpolate(data, x): - """ - Construct an interpolating polynomial for the data points. - - Examples - ======== - - >>> from sympy.polys.polyfuncs import interpolate - >>> from sympy.abc import x - - A list is interpreted as though it were paired with a range starting - from 1: - - >>> interpolate([1, 4, 9, 16], x) - x**2 - - This can be made explicit by giving a list of coordinates: - - >>> interpolate([(1, 1), (2, 4), (3, 9)], x) - x**2 - - The (x, y) coordinates can also be given as keys and values of a - dictionary (and the points need not be equispaced): - - >>> interpolate([(-1, 2), (1, 2), (2, 5)], x) - x**2 + 1 - >>> interpolate({-1: 2, 1: 2, 2: 5}, x) - x**2 + 1 - - """ - n = len(data) - - if isinstance(data, dict): - X, Y = list(zip(*list(data.items()))) - else: - if isinstance(data[0], tuple): - X, Y = list(zip(*data)) - else: - X = list(range(1, n + 1)) - Y = list(data) - - poly = interpolating_poly(n, x, X, Y) - - return poly.expand() - -
    -
    [docs]def viete(f, roots=None, *gens, **args): - """ - Generate Viete's formulas for ``f``. - - Examples - ======== - - >>> from sympy.polys.polyfuncs import viete - >>> from sympy import symbols - - >>> x, a, b, c, r1, r2 = symbols('x,a:c,r1:3') - - >>> viete(a*x**2 + b*x + c, [r1, r2], x) - [(r1 + r2, -b/a), (r1*r2, c/a)] - - """ - allowed_flags(args, []) - - if isinstance(roots, Basic): - gens, roots = (roots,) + gens, None - - try: - f, opt = poly_from_expr(f, *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('viete', 1, exc) - - if f.is_multivariate: - raise MultivariatePolynomialError( - "multivariate polynomials are not allowed") - - n = f.degree() - - if n < 1: - raise ValueError( - "can't derive Viete's formulas for a constant polynomial") - - if roots is None: - roots = numbered_symbols('r', start=1) - - roots = take(roots, n) - - if n != len(roots): - raise ValueError("required %s roots, got %s" % (n, len(roots))) - - lc, coeffs = f.LC(), f.all_coeffs() - result, sign = [], -1 - - for i, coeff in enumerate(coeffs[1:]): - poly = symmetric_poly(i + 1, roots) - coeff = sign*(coeff/lc) - result.append((poly, coeff)) - sign = -sign - - return result
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/polys/polyroots.html b/dev-py3k/_modules/sympy/polys/polyroots.html deleted file mode 100644 index c98ea1c09b4..00000000000 --- a/dev-py3k/_modules/sympy/polys/polyroots.html +++ /dev/null @@ -1,832 +0,0 @@ - - - - - - - - - - sympy.polys.polyroots — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.polys.polyroots

    -"""Algorithms for computing symbolic roots of polynomials. """
    -
    -from sympy.core.symbol import Dummy
    -from sympy.core import S, I
    -from sympy.core.sympify import sympify
    -from sympy.core.numbers import Rational, igcd
    -
    -from sympy.ntheory import divisors, isprime, nextprime
    -from sympy.functions import exp, sqrt
    -
    -from sympy.polys.polytools import Poly, cancel, factor, gcd_list
    -from sympy.polys.specialpolys import cyclotomic_poly
    -from sympy.polys.polyerrors import PolynomialError, GeneratorsNeeded, DomainError
    -
    -from sympy.simplify import simplify
    -from sympy.utilities import default_sort_key
    -
    -from sympy.core.compatibility import reduce
    -
    -import math
    -from functools import reduce
    -
    -
    -def roots_linear(f):
    -    """Returns a list of roots of a linear polynomial."""
    -    r = -f.nth(0)/f.nth(1)
    -    dom = f.get_domain()
    -
    -    if not dom.is_Numerical:
    -        if dom.is_Composite:
    -            r = factor(r)
    -        else:
    -            r = simplify(r)
    -
    -    return [r]
    -
    -
    -def roots_quadratic(f):
    -    """Returns a list of roots of a quadratic polynomial."""
    -    a, b, c = f.all_coeffs()
    -    dom = f.get_domain()
    -
    -    def _simplify(expr):
    -        if dom.is_Composite:
    -            return factor(expr)
    -        else:
    -            return simplify(expr)
    -
    -    if c is S.Zero:
    -        r0, r1 = S.Zero, -b/a
    -
    -        if not dom.is_Numerical:
    -            r1 = _simplify(r1)
    -    elif b is S.Zero:
    -        r = -c/a
    -
    -        if not dom.is_Numerical:
    -            R = sqrt(_simplify(r))
    -        else:
    -            R = sqrt(r)
    -
    -        r0 = R
    -        r1 = -R
    -    else:
    -        d = b**2 - 4*a*c
    -
    -        if dom.is_Numerical:
    -            D = sqrt(d)
    -
    -            r0 = (-b + D) / (2*a)
    -            r1 = (-b - D) / (2*a)
    -        else:
    -            D = sqrt(_simplify(d))
    -            A = 2*a
    -
    -            E = _simplify(-b/A)
    -            F = D/A
    -
    -            r0 = E + F
    -            r1 = E - F
    -
    -    return sorted([r0, r1], key=default_sort_key)
    -
    -
    -def roots_cubic(f):
    -    """Returns a list of roots of a cubic polynomial."""
    -    _, a, b, c = f.monic().all_coeffs()
    -
    -    if c is S.Zero:
    -        x1, x2 = roots([1, a, b], multiple=True)
    -        return [x1, S.Zero, x2]
    -
    -    p = b - a**2/3
    -    q = c - a*b/3 + 2*a**3/27
    -
    -    pon3 = p/3
    -    aon3 = a/3
    -
    -    if p is S.Zero:
    -        if q is S.Zero:
    -            return [-aon3]*3
    -        else:
    -            u1 = q**Rational(1, 3)
    -    elif q is S.Zero:
    -        y1, y2 = roots([1, 0, p], multiple=True)
    -        return [tmp - aon3 for tmp in [y1, S.Zero, y2]]
    -    else:
    -        u1 = (q/2 + sqrt(q**2/4 + pon3**3))**Rational(1, 3)
    -
    -    coeff = S.ImaginaryUnit*sqrt(3)/2
    -
    -    u2 = u1*(-S.Half + coeff)
    -    u3 = u1*(-S.Half - coeff)
    -
    -    soln = [
    -        -u1 + pon3/u1 - aon3,
    -        -u2 + pon3/u2 - aon3,
    -        -u3 + pon3/u3 - aon3
    -    ]
    -
    -    return soln
    -
    -
    -def roots_quartic(f):
    -    r"""
    -    Returns a list of roots of a quartic polynomial.
    -
    -    There are many references for solving quartic expressions available [1-5].
    -    This reviewer has found that many of them require one to select from among
    -    2 or more possible sets of solutions and that some solutions work when one
    -    is searching for real roots but don't work when searching for complex roots
    -    (though this is not always stated clearly). The following routine has been
    -    tested and found to be correct for 0, 2 or 4 complex roots.
    -
    -    The quasisymmetric case solution [6] looks for quartics that have the form
    -    `x**4 + A*x**3 + B*x**2 + C*x + D = 0` where `(C/A)**2 = D`.
    -
    -    Although there is a general solution, simpler results can be obtained for
    -    certain values of the coefficients. In all cases, 4 roots are returned:
    -
    -      1) `f = c + a*(a**2/8 - b/2) == 0`
    -      2) `g = d - a*(a*(3*a**2/256 - b/16) + c/4) = 0`
    -      3) if `f != 0` and `g != 0` and `p = -d + a*c/4 - b**2/12` then
    -        a) `p == 0`
    -        b) `p != 0`
    -
    -    Examples
    -    ========
    -
    -        >>> from sympy import Poly, symbols, I
    -        >>> from sympy.polys.polyroots import roots_quartic
    -
    -        >>> r = roots_quartic(Poly('x**4-6*x**3+17*x**2-26*x+20'))
    -
    -        >>> # 4 complex roots: 1+-I*sqrt(3), 2+-I
    -        >>> sorted(str(tmp.evalf(n=2)) for tmp in r)
    -        ['1.0 + 1.7*I', '1.0 - 1.7*I', '2.0 + 1.0*I', '2.0 - 1.0*I']
    -
    -    References
    -    ==========
    -
    -    1. http://mathforum.org/dr.math/faq/faq.cubic.equations.html
    -    2. http://en.wikipedia.org/wiki/Quartic_function#Summary_of_Ferrari.27s_method
    -    3. http://planetmath.org/encyclopedia/GaloisTheoreticDerivationOfTheQuarticFormula.html
    -    4. http://staff.bath.ac.uk/masjhd/JHD-CA.pdf
    -    5. http://www.albmath.org/files/Math_5713.pdf
    -    6. http://www.statemaster.com/encyclopedia/Quartic-equation
    -
    -    """
    -    _, a, b, c, d = f.monic().all_coeffs()
    -
    -    if not d:
    -        return [S.Zero] + roots([1, a, b, c], multiple=True)
    -    elif (c/a)**2 == d:
    -        x, m = f.gen, c/a
    -
    -        g = Poly(x**2 + a*x + b - 2*m, x)
    -
    -        z1, z2 = roots_quadratic(g)
    -
    -        h1 = Poly(x**2 - z1*x + m, x)
    -        h2 = Poly(x**2 - z2*x + m, x)
    -
    -        r1 = roots_quadratic(h1)
    -        r2 = roots_quadratic(h2)
    -
    -        return r1 + r2
    -    else:
    -        a2 = a**2
    -        e = b - 3*a2/8
    -        f = c + a*(a2/8 - b/2)
    -        g = d - a*(a*(3*a2/256 - b/16) + c/4)
    -        aon4 = a/4
    -        ans = []
    -
    -        if f is S.Zero:
    -            y1, y2 = [sqrt(tmp) for tmp in
    -                      roots([1, e, g], multiple=True)]
    -            return [tmp - aon4 for tmp in [-y1, -y2, y1, y2]]
    -        if g is S.Zero:
    -            y = [S.Zero] + roots([1, 0, e, f], multiple=True)
    -            return [tmp - aon4 for tmp in y]
    -        else:
    -            p = -e**2/12 - g
    -            q = -e**3/108 + e*g/3 - f**2/8
    -            TH = Rational(1, 3)
    -            if p is S.Zero:
    -                y = -5*e/6 - q**TH
    -            else:
    -                # with p !=0 then u below is not 0
    -                root = sqrt(q**2/4 + p**3/27)
    -                r = -q/2 + root  # or -q/2 - root
    -                u = r**TH  # primary root of solve(x**3-r, x)
    -                y = -5*e/6 + u - p/u/3
    -            w = sqrt(e + 2*y)
    -            arg1 = 3*e + 2*y
    -            arg2 = 2*f/w
    -            for s in [-1, 1]:
    -                root = sqrt(-(arg1 + s*arg2))
    -                for t in [-1, 1]:
    -                    ans.append((s*w - t*root)/2 - aon4)
    -
    -    return ans
    -
    -
    -def roots_binomial(f):
    -    """Returns a list of roots of a binomial polynomial."""
    -    n = f.degree()
    -
    -    a, b = f.nth(n), f.nth(0)
    -    alpha = (-cancel(b/a))**Rational(1, n)
    -
    -    if alpha.is_number:
    -        alpha = alpha.expand(complex=True)
    -
    -    roots, I = [], S.ImaginaryUnit
    -
    -    for k in range(n):
    -        zeta = exp(2*k*S.Pi*I/n).expand(complex=True)
    -        roots.append((alpha*zeta).expand(power_base=False))
    -
    -    return sorted(roots, key=default_sort_key)
    -
    -
    -def _inv_totient_estimate(m):
    -    """
    -    Find ``(L, U)`` such that ``L <= phi^-1(m) <= U``.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.polys.polyroots import _inv_totient_estimate
    -
    -    >>> _inv_totient_estimate(192)
    -    (192, 840)
    -    >>> _inv_totient_estimate(400)
    -    (400, 1750)
    -
    -    """
    -    primes = [ d + 1 for d in divisors(m) if isprime(d + 1) ]
    -
    -    a, b = 1, 1
    -
    -    for p in primes:
    -        a *= p
    -        b *= p - 1
    -
    -    L = m
    -    U = int(math.ceil(m*(float(a)/b)))
    -
    -    P = p = 2
    -    primes = []
    -
    -    while P <= U:
    -        p = nextprime(p)
    -        primes.append(p)
    -        P *= p
    -
    -    P //= p
    -    b = 1
    -
    -    for p in primes[:-1]:
    -        b *= p - 1
    -
    -    U = int(math.ceil(m*(float(P)/b)))
    -
    -    return L, U
    -
    -
    -def roots_cyclotomic(f, factor=False):
    -    """Compute roots of cyclotomic polynomials. """
    -    L, U = _inv_totient_estimate(f.degree())
    -
    -    for n in range(L, U + 1):
    -        g = cyclotomic_poly(n, f.gen, polys=True)
    -
    -        if f == g:
    -            break
    -    else:  # pragma: no cover
    -        raise RuntimeError("failed to find index of a cyclotomic polynomial")
    -
    -    roots = []
    -
    -    if not factor:
    -        for k in range(1, n + 1):
    -            if igcd(k, n) == 1:
    -                roots.append(exp(2*k*S.Pi*I/n).expand(complex=True))
    -    else:
    -        g = Poly(f, extension=(-1)**Rational(1, n))
    -
    -        for h, _ in g.factor_list()[1]:
    -            roots.append(-h.TC())
    -
    -    return sorted(roots, key=default_sort_key)
    -
    -
    -def roots_rational(f):
    -    """Returns a list of rational roots of a polynomial."""
    -    domain = f.get_domain()
    -
    -    if domain.is_QQ:
    -        _, f = f.clear_denoms()
    -    elif domain.is_ZZ:
    -        f = f.set_domain('QQ')
    -    else:
    -        return []
    -
    -    LC_divs = divisors(int(f.LC()))
    -    EC_divs = divisors(int(f.EC()))
    -
    -    if not f.eval(S.Zero):
    -        zeros = [S.Zero]
    -    else:
    -        zeros = []
    -
    -    for p in LC_divs:
    -        for q in EC_divs:
    -            zero = Rational(p, q)
    -
    -            if not f.eval(zero):
    -                zeros.append(zero)
    -
    -            if not f.eval(-zero):
    -                zeros.append(-zero)
    -
    -    return sorted(zeros, key=default_sort_key)
    -
    -
    -def _integer_basis(poly):
    -    """Compute coefficient basis for a polynomial over integers. """
    -    monoms, coeffs = list(zip(*poly.terms()))
    -
    -    monoms, = list(zip(*monoms))
    -    coeffs = list(map(abs, coeffs))
    -
    -    if coeffs[0] < coeffs[-1]:
    -        coeffs = list(reversed(coeffs))
    -    else:
    -        return None
    -
    -    monoms = monoms[:-1]
    -    coeffs = coeffs[:-1]
    -
    -    divs = reversed(divisors(gcd_list(coeffs))[1:])
    -
    -    try:
    -        div = next(divs)
    -    except StopIteration:
    -        return None
    -
    -    while True:
    -        for monom, coeff in zip(monoms, coeffs):
    -            if coeff % div**monom != 0:
    -                try:
    -                    div = next(divs)
    -                except StopIteration:
    -                    return None
    -                else:
    -                    break
    -        else:
    -            return div
    -
    -
    -def preprocess_roots(poly):
    -    """Try to get rid of symbolic coefficients from ``poly``. """
    -    coeff = S.One
    -
    -    try:
    -        _, poly = poly.clear_denoms(convert=True)
    -    except DomainError:
    -        return coeff, poly
    -
    -    poly = poly.primitive()[1]
    -    poly = poly.retract()
    -
    -    if poly.get_domain().is_Poly and all(c.is_monomial for c in poly.rep.coeffs()):
    -        poly = poly.inject()
    -
    -        strips = list(zip(*poly.monoms()))
    -        gens = list(poly.gens[1:])
    -
    -        base, strips = strips[0], strips[1:]
    -
    -        for gen, strip in zip(list(gens), strips):
    -            reverse = False
    -
    -            if strip[0] < strip[-1]:
    -                strip = reversed(strip)
    -                reverse = True
    -
    -            ratio = None
    -
    -            for a, b in zip(base, strip):
    -                if not a and not b:
    -                    continue
    -                elif not a or not b:
    -                    break
    -                elif b % a != 0:
    -                    break
    -                else:
    -                    _ratio = b // a
    -
    -                    if ratio is None:
    -                        ratio = _ratio
    -                    elif ratio != _ratio:
    -                        break
    -            else:
    -                if reverse:
    -                    ratio = -ratio
    -
    -                poly = poly.eval(gen, 1)
    -                coeff *= gen**(-ratio)
    -                gens.remove(gen)
    -
    -        if gens:
    -            poly = poly.eject(*gens)
    -
    -    if poly.is_univariate and poly.get_domain().is_ZZ:
    -        basis = _integer_basis(poly)
    -
    -        if basis is not None:
    -            n = poly.degree()
    -
    -            def func(k, coeff):
    -                return coeff//basis**(n - k[0])
    -
    -            poly = poly.termwise(func)
    -            coeff *= basis
    -
    -    return coeff, poly
    -
    -
    -
    [docs]def roots(f, *gens, **flags): - """ - Computes symbolic roots of a univariate polynomial. - - Given a univariate polynomial f with symbolic coefficients (or - a list of the polynomial's coefficients), returns a dictionary - with its roots and their multiplicities. - - Only roots expressible via radicals will be returned. To get - a complete set of roots use RootOf class or numerical methods - instead. By default cubic and quartic formulas are used in - the algorithm. To disable them because of unreadable output - set ``cubics=False`` or ``quartics=False`` respectively. - - To get roots from a specific domain set the ``filter`` flag with - one of the following specifiers: Z, Q, R, I, C. By default all - roots are returned (this is equivalent to setting ``filter='C'``). - - By default a dictionary is returned giving a compact result in - case of multiple roots. However to get a tuple containing all - those roots set the ``multiple`` flag to True. - - Examples - ======== - - >>> from sympy import Poly, roots - >>> from sympy.abc import x, y - - >>> roots(x**2 - 1, x) - {-1: 1, 1: 1} - - >>> p = Poly(x**2-1, x) - >>> roots(p) - {-1: 1, 1: 1} - - >>> p = Poly(x**2-y, x, y) - - >>> roots(Poly(p, x)) - {-sqrt(y): 1, sqrt(y): 1} - - >>> roots(x**2 - y, x) - {-sqrt(y): 1, sqrt(y): 1} - - >>> roots([1, 0, -1]) - {-1: 1, 1: 1} - - """ - flags = dict(flags) - - auto = flags.pop('auto', True) - cubics = flags.pop('cubics', True) - quartics = flags.pop('quartics', True) - multiple = flags.pop('multiple', False) - filter = flags.pop('filter', None) - predicate = flags.pop('predicate', None) - - if isinstance(f, list): - if gens: - raise ValueError('redundant generators given') - - x = Dummy('x') - - poly, i = {}, len(f) - 1 - - for coeff in f: - poly[i], i = sympify(coeff), i - 1 - - f = Poly(poly, x, field=True) - else: - try: - f = Poly(f, *gens, **flags) - except GeneratorsNeeded: - if multiple: - return [] - else: - return {} - - if f.is_multivariate: - raise PolynomialError('multivariate polynomials are not supported') - - def _update_dict(result, root, k): - if root in result: - result[root] += k - else: - result[root] = k - - def _try_decompose(f): - """Find roots using functional decomposition. """ - factors, roots = f.decompose(), [] - - for root in _try_heuristics(factors[0]): - roots.append(root) - - for factor in factors[1:]: - previous, roots = list(roots), [] - - for root in previous: - g = factor - Poly(root, f.gen) - - for root in _try_heuristics(g): - roots.append(root) - - return roots - - def _try_heuristics(f): - """Find roots using formulas and some tricks. """ - if f.is_ground: - return [] - if f.is_monomial: - return [S(0)]*f.degree() - - if f.length() == 2: - if f.degree() == 1: - return list(map(cancel, roots_linear(f))) - else: - return roots_binomial(f) - - result = [] - - for i in [-1, 1]: - if not f.eval(i): - f = f.quo(Poly(f.gen - i, f.gen)) - result.append(i) - break - - n = f.degree() - - if n == 1: - result += list(map(cancel, roots_linear(f))) - elif n == 2: - result += list(map(cancel, roots_quadratic(f))) - elif f.is_cyclotomic: - result += roots_cyclotomic(f) - elif n == 3 and cubics: - result += roots_cubic(f) - elif n == 4 and quartics: - result += roots_quartic(f) - - return result - - (k,), f = f.terms_gcd() - - if not k: - zeros = {} - else: - zeros = {S(0): k} - - coeff, f = preprocess_roots(f) - - if auto and f.get_domain().has_Ring: - f = f.to_field() - - result = {} - - if not f.is_ground: - if not f.get_domain().is_Exact: - for r in f.nroots(): - _update_dict(result, r, 1) - elif f.degree() == 1: - result[roots_linear(f)[0]] = 1 - elif f.degree() == 2: - for r in roots_quadratic(f): - _update_dict(result, r, 1) - elif f.length() == 2: - for r in roots_binomial(f): - _update_dict(result, r, 1) - else: - _, factors = Poly(f.as_expr()).factor_list() - - if len(factors) == 1 and factors[0][1] == 1: - for root in _try_decompose(f): - _update_dict(result, root, 1) - else: - for factor, k in factors: - for r in _try_heuristics(Poly(factor, f.gen, field=True)): - _update_dict(result, r, k) - - if coeff is not S.One: - _result, result, = result, {} - - for root, k in _result.items(): - result[coeff*root] = k - - result.update(zeros) - - if filter not in [None, 'C']: - handlers = { - 'Z': lambda r: r.is_Integer, - 'Q': lambda r: r.is_Rational, - 'R': lambda r: r.is_real, - 'I': lambda r: r.is_imaginary, - } - - try: - query = handlers[filter] - except KeyError: - raise ValueError("Invalid filter: %s" % filter) - - for zero in dict(result).keys(): - if not query(zero): - del result[zero] - - if predicate is not None: - for zero in dict(result).keys(): - if not predicate(zero): - del result[zero] - - if not multiple: - return result - else: - zeros = [] - - for zero, k in result.items(): - zeros.extend([zero]*k) - - return sorted(zeros, key=default_sort_key) - -
    -def root_factors(f, *gens, **args): - """ - Returns all factors of a univariate polynomial. - - Examples - ======== - - >>> from sympy.abc import x, y - >>> from sympy.polys.polyroots import root_factors - - >>> root_factors(x**2 - y, x) - [x - sqrt(y), x + sqrt(y)] - - """ - args = dict(args) - filter = args.pop('filter', None) - - F = Poly(f, *gens, **args) - - if not F.is_Poly: - return [f] - - if F.is_multivariate: - raise ValueError('multivariate polynomials not supported') - - x = F.gens[0] - - zeros = roots(F, filter=filter) - - if not zeros: - factors = [F] - else: - factors, N = [], 0 - - for r, n in zeros.items(): - factors, N = factors + [Poly(x - r, x)]*n, N + n - - if N < F.degree(): - G = reduce(lambda p, q: p*q, factors) - factors.append(F.quo(G)) - - if not isinstance(f, Poly): - factors = [ f.as_expr() for f in factors ] - - return sorted(factors, key=default_sort_key) -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/polys/polytools.html b/dev-py3k/_modules/sympy/polys/polytools.html deleted file mode 100644 index 53e258d894c..00000000000 --- a/dev-py3k/_modules/sympy/polys/polytools.html +++ /dev/null @@ -1,6296 +0,0 @@ - - - - - - - - - - sympy.polys.polytools — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.polys.polytools

    -"""User-friendly public interface to polynomial functions. """
    -
    -from sympy.core import (
    -    S, Basic, Expr, I, Integer, Add, Mul, Dummy, Tuple, Rational
    -)
    -
    -from sympy.core.mul import _keep_coeff
    -
    -from sympy.core.sympify import (
    -    sympify, SympifyError,
    -)
    -
    -from sympy.core.decorators import (
    -    _sympifyit,
    -)
    -
    -from sympy.polys.polyclasses import DMP
    -
    -from sympy.polys.polyutils import (
    -    basic_from_dict,
    -    _sort_gens,
    -    _unify_gens,
    -    _dict_reorder,
    -    _dict_from_expr,
    -    _parallel_dict_from_expr,
    -)
    -
    -from sympy.polys.rationaltools import (
    -    together,
    -)
    -
    -from sympy.polys.rootisolation import (
    -    dup_isolate_real_roots_list,
    -)
    -
    -from sympy.polys.distributedpolys import (
    -    sdp_from_dict, sdp_div,
    -)
    -
    -from sympy.polys.groebnertools import (
    -    sdp_groebner, matrix_fglm,
    -)
    -
    -from sympy.polys.monomialtools import (
    -    Monomial, monomial_key,
    -)
    -
    -from sympy.polys.polyerrors import (
    -    OperationNotSupported, DomainError,
    -    CoercionFailed, UnificationFailed,
    -    GeneratorsNeeded, PolynomialError,
    -    MultivariatePolynomialError,
    -    ExactQuotientFailed,
    -    PolificationFailed,
    -    ComputationFailed,
    -    GeneratorsError,
    -)
    -
    -from sympy.utilities import group
    -
    -import sympy.polys
    -import sympy.mpmath
    -
    -from sympy.polys.domains import FF, QQ
    -from sympy.polys.constructor import construct_domain
    -
    -from sympy.polys import polyoptions as options
    -
    -from sympy.core.compatibility import iterable
    -
    -
    -
    [docs]class Poly(Expr): - """Generic class for representing polynomial expressions. """ - - __slots__ = ['rep', 'gens'] - - is_commutative = True - is_Poly = True - - def __new__(cls, rep, *gens, **args): - """Create a new polynomial instance out of something useful. """ - opt = options.build_options(gens, args) - - if 'order' in opt: - raise NotImplementedError("'order' keyword is not implemented yet") - - if iterable(rep, exclude=str): - if isinstance(rep, dict): - return cls._from_dict(rep, opt) - else: - return cls._from_list(list(rep), opt) - else: - rep = sympify(rep) - - if rep.is_Poly: - return cls._from_poly(rep, opt) - else: - return cls._from_expr(rep, opt) - - @classmethod -
    [docs] def new(cls, rep, *gens): - """Construct :class:`Poly` instance from raw representation. """ - if not isinstance(rep, DMP): - raise PolynomialError( - "invalid polynomial representation: %s" % rep) - elif rep.lev != len(gens) - 1: - raise PolynomialError("invalid arguments: %s, %s" % (rep, gens)) - - obj = Basic.__new__(cls) - - obj.rep = rep - obj.gens = gens - - return obj -
    - @classmethod -
    [docs] def from_dict(cls, rep, *gens, **args): - """Construct a polynomial from a ``dict``. """ - opt = options.build_options(gens, args) - return cls._from_dict(rep, opt) -
    - @classmethod -
    [docs] def from_list(cls, rep, *gens, **args): - """Construct a polynomial from a ``list``. """ - opt = options.build_options(gens, args) - return cls._from_list(rep, opt) -
    - @classmethod -
    [docs] def from_poly(cls, rep, *gens, **args): - """Construct a polynomial from a polynomial. """ - opt = options.build_options(gens, args) - return cls._from_poly(rep, opt) -
    - @classmethod -
    [docs] def from_expr(cls, rep, *gens, **args): - """Construct a polynomial from an expression. """ - opt = options.build_options(gens, args) - return cls._from_expr(rep, opt) -
    - @classmethod - def _from_dict(cls, rep, opt): - """Construct a polynomial from a ``dict``. """ - gens = opt.gens - - if not gens: - raise GeneratorsNeeded( - "can't initialize from 'dict' without generators") - - level = len(gens) - 1 - domain = opt.domain - - if domain is None: - domain, rep = construct_domain(rep, opt=opt) - else: - for monom, coeff in rep.items(): - rep[monom] = domain.convert(coeff) - - return cls.new(DMP.from_dict(rep, level, domain), *gens) - - @classmethod - def _from_list(cls, rep, opt): - """Construct a polynomial from a ``list``. """ - gens = opt.gens - - if not gens: - raise GeneratorsNeeded( - "can't initialize from 'list' without generators") - elif len(gens) != 1: - raise MultivariatePolynomialError( - "'list' representation not supported") - - level = len(gens) - 1 - domain = opt.domain - - if domain is None: - domain, rep = construct_domain(rep, opt=opt) - else: - rep = list(map(domain.convert, rep)) - - return cls.new(DMP.from_list(rep, level, domain), *gens) - - @classmethod - def _from_poly(cls, rep, opt): - """Construct a polynomial from a polynomial. """ - if cls != rep.__class__: - rep = cls.new(rep.rep, *rep.gens) - - gens = opt.gens - field = opt.field - domain = opt.domain - - if gens and rep.gens != gens: - if set(rep.gens) != set(gens): - return cls._from_expr(rep.as_expr(), opt) - else: - rep = rep.reorder(*gens) - - if 'domain' in opt and domain: - rep = rep.set_domain(domain) - elif field is True: - rep = rep.to_field() - - return rep - - @classmethod - def _from_expr(cls, rep, opt): - """Construct a polynomial from an expression. """ - rep, opt = _dict_from_expr(rep, opt) - return cls._from_dict(rep, opt) - - def _hashable_content(self): - """Allow SymPy to hash Poly instances. """ - return (self.rep, self.gens) - - def __hash__(self): - return super(Poly, self).__hash__() - - @property -
    [docs] def free_symbols(self): - """ - Free symbols of a polynomial expression. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> Poly(x**2 + 1).free_symbols - set([x]) - >>> Poly(x**2 + y).free_symbols - set([x, y]) - >>> Poly(x**2 + y, x).free_symbols - set([x, y]) - - """ - symbols = set([]) - - for gen in self.gens: - symbols |= gen.free_symbols - - return symbols | self.free_symbols_in_domain -
    - @property -
    [docs] def free_symbols_in_domain(self): - """ - Free symbols of the domain of ``self``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> Poly(x**2 + 1).free_symbols_in_domain - set() - >>> Poly(x**2 + y).free_symbols_in_domain - set() - >>> Poly(x**2 + y, x).free_symbols_in_domain - set([y]) - - """ - domain, symbols = self.rep.dom, set() - - if domain.is_Composite: - for gen in domain.gens: - symbols |= gen.free_symbols - elif domain.is_EX: - for coeff in self.coeffs(): - symbols |= coeff.free_symbols - - return symbols -
    - @property -
    [docs] def args(self): - """ - Don't mess up with the core. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 + 1, x).args - (x**2 + 1,) - - """ - return (self.as_expr(),) -
    - @property -
    [docs] def gen(self): - """ - Return the principal generator. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 + 1, x).gen - x - - """ - return self.gens[0] -
    - @property -
    [docs] def domain(self): - """Get the ground domain of ``self``. """ - return self.get_domain() -
    - @property -
    [docs] def zero(self): - """Return zero polynomial with ``self``'s properties. """ - return self.new(self.rep.zero(self.rep.lev, self.rep.dom), *self.gens) -
    - @property -
    [docs] def one(self): - """Return one polynomial with ``self``'s properties. """ - return self.new(self.rep.one(self.rep.lev, self.rep.dom), *self.gens) -
    - @property -
    [docs] def unit(self): - """Return unit polynomial with ``self``'s properties. """ - return self.new(self.rep.unit(self.rep.lev, self.rep.dom), *self.gens) -
    -
    [docs] def unify(f, g): - """ - Make ``f`` and ``g`` belong to the same domain. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> f, g = Poly(x/2 + 1), Poly(2*x + 1) - - >>> f - Poly(1/2*x + 1, x, domain='QQ') - >>> g - Poly(2*x + 1, x, domain='ZZ') - - >>> F, G = f.unify(g) - - >>> F - Poly(1/2*x + 1, x, domain='QQ') - >>> G - Poly(2*x + 1, x, domain='QQ') - - """ - _, per, F, G = f._unify(g) - return per(F), per(G) -
    - def _unify(f, g): - g = sympify(g) - - if not g.is_Poly: - try: - return f.rep.dom, f.per, f.rep, f.rep.per(f.rep.dom.from_sympy(g)) - except CoercionFailed: - raise UnificationFailed("can't unify %s with %s" % (f, g)) - - if isinstance(f.rep, DMP) and isinstance(g.rep, DMP): - gens = _unify_gens(f.gens, g.gens) - - dom, lev = f.rep.dom.unify(g.rep.dom, gens), len(gens) - 1 - - if f.gens != gens: - f_monoms, f_coeffs = _dict_reorder( - f.rep.to_dict(), f.gens, gens) - - if f.rep.dom != dom: - f_coeffs = [ dom.convert(c, f.rep.dom) for c in f_coeffs ] - - F = DMP(dict(list(zip(f_monoms, f_coeffs))), dom, lev) - else: - F = f.rep.convert(dom) - - if g.gens != gens: - g_monoms, g_coeffs = _dict_reorder( - g.rep.to_dict(), g.gens, gens) - - if g.rep.dom != dom: - g_coeffs = [ dom.convert(c, g.rep.dom) for c in g_coeffs ] - - G = DMP(dict(list(zip(g_monoms, g_coeffs))), dom, lev) - else: - G = g.rep.convert(dom) - else: - raise UnificationFailed("can't unify %s with %s" % (f, g)) - - cls = f.__class__ - - def per(rep, dom=dom, gens=gens, remove=None): - if remove is not None: - gens = gens[:remove] + gens[remove + 1:] - - if not gens: - return dom.to_sympy(rep) - - return cls.new(rep, *gens) - - return dom, per, F, G - -
    [docs] def per(f, rep, gens=None, remove=None): - """ - Create a Poly out of the given representation. - - Examples - ======== - - >>> from sympy import Poly, ZZ - >>> from sympy.abc import x, y - - >>> from sympy.polys.polyclasses import DMP - - >>> a = Poly(x**2 + 1) - - >>> a.per(DMP([ZZ(1), ZZ(1)], ZZ), gens=[y]) - Poly(y + 1, y, domain='ZZ') - - """ - if gens is None: - gens = f.gens - - if remove is not None: - gens = gens[:remove] + gens[remove + 1:] - - if not gens: - return f.rep.dom.to_sympy(rep) - - return f.__class__.new(rep, *gens) -
    -
    [docs] def set_domain(f, domain): - """Set the ground domain of ``f``. """ - opt = options.build_options(f.gens, {'domain': domain}) - return f.per(f.rep.convert(opt.domain)) -
    -
    [docs] def get_domain(f): - """Get the ground domain of ``f``. """ - return f.rep.dom -
    -
    [docs] def set_modulus(f, modulus): - """ - Set the modulus of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(5*x**2 + 2*x - 1, x).set_modulus(2) - Poly(x**2 + 1, x, modulus=2) - - """ - modulus = options.Modulus.preprocess(modulus) - return f.set_domain(FF(modulus)) -
    -
    [docs] def get_modulus(f): - """ - Get the modulus of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 + 1, modulus=2).get_modulus() - 2 - - """ - domain = f.get_domain() - - if not domain.has_CharacteristicZero: - return Integer(domain.characteristic()) - else: - raise PolynomialError("not a polynomial over a Galois field") -
    - def _eval_subs(f, old, new): - """Internal implementation of :func:`subs`. """ - if old in f.gens: - if new.is_number: - return f.eval(old, new) - else: - try: - return f.replace(old, new) - except PolynomialError: - pass - - return f.as_expr().subs(old, new) - -
    [docs] def exclude(f): - """ - Remove unnecessary generators from ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import a, b, c, d, x - - >>> Poly(a + x, a, b, c, d, x).exclude() - Poly(a + x, a, x, domain='ZZ') - - """ - J, new = f.rep.exclude() - gens = [] - - for j in range(len(f.gens)): - if j not in J: - gens.append(f.gens[j]) - - return f.per(new, gens=gens) -
    -
    [docs] def replace(f, x, y=None): - """ - Replace ``x`` with ``y`` in generators list. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> Poly(x**2 + 1, x).replace(x, y) - Poly(y**2 + 1, y, domain='ZZ') - - """ - if y is None: - if f.is_univariate: - x, y = f.gen, x - else: - raise PolynomialError( - "syntax supported only in univariate case") - - if x == y: - return f - - if x in f.gens and y not in f.gens: - dom = f.get_domain() - - if not dom.is_Composite or y not in dom.gens: - gens = list(f.gens) - gens[gens.index(x)] = y - return f.per(f.rep, gens=gens) - - raise PolynomialError("can't replace %s with %s in %s" % (x, y, f)) -
    -
    [docs] def reorder(f, *gens, **args): - """ - Efficiently apply new order of generators. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> Poly(x**2 + x*y**2, x, y).reorder(y, x) - Poly(y**2*x + x**2, y, x, domain='ZZ') - - """ - opt = options.Options((), args) - - if not gens: - gens = _sort_gens(f.gens, opt=opt) - elif set(f.gens) != set(gens): - raise PolynomialError( - "generators list can differ only up to order of elements") - - rep = dict(list(zip(*_dict_reorder(f.rep.to_dict(), f.gens, gens)))) - - return f.per(DMP(rep, f.rep.dom, len(gens) - 1), gens=gens) -
    -
    [docs] def ltrim(f, gen): - """ - Remove dummy generators from the "left" of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y, z - - >>> Poly(y**2 + y*z**2, x, y, z).ltrim(y) - Poly(y**2 + y*z**2, y, z, domain='ZZ') - - """ - rep = f.as_dict(native=True) - j = f._gen_to_level(gen) - terms = {} - - for monom, coeff in rep.items(): - monom = monom[j:] - - if monom not in terms: - terms[monom] = coeff - else: - raise PolynomialError("can't left trim %s" % f) - - gens = f.gens[j:] - - return f.new(DMP.from_dict(terms, len(gens) - 1, f.rep.dom), *gens) -
    -
    [docs] def has_only_gens(f, *gens): - """ - Return ``True`` if ``Poly(f, *gens)`` retains ground domain. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y, z - - >>> Poly(x*y + 1, x, y, z).has_only_gens(x, y) - True - >>> Poly(x*y + z, x, y, z).has_only_gens(x, y) - False - - """ - f_gens = list(f.gens) - indices = set([]) - - for gen in gens: - try: - index = f_gens.index(gen) - except ValueError: - raise GeneratorsError( - "%s doesn't have %s as generator" % (f, gen)) - else: - indices.add(index) - - for monom in f.monoms(): - for i, elt in enumerate(monom): - if i not in indices and elt: - return False - - return True -
    -
    [docs] def to_ring(f): - """ - Make the ground domain a ring. - - Examples - ======== - - >>> from sympy import Poly, QQ - >>> from sympy.abc import x - - >>> Poly(x**2 + 1, domain=QQ).to_ring() - Poly(x**2 + 1, x, domain='ZZ') - - """ - if hasattr(f.rep, 'to_ring'): - result = f.rep.to_ring() - else: # pragma: no cover - raise OperationNotSupported(f, 'to_ring') - - return f.per(result) -
    -
    [docs] def to_field(f): - """ - Make the ground domain a field. - - Examples - ======== - - >>> from sympy import Poly, ZZ - >>> from sympy.abc import x - - >>> Poly(x**2 + 1, x, domain=ZZ).to_field() - Poly(x**2 + 1, x, domain='QQ') - - """ - if hasattr(f.rep, 'to_field'): - result = f.rep.to_field() - else: # pragma: no cover - raise OperationNotSupported(f, 'to_field') - - return f.per(result) -
    -
    [docs] def to_exact(f): - """ - Make the ground domain exact. - - Examples - ======== - - >>> from sympy import Poly, RR - >>> from sympy.abc import x - - >>> Poly(x**2 + 1.0, x, domain=RR).to_exact() - Poly(x**2 + 1, x, domain='QQ') - - """ - if hasattr(f.rep, 'to_exact'): - result = f.rep.to_exact() - else: # pragma: no cover - raise OperationNotSupported(f, 'to_exact') - - return f.per(result) -
    -
    [docs] def retract(f, field=None): - """ - Recalculate the ground domain of a polynomial. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> f = Poly(x**2 + 1, x, domain='QQ[y]') - >>> f - Poly(x**2 + 1, x, domain='QQ[y]') - - >>> f.retract() - Poly(x**2 + 1, x, domain='ZZ') - >>> f.retract(field=True) - Poly(x**2 + 1, x, domain='QQ') - - """ - dom, rep = construct_domain(f.as_dict(zero=True), field=field) - return f.from_dict(rep, f.gens, domain=dom) -
    -
    [docs] def slice(f, x, m, n=None): - """Take a continuous subsequence of terms of ``f``. """ - if n is None: - j, m, n = 0, x, m - else: - j = f._gen_to_level(x) - - m, n = int(m), int(n) - - if hasattr(f.rep, 'slice'): - result = f.rep.slice(m, n, j) - else: # pragma: no cover - raise OperationNotSupported(f, 'slice') - - return f.per(result) -
    -
    [docs] def coeffs(f, order=None): - """ - Returns all non-zero coefficients from ``f`` in lex order. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**3 + 2*x + 3, x).coeffs() - [1, 2, 3] - - """ - return [ f.rep.dom.to_sympy(c) for c in f.rep.coeffs(order=order) ] -
    -
    [docs] def monoms(f, order=None): - """ - Returns all non-zero monomials from ``f`` in lex order. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> Poly(x**2 + 2*x*y**2 + x*y + 3*y, x, y).monoms() - [(2, 0), (1, 2), (1, 1), (0, 1)] - - """ - return f.rep.monoms(order=order) -
    -
    [docs] def terms(f, order=None): - """ - Returns all non-zero terms from ``f`` in lex order. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> Poly(x**2 + 2*x*y**2 + x*y + 3*y, x, y).terms() - [((2, 0), 1), ((1, 2), 2), ((1, 1), 1), ((0, 1), 3)] - - """ - return [ (m, f.rep.dom.to_sympy(c)) for m, c in f.rep.terms(order=order) ] -
    -
    [docs] def all_coeffs(f): - """ - Returns all coefficients from a univariate polynomial ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**3 + 2*x - 1, x).all_coeffs() - [1, 0, 2, -1] - - """ - return [ f.rep.dom.to_sympy(c) for c in f.rep.all_coeffs() ] -
    -
    [docs] def all_monoms(f): - """ - Returns all monomials from a univariate polynomial ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**3 + 2*x - 1, x).all_monoms() - [(3,), (2,), (1,), (0,)] - - """ - return f.rep.all_monoms() -
    -
    [docs] def all_terms(f): - """ - Returns all terms from a univariate polynomial ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**3 + 2*x - 1, x).all_terms() - [((3,), 1), ((2,), 0), ((1,), 2), ((0,), -1)] - - """ - return [ (m, f.rep.dom.to_sympy(c)) for m, c in f.rep.all_terms() ] -
    -
    [docs] def termwise(f, func, *gens, **args): - """ - Apply a function to all terms of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> def func(xxx_todo_changeme, coeff): - ... (k,) = xxx_todo_changeme - ... return coeff//10**(2-k) - - >>> Poly(x**2 + 20*x + 400).termwise(func) - Poly(x**2 + 2*x + 4, x, domain='ZZ') - - """ - terms = {} - - for monom, coeff in f.terms(): - result = func(monom, coeff) - - if isinstance(result, tuple): - monom, coeff = result - else: - coeff = result - - if coeff: - if monom not in terms: - terms[monom] = coeff - else: - raise PolynomialError( - "%s monomial was generated twice" % monom) - - return f.from_dict(terms, *(gens or f.gens), **args) -
    -
    [docs] def length(f): - """ - Returns the number of non-zero terms in ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 + 2*x - 1).length() - 3 - - """ - return len(f.as_dict()) -
    -
    [docs] def as_dict(f, native=False, zero=False): - """ - Switch to a ``dict`` representation. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> Poly(x**2 + 2*x*y**2 - y, x, y).as_dict() - {(0, 1): -1, (1, 2): 2, (2, 0): 1} - - """ - if native: - return f.rep.to_dict(zero=zero) - else: - return f.rep.to_sympy_dict(zero=zero) -
    -
    [docs] def as_list(f, native=False): - """Switch to a ``list`` representation. """ - if native: - return f.rep.to_list() - else: - return f.rep.to_sympy_list() -
    -
    [docs] def as_expr(f, *gens): - """ - Convert a polynomial an expression. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> f = Poly(x**2 + 2*x*y**2 - y, x, y) - - >>> f.as_expr() - x**2 + 2*x*y**2 - y - >>> f.as_expr({x: 5}) - 10*y**2 - y + 25 - >>> f.as_expr(5, 6) - 379 - - """ - if not gens: - gens = f.gens - elif len(gens) == 1 and isinstance(gens[0], dict): - mapping = gens[0] - gens = list(f.gens) - - for gen, value in mapping.items(): - try: - index = gens.index(gen) - except ValueError: - raise GeneratorsError( - "%s doesn't have %s as generator" % (f, gen)) - else: - gens[index] = value - - return basic_from_dict(f.rep.to_sympy_dict(), *gens) -
    -
    [docs] def lift(f): - """ - Convert algebraic coefficients to rationals. - - Examples - ======== - - >>> from sympy import Poly, I - >>> from sympy.abc import x - - >>> Poly(x**2 + I*x + 1, x, extension=I).lift() - Poly(x**4 + 3*x**2 + 1, x, domain='QQ') - - """ - if hasattr(f.rep, 'lift'): - result = f.rep.lift() - else: # pragma: no cover - raise OperationNotSupported(f, 'lift') - - return f.per(result) -
    -
    [docs] def deflate(f): - """ - Reduce degree of ``f`` by mapping ``x_i**m`` to ``y_i``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> Poly(x**6*y**2 + x**3 + 1, x, y).deflate() - ((3, 2), Poly(x**2*y + x + 1, x, y, domain='ZZ')) - - """ - if hasattr(f.rep, 'deflate'): - J, result = f.rep.deflate() - else: # pragma: no cover - raise OperationNotSupported(f, 'deflate') - - return J, f.per(result) -
    -
    [docs] def inject(f, front=False): - """ - Inject ground domain generators into ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> f = Poly(x**2*y + x*y**3 + x*y + 1, x) - - >>> f.inject() - Poly(x**2*y + x*y**3 + x*y + 1, x, y, domain='ZZ') - >>> f.inject(front=True) - Poly(y**3*x + y*x**2 + y*x + 1, y, x, domain='ZZ') - - """ - dom = f.rep.dom - - if dom.is_Numerical: - return f - elif not dom.is_Poly: - raise DomainError("can't inject generators over %s" % dom) - - if hasattr(f.rep, 'inject'): - result = f.rep.inject(front=front) - else: # pragma: no cover - raise OperationNotSupported(f, 'inject') - - if front: - gens = dom.gens + f.gens - else: - gens = f.gens + dom.gens - - return f.new(result, *gens) -
    -
    [docs] def eject(f, *gens): - """ - Eject selected generators into the ground domain. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> f = Poly(x**2*y + x*y**3 + x*y + 1, x, y) - - >>> f.eject(x) - Poly(x*y**3 + (x**2 + x)*y + 1, y, domain='ZZ[x]') - >>> f.eject(y) - Poly(y*x**2 + (y**3 + y)*x + 1, x, domain='ZZ[y]') - - """ - dom = f.rep.dom - - if not dom.is_Numerical: - raise DomainError("can't eject generators over %s" % dom) - - n, k = len(f.gens), len(gens) - - if f.gens[:k] == gens: - _gens, front = f.gens[n - k:], True - elif f.gens[-k:] == gens: - _gens, front = f.gens[:n - k], False - else: - raise NotImplementedError( - "can only eject front or back generators") - - dom = dom.inject(*gens) - - if hasattr(f.rep, 'eject'): - result = f.rep.eject(dom, front=front) - else: # pragma: no cover - raise OperationNotSupported(f, 'eject') - - return f.new(result, *_gens) -
    -
    [docs] def terms_gcd(f): - """ - Remove GCD of terms from the polynomial ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> Poly(x**6*y**2 + x**3*y, x, y).terms_gcd() - ((3, 1), Poly(x**3*y + 1, x, y, domain='ZZ')) - - """ - if hasattr(f.rep, 'terms_gcd'): - J, result = f.rep.terms_gcd() - else: # pragma: no cover - raise OperationNotSupported(f, 'terms_gcd') - - return J, f.per(result) -
    -
    [docs] def add_ground(f, coeff): - """ - Add an element of the ground domain to ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x + 1).add_ground(2) - Poly(x + 3, x, domain='ZZ') - - """ - if hasattr(f.rep, 'add_ground'): - result = f.rep.add_ground(coeff) - else: # pragma: no cover - raise OperationNotSupported(f, 'add_ground') - - return f.per(result) -
    -
    [docs] def sub_ground(f, coeff): - """ - Subtract an element of the ground domain from ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x + 1).sub_ground(2) - Poly(x - 1, x, domain='ZZ') - - """ - if hasattr(f.rep, 'sub_ground'): - result = f.rep.sub_ground(coeff) - else: # pragma: no cover - raise OperationNotSupported(f, 'sub_ground') - - return f.per(result) -
    -
    [docs] def mul_ground(f, coeff): - """ - Multiply ``f`` by a an element of the ground domain. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x + 1).mul_ground(2) - Poly(2*x + 2, x, domain='ZZ') - - """ - if hasattr(f.rep, 'mul_ground'): - result = f.rep.mul_ground(coeff) - else: # pragma: no cover - raise OperationNotSupported(f, 'mul_ground') - - return f.per(result) -
    -
    [docs] def quo_ground(f, coeff): - """ - Quotient of ``f`` by a an element of the ground domain. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(2*x + 4).quo_ground(2) - Poly(x + 2, x, domain='ZZ') - - >>> Poly(2*x + 3).quo_ground(2) - Poly(x + 1, x, domain='ZZ') - - """ - if hasattr(f.rep, 'quo_ground'): - result = f.rep.quo_ground(coeff) - else: # pragma: no cover - raise OperationNotSupported(f, 'quo_ground') - - return f.per(result) -
    -
    [docs] def exquo_ground(f, coeff): - """ - Exact quotient of ``f`` by a an element of the ground domain. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(2*x + 4).exquo_ground(2) - Poly(x + 2, x, domain='ZZ') - - >>> Poly(2*x + 3).exquo_ground(2) - Traceback (most recent call last): - ... - ExactQuotientFailed: 2 does not divide 3 in ZZ - - """ - if hasattr(f.rep, 'exquo_ground'): - result = f.rep.exquo_ground(coeff) - else: # pragma: no cover - raise OperationNotSupported(f, 'exquo_ground') - - return f.per(result) -
    -
    [docs] def abs(f): - """ - Make all coefficients in ``f`` positive. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 - 1, x).abs() - Poly(x**2 + 1, x, domain='ZZ') - - """ - if hasattr(f.rep, 'abs'): - result = f.rep.abs() - else: # pragma: no cover - raise OperationNotSupported(f, 'abs') - - return f.per(result) -
    -
    [docs] def neg(f): - """ - Negate all coefficients in ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 - 1, x).neg() - Poly(-x**2 + 1, x, domain='ZZ') - - >>> -Poly(x**2 - 1, x) - Poly(-x**2 + 1, x, domain='ZZ') - - """ - if hasattr(f.rep, 'neg'): - result = f.rep.neg() - else: # pragma: no cover - raise OperationNotSupported(f, 'neg') - - return f.per(result) -
    -
    [docs] def add(f, g): - """ - Add two polynomials ``f`` and ``g``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 + 1, x).add(Poly(x - 2, x)) - Poly(x**2 + x - 1, x, domain='ZZ') - - >>> Poly(x**2 + 1, x) + Poly(x - 2, x) - Poly(x**2 + x - 1, x, domain='ZZ') - - """ - g = sympify(g) - - if not g.is_Poly: - return f.add_ground(g) - - _, per, F, G = f._unify(g) - - if hasattr(f.rep, 'add'): - result = F.add(G) - else: # pragma: no cover - raise OperationNotSupported(f, 'add') - - return per(result) -
    -
    [docs] def sub(f, g): - """ - Subtract two polynomials ``f`` and ``g``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 + 1, x).sub(Poly(x - 2, x)) - Poly(x**2 - x + 3, x, domain='ZZ') - - >>> Poly(x**2 + 1, x) - Poly(x - 2, x) - Poly(x**2 - x + 3, x, domain='ZZ') - - """ - g = sympify(g) - - if not g.is_Poly: - return f.sub_ground(g) - - _, per, F, G = f._unify(g) - - if hasattr(f.rep, 'sub'): - result = F.sub(G) - else: # pragma: no cover - raise OperationNotSupported(f, 'sub') - - return per(result) -
    -
    [docs] def mul(f, g): - """ - Multiply two polynomials ``f`` and ``g``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 + 1, x).mul(Poly(x - 2, x)) - Poly(x**3 - 2*x**2 + x - 2, x, domain='ZZ') - - >>> Poly(x**2 + 1, x)*Poly(x - 2, x) - Poly(x**3 - 2*x**2 + x - 2, x, domain='ZZ') - - """ - g = sympify(g) - - if not g.is_Poly: - return f.mul_ground(g) - - _, per, F, G = f._unify(g) - - if hasattr(f.rep, 'mul'): - result = F.mul(G) - else: # pragma: no cover - raise OperationNotSupported(f, 'mul') - - return per(result) -
    -
    [docs] def sqr(f): - """ - Square a polynomial ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x - 2, x).sqr() - Poly(x**2 - 4*x + 4, x, domain='ZZ') - - >>> Poly(x - 2, x)**2 - Poly(x**2 - 4*x + 4, x, domain='ZZ') - - """ - if hasattr(f.rep, 'sqr'): - result = f.rep.sqr() - else: # pragma: no cover - raise OperationNotSupported(f, 'sqr') - - return f.per(result) -
    -
    [docs] def pow(f, n): - """ - Raise ``f`` to a non-negative power ``n``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x - 2, x).pow(3) - Poly(x**3 - 6*x**2 + 12*x - 8, x, domain='ZZ') - - >>> Poly(x - 2, x)**3 - Poly(x**3 - 6*x**2 + 12*x - 8, x, domain='ZZ') - - """ - n = int(n) - - if hasattr(f.rep, 'pow'): - result = f.rep.pow(n) - else: # pragma: no cover - raise OperationNotSupported(f, 'pow') - - return f.per(result) -
    -
    [docs] def pdiv(f, g): - """ - Polynomial pseudo-division of ``f`` by ``g``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 + 1, x).pdiv(Poly(2*x - 4, x)) - (Poly(2*x + 4, x, domain='ZZ'), Poly(20, x, domain='ZZ')) - - """ - _, per, F, G = f._unify(g) - - if hasattr(f.rep, 'pdiv'): - q, r = F.pdiv(G) - else: # pragma: no cover - raise OperationNotSupported(f, 'pdiv') - - return per(q), per(r) -
    -
    [docs] def prem(f, g): - """ - Polynomial pseudo-remainder of ``f`` by ``g``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 + 1, x).prem(Poly(2*x - 4, x)) - Poly(20, x, domain='ZZ') - - """ - _, per, F, G = f._unify(g) - - if hasattr(f.rep, 'prem'): - result = F.prem(G) - else: # pragma: no cover - raise OperationNotSupported(f, 'prem') - - return per(result) -
    -
    [docs] def pquo(f, g): - """ - Polynomial pseudo-quotient of ``f`` by ``g``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 + 1, x).pquo(Poly(2*x - 4, x)) - Poly(2*x + 4, x, domain='ZZ') - - >>> Poly(x**2 - 1, x).pquo(Poly(2*x - 2, x)) - Poly(2*x + 2, x, domain='ZZ') - - """ - _, per, F, G = f._unify(g) - - if hasattr(f.rep, 'pquo'): - result = F.pquo(G) - else: # pragma: no cover - raise OperationNotSupported(f, 'pquo') - - return per(result) -
    -
    [docs] def pexquo(f, g): - """ - Polynomial exact pseudo-quotient of ``f`` by ``g``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 - 1, x).pexquo(Poly(2*x - 2, x)) - Poly(2*x + 2, x, domain='ZZ') - - >>> Poly(x**2 + 1, x).pexquo(Poly(2*x - 4, x)) - Traceback (most recent call last): - ... - ExactQuotientFailed: 2*x - 4 does not divide x**2 + 1 - - """ - _, per, F, G = f._unify(g) - - if hasattr(f.rep, 'pexquo'): - try: - result = F.pexquo(G) - except ExactQuotientFailed as exc: - raise exc.new(f.as_expr(), g.as_expr()) - else: # pragma: no cover - raise OperationNotSupported(f, 'pexquo') - - return per(result) -
    -
    [docs] def div(f, g, auto=True): - """ - Polynomial division with remainder of ``f`` by ``g``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 + 1, x).div(Poly(2*x - 4, x)) - (Poly(1/2*x + 1, x, domain='QQ'), Poly(5, x, domain='QQ')) - - >>> Poly(x**2 + 1, x).div(Poly(2*x - 4, x), auto=False) - (Poly(0, x, domain='ZZ'), Poly(x**2 + 1, x, domain='ZZ')) - - """ - dom, per, F, G = f._unify(g) - retract = False - - if auto and dom.has_Ring and not dom.has_Field: - F, G = F.to_field(), G.to_field() - retract = True - - if hasattr(f.rep, 'div'): - q, r = F.div(G) - else: # pragma: no cover - raise OperationNotSupported(f, 'div') - - if retract: - try: - Q, R = q.to_ring(), r.to_ring() - except CoercionFailed: - pass - else: - q, r = Q, R - - return per(q), per(r) -
    -
    [docs] def rem(f, g, auto=True): - """ - Computes the polynomial remainder of ``f`` by ``g``. - - Examples - ======== - - >>> from sympy import Poly, ZZ, QQ - >>> from sympy.abc import x - - >>> Poly(x**2 + 1, x).rem(Poly(2*x - 4, x)) - Poly(5, x, domain='ZZ') - - >>> Poly(x**2 + 1, x).rem(Poly(2*x - 4, x), auto=False) - Poly(x**2 + 1, x, domain='ZZ') - - """ - dom, per, F, G = f._unify(g) - retract = False - - if auto and dom.has_Ring and not dom.has_Field: - F, G = F.to_field(), G.to_field() - retract = True - - if hasattr(f.rep, 'rem'): - r = F.rem(G) - else: # pragma: no cover - raise OperationNotSupported(f, 'rem') - - if retract: - try: - r = r.to_ring() - except CoercionFailed: - pass - - return per(r) -
    -
    [docs] def quo(f, g, auto=True): - """ - Computes polynomial quotient of ``f`` by ``g``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 + 1, x).quo(Poly(2*x - 4, x)) - Poly(1/2*x + 1, x, domain='QQ') - - >>> Poly(x**2 - 1, x).quo(Poly(x - 1, x)) - Poly(x + 1, x, domain='ZZ') - - """ - dom, per, F, G = f._unify(g) - retract = False - - if auto and dom.has_Ring and not dom.has_Field: - F, G = F.to_field(), G.to_field() - retract = True - - if hasattr(f.rep, 'quo'): - q = F.quo(G) - else: # pragma: no cover - raise OperationNotSupported(f, 'quo') - - if retract: - try: - q = q.to_ring() - except CoercionFailed: - pass - - return per(q) -
    -
    [docs] def exquo(f, g, auto=True): - """ - Computes polynomial exact quotient of ``f`` by ``g``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 - 1, x).exquo(Poly(x - 1, x)) - Poly(x + 1, x, domain='ZZ') - - >>> Poly(x**2 + 1, x).exquo(Poly(2*x - 4, x)) - Traceback (most recent call last): - ... - ExactQuotientFailed: 2*x - 4 does not divide x**2 + 1 - - """ - dom, per, F, G = f._unify(g) - retract = False - - if auto and dom.has_Ring and not dom.has_Field: - F, G = F.to_field(), G.to_field() - retract = True - - if hasattr(f.rep, 'exquo'): - try: - q = F.exquo(G) - except ExactQuotientFailed as exc: - raise exc.new(f.as_expr(), g.as_expr()) - else: # pragma: no cover - raise OperationNotSupported(f, 'exquo') - - if retract: - try: - q = q.to_ring() - except CoercionFailed: - pass - - return per(q) -
    - def _gen_to_level(f, gen): - """Returns level associated with the given generator. """ - if isinstance(gen, int): - length = len(f.gens) - - if -length <= gen < length: - if gen < 0: - return length + gen - else: - return gen - else: - raise PolynomialError("-%s <= gen < %s expected, got %s" % - (length, length, gen)) - else: - try: - return list(f.gens).index(sympify(gen)) - except ValueError: - raise PolynomialError( - "a valid generator expected, got %s" % gen) - -
    [docs] def degree(f, gen=0): - """ - Returns degree of ``f`` in ``x_j``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> Poly(x**2 + y*x + 1, x, y).degree() - 2 - >>> Poly(x**2 + y*x + y, x, y).degree(y) - 1 - - """ - j = f._gen_to_level(gen) - - if hasattr(f.rep, 'degree'): - return f.rep.degree(j) - else: # pragma: no cover - raise OperationNotSupported(f, 'degree') -
    -
    [docs] def degree_list(f): - """ - Returns a list of degrees of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> Poly(x**2 + y*x + 1, x, y).degree_list() - (2, 1) - - """ - if hasattr(f.rep, 'degree_list'): - return f.rep.degree_list() - else: # pragma: no cover - raise OperationNotSupported(f, 'degree_list') -
    -
    [docs] def total_degree(f): - """ - Returns the total degree of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> Poly(x**2 + y*x + 1, x, y).total_degree() - 2 - >>> Poly(x + y**5, x, y).total_degree() - 5 - - """ - if hasattr(f.rep, 'total_degree'): - return f.rep.total_degree() - else: # pragma: no cover - raise OperationNotSupported(f, 'total_degree') -
    -
    [docs] def homogeneous_order(f): - """ - Returns the homogeneous order of ``f``. - - A homogeneous polynomial is a polynomial whose all monomials with - non-zero coefficients have the same total degree. This degree is - the homogeneous order of ``f``. If you only want to check if a - polynomial is homogeneous, then use :func:`Poly.is_homogeneous`. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> f = Poly(x**5 + 2*x**3*y**2 + 9*x*y**4) - >>> f.homogeneous_order() - 5 - - """ - if hasattr(f.rep, 'homogeneous_order'): - return f.rep.homogeneous_order() - else: # pragma: no cover - raise OperationNotSupported(f, 'homogeneous_order') -
    -
    [docs] def LC(f, order=None): - """ - Returns the leading coefficient of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(4*x**3 + 2*x**2 + 3*x, x).LC() - 4 - - """ - if order is not None: - return f.coeffs(order)[0] - - if hasattr(f.rep, 'LC'): - result = f.rep.LC() - else: # pragma: no cover - raise OperationNotSupported(f, 'LC') - - return f.rep.dom.to_sympy(result) -
    -
    [docs] def TC(f): - """ - Returns the trailing coefficient of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**3 + 2*x**2 + 3*x, x).TC() - 0 - - """ - if hasattr(f.rep, 'TC'): - result = f.rep.TC() - else: # pragma: no cover - raise OperationNotSupported(f, 'TC') - - return f.rep.dom.to_sympy(result) -
    -
    [docs] def EC(f, order=None): - """ - Returns the last non-zero coefficient of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**3 + 2*x**2 + 3*x, x).EC() - 3 - - """ - if hasattr(f.rep, 'coeffs'): - return f.coeffs(order)[-1] - else: # pragma: no cover - raise OperationNotSupported(f, 'EC') -
    -
    [docs] def nth(f, *N): - """ - Returns the ``n``-th coefficient of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> Poly(x**3 + 2*x**2 + 3*x, x).nth(2) - 2 - >>> Poly(x**3 + 2*x*y**2 + y**2, x, y).nth(1, 2) - 2 - - """ - if hasattr(f.rep, 'nth'): - result = f.rep.nth(*list(map(int, N))) - else: # pragma: no cover - raise OperationNotSupported(f, 'nth') - - return f.rep.dom.to_sympy(result) -
    -
    [docs] def LM(f, order=None): - """ - Returns the leading monomial of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> Poly(4*x**2 + 2*x*y**2 + x*y + 3*y, x, y).LM() - x**2*y**0 - - """ - return Monomial(f.monoms(order)[0], f.gens) -
    -
    [docs] def EM(f, order=None): - """ - Returns the last non-zero monomial of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> Poly(4*x**2 + 2*x*y**2 + x*y + 3*y, x, y).EM() - x**0*y**1 - - """ - return Monomial(f.monoms(order)[-1], f.gens) -
    -
    [docs] def LT(f, order=None): - """ - Returns the leading term of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> Poly(4*x**2 + 2*x*y**2 + x*y + 3*y, x, y).LT() - (x**2*y**0, 4) - - """ - monom, coeff = f.terms(order)[0] - return Monomial(monom, f.gens), coeff -
    -
    [docs] def ET(f, order=None): - """ - Returns the last non-zero term of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> Poly(4*x**2 + 2*x*y**2 + x*y + 3*y, x, y).ET() - (x**0*y**1, 3) - - """ - monom, coeff = f.terms(order)[-1] - return Monomial(monom, f.gens), coeff -
    -
    [docs] def max_norm(f): - """ - Returns maximum norm of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(-x**2 + 2*x - 3, x).max_norm() - 3 - - """ - if hasattr(f.rep, 'max_norm'): - result = f.rep.max_norm() - else: # pragma: no cover - raise OperationNotSupported(f, 'max_norm') - - return f.rep.dom.to_sympy(result) -
    -
    [docs] def l1_norm(f): - """ - Returns l1 norm of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(-x**2 + 2*x - 3, x).l1_norm() - 6 - - """ - if hasattr(f.rep, 'l1_norm'): - result = f.rep.l1_norm() - else: # pragma: no cover - raise OperationNotSupported(f, 'l1_norm') - - return f.rep.dom.to_sympy(result) -
    -
    [docs] def clear_denoms(f, convert=False): - """ - Clear denominators, but keep the ground domain. - - Examples - ======== - - >>> from sympy import Poly, S, QQ - >>> from sympy.abc import x - - >>> f = Poly(x/2 + S(1)/3, x, domain=QQ) - - >>> f.clear_denoms() - (6, Poly(3*x + 2, x, domain='QQ')) - >>> f.clear_denoms(convert=True) - (6, Poly(3*x + 2, x, domain='ZZ')) - - """ - if not f.rep.dom.has_Field: - return S.One, f - - dom = f.get_domain() - if dom.has_assoc_Ring: - dom = f.rep.dom.get_ring() - - if hasattr(f.rep, 'clear_denoms'): - coeff, result = f.rep.clear_denoms() - else: # pragma: no cover - raise OperationNotSupported(f, 'clear_denoms') - - coeff, f = dom.to_sympy(coeff), f.per(result) - - if not convert: - return coeff, f - else: - return coeff, f.to_ring() -
    -
    [docs] def rat_clear_denoms(f, g): - """ - Clear denominators in a rational function ``f/g``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> f = Poly(x**2/y + 1, x) - >>> g = Poly(x**3 + y, x) - - >>> p, q = f.rat_clear_denoms(g) - - >>> p - Poly(x**2 + y, x, domain='ZZ[y]') - >>> q - Poly(y*x**3 + y**2, x, domain='ZZ[y]') - - """ - dom, per, f, g = f._unify(g) - - f = per(f) - g = per(g) - - if not (dom.has_Field and dom.has_assoc_Ring): - return f, g - - a, f = f.clear_denoms(convert=True) - b, g = g.clear_denoms(convert=True) - - f = f.mul_ground(b) - g = g.mul_ground(a) - - return f, g -
    -
    [docs] def integrate(f, *specs, **args): - """ - Computes indefinite integral of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> Poly(x**2 + 2*x + 1, x).integrate() - Poly(1/3*x**3 + x**2 + x, x, domain='QQ') - - >>> Poly(x*y**2 + x, x, y).integrate((0, 1), (1, 0)) - Poly(1/2*x**2*y**2 + 1/2*x**2, x, y, domain='QQ') - - """ - if args.get('auto', True) and f.rep.dom.has_Ring: - f = f.to_field() - - if hasattr(f.rep, 'integrate'): - if not specs: - return f.per(f.rep.integrate(m=1)) - - rep = f.rep - - for spec in specs: - if type(spec) is tuple: - gen, m = spec - else: - gen, m = spec, 1 - - rep = rep.integrate(int(m), f._gen_to_level(gen)) - - return f.per(rep) - else: # pragma: no cover - raise OperationNotSupported(f, 'integrate') -
    -
    [docs] def diff(f, *specs): - """ - Computes partial derivative of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> Poly(x**2 + 2*x + 1, x).diff() - Poly(2*x + 2, x, domain='ZZ') - - >>> Poly(x*y**2 + x, x, y).diff((0, 0), (1, 1)) - Poly(2*x*y, x, y, domain='ZZ') - - """ - if hasattr(f.rep, 'diff'): - if not specs: - return f.per(f.rep.diff(m=1)) - - rep = f.rep - - for spec in specs: - if type(spec) is tuple: - gen, m = spec - else: - gen, m = spec, 1 - - rep = rep.diff(int(m), f._gen_to_level(gen)) - - return f.per(rep) - else: # pragma: no cover - raise OperationNotSupported(f, 'diff') -
    -
    [docs] def eval(f, x, a=None, auto=True): - """ - Evaluate ``f`` at ``a`` in the given variable. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y, z - - >>> Poly(x**2 + 2*x + 3, x).eval(2) - 11 - - >>> Poly(2*x*y + 3*x + y + 2, x, y).eval(x, 2) - Poly(5*y + 8, y, domain='ZZ') - - >>> f = Poly(2*x*y + 3*x + y + 2*z, x, y, z) - - >>> f.eval({x: 2}) - Poly(5*y + 2*z + 6, y, z, domain='ZZ') - >>> f.eval({x: 2, y: 5}) - Poly(2*z + 31, z, domain='ZZ') - >>> f.eval({x: 2, y: 5, z: 7}) - 45 - - >>> f.eval((2, 5)) - Poly(2*z + 31, z, domain='ZZ') - >>> f(2, 5) - Poly(2*z + 31, z, domain='ZZ') - - """ - if a is None: - if isinstance(x, dict): - mapping = x - - for gen, value in mapping.items(): - f = f.eval(gen, value) - - return f - elif isinstance(x, (tuple, list)): - values = x - - if len(values) > len(f.gens): - raise ValueError("too many values provided") - - for gen, value in zip(f.gens, values): - f = f.eval(gen, value) - - return f - else: - j, a = 0, x - else: - j = f._gen_to_level(x) - - if not hasattr(f.rep, 'eval'): # pragma: no cover - raise OperationNotSupported(f, 'eval') - - try: - result = f.rep.eval(a, j) - except CoercionFailed: - if not auto: - raise DomainError( - "can't evaluate at %s in %s" % (a, f.rep.dom)) - else: - domain, [a] = construct_domain([a]) - f = f.set_domain(domain) - result = f.rep.eval(a, j) - - return f.per(result, remove=j) -
    - def __call__(f, *values): - """ - Evaluate ``f`` at the give values. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y, z - - >>> f = Poly(2*x*y + 3*x + y + 2*z, x, y, z) - - >>> f(2) - Poly(5*y + 2*z + 6, y, z, domain='ZZ') - >>> f(2, 5) - Poly(2*z + 31, z, domain='ZZ') - >>> f(2, 5, 7) - 45 - - """ - return f.eval(values) - -
    [docs] def half_gcdex(f, g, auto=True): - """ - Half extended Euclidean algorithm of ``f`` and ``g``. - - Returns ``(s, h)`` such that ``h = gcd(f, g)`` and ``s*f = h (mod g)``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> f = x**4 - 2*x**3 - 6*x**2 + 12*x + 15 - >>> g = x**3 + x**2 - 4*x - 4 - - >>> Poly(f).half_gcdex(Poly(g)) - (Poly(-1/5*x + 3/5, x, domain='QQ'), Poly(x + 1, x, domain='QQ')) - - """ - dom, per, F, G = f._unify(g) - - if auto and dom.has_Ring: - F, G = F.to_field(), G.to_field() - - if hasattr(f.rep, 'half_gcdex'): - s, h = F.half_gcdex(G) - else: # pragma: no cover - raise OperationNotSupported(f, 'half_gcdex') - - return per(s), per(h) -
    -
    [docs] def gcdex(f, g, auto=True): - """ - Extended Euclidean algorithm of ``f`` and ``g``. - - Returns ``(s, t, h)`` such that ``h = gcd(f, g)`` and ``s*f + t*g = h``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> f = x**4 - 2*x**3 - 6*x**2 + 12*x + 15 - >>> g = x**3 + x**2 - 4*x - 4 - - >>> Poly(f).gcdex(Poly(g)) - (Poly(-1/5*x + 3/5, x, domain='QQ'), - Poly(1/5*x**2 - 6/5*x + 2, x, domain='QQ'), - Poly(x + 1, x, domain='QQ')) - - """ - dom, per, F, G = f._unify(g) - - if auto and dom.has_Ring: - F, G = F.to_field(), G.to_field() - - if hasattr(f.rep, 'gcdex'): - s, t, h = F.gcdex(G) - else: # pragma: no cover - raise OperationNotSupported(f, 'gcdex') - - return per(s), per(t), per(h) -
    -
    [docs] def invert(f, g, auto=True): - """ - Invert ``f`` modulo ``g`` when possible. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 - 1, x).invert(Poly(2*x - 1, x)) - Poly(-4/3, x, domain='QQ') - - >>> Poly(x**2 - 1, x).invert(Poly(x - 1, x)) - Traceback (most recent call last): - ... - NotInvertible: zero divisor - - """ - dom, per, F, G = f._unify(g) - - if auto and dom.has_Ring: - F, G = F.to_field(), G.to_field() - - if hasattr(f.rep, 'invert'): - result = F.invert(G) - else: # pragma: no cover - raise OperationNotSupported(f, 'invert') - - return per(result) -
    -
    [docs] def revert(f, n): - """Compute ``f**(-1)`` mod ``x**n``. """ - if hasattr(f.rep, 'revert'): - result = f.rep.revert(int(n)) - else: # pragma: no cover - raise OperationNotSupported(f, 'revert') - - return f.per(result) -
    -
    [docs] def subresultants(f, g): - """ - Computes the subresultant PRS of ``f`` and ``g``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 + 1, x).subresultants(Poly(x**2 - 1, x)) - [Poly(x**2 + 1, x, domain='ZZ'), - Poly(x**2 - 1, x, domain='ZZ'), - Poly(-2, x, domain='ZZ')] - - """ - _, per, F, G = f._unify(g) - - if hasattr(f.rep, 'subresultants'): - result = F.subresultants(G) - else: # pragma: no cover - raise OperationNotSupported(f, 'subresultants') - - return list(map(per, result)) -
    -
    [docs] def resultant(f, g, includePRS=False): - """ - Computes the resultant of ``f`` and ``g`` via PRS. - - If includePRS=True, it includes the subresultant PRS in the result. - Because the PRS is used to calculate the resultant, this is more - efficient than calling :func:`subresultants` separately. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> f = Poly(x**2 + 1, x) - - >>> f.resultant(Poly(x**2 - 1, x)) - 4 - >>> f.resultant(Poly(x**2 - 1, x), includePRS=True) - (4, [Poly(x**2 + 1, x, domain='ZZ'), Poly(x**2 - 1, x, domain='ZZ'), - Poly(-2, x, domain='ZZ')]) - - """ - _, per, F, G = f._unify(g) - - if hasattr(f.rep, 'resultant'): - if includePRS: - result, R = F.resultant(G, includePRS=includePRS) - else: - result = F.resultant(G) - else: # pragma: no cover - raise OperationNotSupported(f, 'resultant') - - if includePRS: - return (per(result, remove=0), list(map(per, R))) - return per(result, remove=0) -
    -
    [docs] def discriminant(f): - """ - Computes the discriminant of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 + 2*x + 3, x).discriminant() - -8 - - """ - if hasattr(f.rep, 'discriminant'): - result = f.rep.discriminant() - else: # pragma: no cover - raise OperationNotSupported(f, 'discriminant') - - return f.per(result, remove=0) -
    -
    [docs] def cofactors(f, g): - """ - Returns the GCD of ``f`` and ``g`` and their cofactors. - - Returns polynomials ``(h, cff, cfg)`` such that ``h = gcd(f, g)``, and - ``cff = quo(f, h)`` and ``cfg = quo(g, h)`` are, so called, cofactors - of ``f`` and ``g``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 - 1, x).cofactors(Poly(x**2 - 3*x + 2, x)) - (Poly(x - 1, x, domain='ZZ'), - Poly(x + 1, x, domain='ZZ'), - Poly(x - 2, x, domain='ZZ')) - - """ - _, per, F, G = f._unify(g) - - if hasattr(f.rep, 'cofactors'): - h, cff, cfg = F.cofactors(G) - else: # pragma: no cover - raise OperationNotSupported(f, 'cofactors') - - return per(h), per(cff), per(cfg) -
    -
    [docs] def gcd(f, g): - """ - Returns the polynomial GCD of ``f`` and ``g``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 - 1, x).gcd(Poly(x**2 - 3*x + 2, x)) - Poly(x - 1, x, domain='ZZ') - - """ - _, per, F, G = f._unify(g) - - if hasattr(f.rep, 'gcd'): - result = F.gcd(G) - else: # pragma: no cover - raise OperationNotSupported(f, 'gcd') - - return per(result) -
    -
    [docs] def lcm(f, g): - """ - Returns polynomial LCM of ``f`` and ``g``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 - 1, x).lcm(Poly(x**2 - 3*x + 2, x)) - Poly(x**3 - 2*x**2 - x + 2, x, domain='ZZ') - - """ - _, per, F, G = f._unify(g) - - if hasattr(f.rep, 'lcm'): - result = F.lcm(G) - else: # pragma: no cover - raise OperationNotSupported(f, 'lcm') - - return per(result) -
    -
    [docs] def trunc(f, p): - """ - Reduce ``f`` modulo a constant ``p``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(2*x**3 + 3*x**2 + 5*x + 7, x).trunc(3) - Poly(-x**3 - x + 1, x, domain='ZZ') - - """ - p = f.rep.dom.convert(p) - - if hasattr(f.rep, 'trunc'): - result = f.rep.trunc(p) - else: # pragma: no cover - raise OperationNotSupported(f, 'trunc') - - return f.per(result) -
    -
    [docs] def monic(f, auto=True): - """ - Divides all coefficients by ``LC(f)``. - - Examples - ======== - - >>> from sympy import Poly, ZZ - >>> from sympy.abc import x - - >>> Poly(3*x**2 + 6*x + 9, x, domain=ZZ).monic() - Poly(x**2 + 2*x + 3, x, domain='QQ') - - >>> Poly(3*x**2 + 4*x + 2, x, domain=ZZ).monic() - Poly(x**2 + 4/3*x + 2/3, x, domain='QQ') - - """ - if auto and f.rep.dom.has_Ring: - f = f.to_field() - - if hasattr(f.rep, 'monic'): - result = f.rep.monic() - else: # pragma: no cover - raise OperationNotSupported(f, 'monic') - - return f.per(result) -
    -
    [docs] def content(f): - """ - Returns the GCD of polynomial coefficients. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(6*x**2 + 8*x + 12, x).content() - 2 - - """ - if hasattr(f.rep, 'content'): - result = f.rep.content() - else: # pragma: no cover - raise OperationNotSupported(f, 'content') - - return f.rep.dom.to_sympy(result) -
    -
    [docs] def primitive(f): - """ - Returns the content and a primitive form of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(2*x**2 + 8*x + 12, x).primitive() - (2, Poly(x**2 + 4*x + 6, x, domain='ZZ')) - - """ - if hasattr(f.rep, 'primitive'): - cont, result = f.rep.primitive() - else: # pragma: no cover - raise OperationNotSupported(f, 'primitive') - - return f.rep.dom.to_sympy(cont), f.per(result) -
    -
    [docs] def compose(f, g): - """ - Computes the functional composition of ``f`` and ``g``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 + x, x).compose(Poly(x - 1, x)) - Poly(x**2 - x, x, domain='ZZ') - - """ - _, per, F, G = f._unify(g) - - if hasattr(f.rep, 'compose'): - result = F.compose(G) - else: # pragma: no cover - raise OperationNotSupported(f, 'compose') - - return per(result) -
    -
    [docs] def decompose(f): - """ - Computes a functional decomposition of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**4 + 2*x**3 - x - 1, x, domain='ZZ').decompose() - [Poly(x**2 - x - 1, x, domain='ZZ'), Poly(x**2 + x, x, domain='ZZ')] - - """ - if hasattr(f.rep, 'decompose'): - result = f.rep.decompose() - else: # pragma: no cover - raise OperationNotSupported(f, 'decompose') - - return list(map(f.per, result)) -
    -
    [docs] def shift(f, a): - """ - Efficiently compute Taylor shift ``f(x + a)``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 - 2*x + 1, x).shift(2) - Poly(x**2 + 2*x + 1, x, domain='ZZ') - - """ - if hasattr(f.rep, 'shift'): - result = f.rep.shift(a) - else: # pragma: no cover - raise OperationNotSupported(f, 'shift') - - return f.per(result) -
    -
    [docs] def sturm(f, auto=True): - """ - Computes the Sturm sequence of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**3 - 2*x**2 + x - 3, x).sturm() - [Poly(x**3 - 2*x**2 + x - 3, x, domain='QQ'), - Poly(3*x**2 - 4*x + 1, x, domain='QQ'), - Poly(2/9*x + 25/9, x, domain='QQ'), - Poly(-2079/4, x, domain='QQ')] - - """ - if auto and f.rep.dom.has_Ring: - f = f.to_field() - - if hasattr(f.rep, 'sturm'): - result = f.rep.sturm() - else: # pragma: no cover - raise OperationNotSupported(f, 'sturm') - - return list(map(f.per, result)) -
    -
    [docs] def gff_list(f): - """ - Computes greatest factorial factorization of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> f = x**5 + 2*x**4 - x**3 - 2*x**2 - - >>> Poly(f).gff_list() - [(Poly(x, x, domain='ZZ'), 1), (Poly(x + 2, x, domain='ZZ'), 4)] - - """ - if hasattr(f.rep, 'gff_list'): - result = f.rep.gff_list() - else: # pragma: no cover - raise OperationNotSupported(f, 'gff_list') - - return [ (f.per(g), k) for g, k in result ] -
    -
    [docs] def sqf_norm(f): - """ - Computes square-free norm of ``f``. - - Returns ``s``, ``f``, ``r``, such that ``g(x) = f(x-sa)`` and - ``r(x) = Norm(g(x))`` is a square-free polynomial over ``K``, - where ``a`` is the algebraic extension of the ground domain. - - Examples - ======== - - >>> from sympy import Poly, sqrt - >>> from sympy.abc import x - - >>> s, f, r = Poly(x**2 + 1, x, extension=[sqrt(3)]).sqf_norm() - - >>> s - 1 - >>> f - Poly(x**2 - 2*sqrt(3)*x + 4, x, domain='QQ<sqrt(3)>') - >>> r - Poly(x**4 - 4*x**2 + 16, x, domain='QQ') - - """ - if hasattr(f.rep, 'sqf_norm'): - s, g, r = f.rep.sqf_norm() - else: # pragma: no cover - raise OperationNotSupported(f, 'sqf_norm') - - return s, f.per(g), f.per(r) -
    -
    [docs] def sqf_part(f): - """ - Computes square-free part of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**3 - 3*x - 2, x).sqf_part() - Poly(x**2 - x - 2, x, domain='ZZ') - - """ - if hasattr(f.rep, 'sqf_part'): - result = f.rep.sqf_part() - else: # pragma: no cover - raise OperationNotSupported(f, 'sqf_part') - - return f.per(result) -
    -
    [docs] def sqf_list(f, all=False): - """ - Returns a list of square-free factors of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> f = 2*x**5 + 16*x**4 + 50*x**3 + 76*x**2 + 56*x + 16 - - >>> Poly(f).sqf_list() - (2, [(Poly(x + 1, x, domain='ZZ'), 2), - (Poly(x + 2, x, domain='ZZ'), 3)]) - - >>> Poly(f).sqf_list(all=True) - (2, [(Poly(1, x, domain='ZZ'), 1), - (Poly(x + 1, x, domain='ZZ'), 2), - (Poly(x + 2, x, domain='ZZ'), 3)]) - - """ - if hasattr(f.rep, 'sqf_list'): - coeff, factors = f.rep.sqf_list(all) - else: # pragma: no cover - raise OperationNotSupported(f, 'sqf_list') - - return f.rep.dom.to_sympy(coeff), [ (f.per(g), k) for g, k in factors ] -
    -
    [docs] def sqf_list_include(f, all=False): - """ - Returns a list of square-free factors of ``f``. - - Examples - ======== - - >>> from sympy import Poly, expand - >>> from sympy.abc import x - - >>> f = expand(2*(x + 1)**3*x**4) - >>> f - 2*x**7 + 6*x**6 + 6*x**5 + 2*x**4 - - >>> Poly(f).sqf_list_include() - [(Poly(2, x, domain='ZZ'), 1), - (Poly(x + 1, x, domain='ZZ'), 3), - (Poly(x, x, domain='ZZ'), 4)] - - >>> Poly(f).sqf_list_include(all=True) - [(Poly(2, x, domain='ZZ'), 1), - (Poly(1, x, domain='ZZ'), 2), - (Poly(x + 1, x, domain='ZZ'), 3), - (Poly(x, x, domain='ZZ'), 4)] - - """ - if hasattr(f.rep, 'sqf_list_include'): - factors = f.rep.sqf_list_include(all) - else: # pragma: no cover - raise OperationNotSupported(f, 'sqf_list_include') - - return [ (f.per(g), k) for g, k in factors ] -
    -
    [docs] def factor_list(f): - """ - Returns a list of irreducible factors of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> f = 2*x**5 + 2*x**4*y + 4*x**3 + 4*x**2*y + 2*x + 2*y - - >>> Poly(f).factor_list() - (2, [(Poly(x + y, x, y, domain='ZZ'), 1), - (Poly(x**2 + 1, x, y, domain='ZZ'), 2)]) - - """ - if hasattr(f.rep, 'factor_list'): - try: - coeff, factors = f.rep.factor_list() - except DomainError: - return S.One, [(f, 1)] - else: # pragma: no cover - raise OperationNotSupported(f, 'factor_list') - - return f.rep.dom.to_sympy(coeff), [ (f.per(g), k) for g, k in factors ] -
    -
    [docs] def factor_list_include(f): - """ - Returns a list of irreducible factors of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> f = 2*x**5 + 2*x**4*y + 4*x**3 + 4*x**2*y + 2*x + 2*y - - >>> Poly(f).factor_list_include() - [(Poly(2*x + 2*y, x, y, domain='ZZ'), 1), - (Poly(x**2 + 1, x, y, domain='ZZ'), 2)] - - """ - if hasattr(f.rep, 'factor_list_include'): - try: - factors = f.rep.factor_list_include() - except DomainError: - return [(f, 1)] - else: # pragma: no cover - raise OperationNotSupported(f, 'factor_list_include') - - return [ (f.per(g), k) for g, k in factors ] -
    -
    [docs] def intervals(f, all=False, eps=None, inf=None, sup=None, fast=False, sqf=False): - """ - Compute isolating intervals for roots of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 - 3, x).intervals() - [((-2, -1), 1), ((1, 2), 1)] - >>> Poly(x**2 - 3, x).intervals(eps=1e-2) - [((-26/15, -19/11), 1), ((19/11, 26/15), 1)] - - """ - if eps is not None: - eps = QQ.convert(eps) - - if eps <= 0: - raise ValueError("'eps' must be a positive rational") - - if inf is not None: - inf = QQ.convert(inf) - if sup is not None: - sup = QQ.convert(sup) - - if hasattr(f.rep, 'intervals'): - result = f.rep.intervals( - all=all, eps=eps, inf=inf, sup=sup, fast=fast, sqf=sqf) - else: # pragma: no cover - raise OperationNotSupported(f, 'intervals') - - if sqf: - def _real(interval): - s, t = interval - return (QQ.to_sympy(s), QQ.to_sympy(t)) - - if not all: - return list(map(_real, result)) - - def _complex(rectangle): - (u, v), (s, t) = rectangle - return (QQ.to_sympy(u) + I*QQ.to_sympy(v), - QQ.to_sympy(s) + I*QQ.to_sympy(t)) - - real_part, complex_part = result - - return list(map(_real, real_part)), list(map(_complex, complex_part)) - else: - def _real(interval): - (s, t), k = interval - return ((QQ.to_sympy(s), QQ.to_sympy(t)), k) - - if not all: - return list(map(_real, result)) - - def _complex(rectangle): - ((u, v), (s, t)), k = rectangle - return ((QQ.to_sympy(u) + I*QQ.to_sympy(v), - QQ.to_sympy(s) + I*QQ.to_sympy(t)), k) - - real_part, complex_part = result - - return list(map(_real, real_part)), list(map(_complex, complex_part)) -
    -
    [docs] def refine_root(f, s, t, eps=None, steps=None, fast=False, check_sqf=False): - """ - Refine an isolating interval of a root to the given precision. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 - 3, x).refine_root(1, 2, eps=1e-2) - (19/11, 26/15) - - """ - if check_sqf and not f.is_sqf: - raise PolynomialError("only square-free polynomials supported") - - s, t = QQ.convert(s), QQ.convert(t) - - if eps is not None: - eps = QQ.convert(eps) - - if eps <= 0: - raise ValueError("'eps' must be a positive rational") - - if steps is not None: - steps = int(steps) - elif eps is None: - steps = 1 - - if hasattr(f.rep, 'refine_root'): - S, T = f.rep.refine_root(s, t, eps=eps, steps=steps, fast=fast) - else: # pragma: no cover - raise OperationNotSupported(f, 'refine_root') - - return QQ.to_sympy(S), QQ.to_sympy(T) -
    -
    [docs] def count_roots(f, inf=None, sup=None): - """ - Return the number of roots of ``f`` in ``[inf, sup]`` interval. - - Examples - ======== - - >>> from sympy import Poly, I - >>> from sympy.abc import x - - >>> Poly(x**4 - 4, x).count_roots(-3, 3) - 2 - >>> Poly(x**4 - 4, x).count_roots(0, 1 + 3*I) - 1 - - """ - inf_real, sup_real = True, True - - if inf is not None: - inf = sympify(inf) - - if inf is S.NegativeInfinity: - inf = None - else: - re, im = inf.as_real_imag() - - if not im: - inf = QQ.convert(inf) - else: - inf, inf_real = list(map(QQ.convert, (re, im))), False - - if sup is not None: - sup = sympify(sup) - - if sup is S.Infinity: - sup = None - else: - re, im = sup.as_real_imag() - - if not im: - sup = QQ.convert(sup) - else: - sup, sup_real = list(map(QQ.convert, (re, im))), False - - if inf_real and sup_real: - if hasattr(f.rep, 'count_real_roots'): - count = f.rep.count_real_roots(inf=inf, sup=sup) - else: # pragma: no cover - raise OperationNotSupported(f, 'count_real_roots') - else: - if inf_real and inf is not None: - inf = (inf, QQ.zero) - - if sup_real and sup is not None: - sup = (sup, QQ.zero) - - if hasattr(f.rep, 'count_complex_roots'): - count = f.rep.count_complex_roots(inf=inf, sup=sup) - else: # pragma: no cover - raise OperationNotSupported(f, 'count_complex_roots') - - return Integer(count) -
    -
    [docs] def root(f, index, radicals=True): - """ - Get an indexed root of a polynomial. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> f = Poly(2*x**3 - 7*x**2 + 4*x + 4) - - >>> f.root(0) - -1/2 - >>> f.root(1) - 2 - >>> f.root(2) - 2 - >>> f.root(3) - Traceback (most recent call last): - ... - IndexError: root index out of [-3, 2] range, got 3 - - >>> Poly(x**5 + x + 1).root(0) - RootOf(x**3 - x**2 + 1, 0) - - """ - return sympy.polys.rootoftools.RootOf(f, index, radicals=radicals) -
    -
    [docs] def real_roots(f, multiple=True, radicals=True): - """ - Return a list of real roots with multiplicities. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(2*x**3 - 7*x**2 + 4*x + 4).real_roots() - [-1/2, 2, 2] - >>> Poly(x**3 + x + 1).real_roots() - [RootOf(x**3 + x + 1, 0)] - - """ - reals = sympy.polys.rootoftools.RootOf.real_roots(f, radicals=radicals) - - if multiple: - return reals - else: - return group(reals, multiple=False) -
    -
    [docs] def all_roots(f, multiple=True, radicals=True): - """ - Return a list of real and complex roots with multiplicities. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(2*x**3 - 7*x**2 + 4*x + 4).all_roots() - [-1/2, 2, 2] - >>> Poly(x**3 + x + 1).all_roots() - [RootOf(x**3 + x + 1, 0), - RootOf(x**3 + x + 1, 1), - RootOf(x**3 + x + 1, 2)] - - """ - roots = sympy.polys.rootoftools.RootOf.all_roots(f, radicals=radicals) - - if multiple: - return roots - else: - return group(roots, multiple=False) -
    -
    [docs] def nroots(f, n=15, maxsteps=50, cleanup=True, error=False): - """ - Compute numerical approximations of roots of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 - 3).nroots(n=15) - [-1.73205080756888, 1.73205080756888] - >>> Poly(x**2 - 3).nroots(n=30) - [-1.73205080756887729352744634151, 1.73205080756887729352744634151] - - """ - if f.is_multivariate: - raise MultivariatePolynomialError( - "can't compute numerical roots of %s" % f) - - if f.degree() <= 0: - return [] - - coeffs = [ coeff.evalf(n=n).as_real_imag() - for coeff in f.all_coeffs() ] - - dps = sympy.mpmath.mp.dps - sympy.mpmath.mp.dps = n - - try: - try: - coeffs = [ sympy.mpmath.mpc(*coeff) for coeff in coeffs ] - except TypeError: - raise DomainError( - "numerical domain expected, got %s" % f.rep.dom) - - result = sympy.mpmath.polyroots( - coeffs, maxsteps=maxsteps, cleanup=cleanup, error=error) - - if error: - roots, error = result - else: - roots, error = result, None - - roots = list(map(sympify, sorted(roots, key=lambda r: (r.real, r.imag)))) - finally: - sympy.mpmath.mp.dps = dps - - if error is not None: - return roots, sympify(error) - else: - return roots -
    -
    [docs] def ground_roots(f): - """ - Compute roots of ``f`` by factorization in the ground domain. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**6 - 4*x**4 + 4*x**3 - x**2).ground_roots() - {0: 2, 1: 2} - - """ - if f.is_multivariate: - raise MultivariatePolynomialError( - "can't compute ground roots of %s" % f) - - roots = {} - - for factor, k in f.factor_list()[1]: - if factor.is_linear: - a, b = factor.all_coeffs() - roots[-b/a] = k - - return roots -
    -
    [docs] def nth_power_roots_poly(f, n): - """ - Construct a polynomial with n-th powers of roots of ``f``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> f = Poly(x**4 - x**2 + 1) - - >>> f.nth_power_roots_poly(2) - Poly(x**4 - 2*x**3 + 3*x**2 - 2*x + 1, x, domain='ZZ') - >>> f.nth_power_roots_poly(3) - Poly(x**4 + 2*x**2 + 1, x, domain='ZZ') - >>> f.nth_power_roots_poly(4) - Poly(x**4 + 2*x**3 + 3*x**2 + 2*x + 1, x, domain='ZZ') - >>> f.nth_power_roots_poly(12) - Poly(x**4 - 4*x**3 + 6*x**2 - 4*x + 1, x, domain='ZZ') - - """ - if f.is_multivariate: - raise MultivariatePolynomialError( - "must be a univariate polynomial") - - N = sympify(n) - - if N.is_Integer and N >= 1: - n = int(N) - else: - raise ValueError("'n' must an integer and n >= 1, got %s" % n) - - x = f.gen - t = Dummy('t') - - r = f.resultant(f.__class__.from_expr(x**n - t, x, t)) - - return r.replace(t, x) -
    -
    [docs] def cancel(f, g, include=False): - """ - Cancel common factors in a rational function ``f/g``. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(2*x**2 - 2, x).cancel(Poly(x**2 - 2*x + 1, x)) - (1, Poly(2*x + 2, x, domain='ZZ'), Poly(x - 1, x, domain='ZZ')) - - >>> Poly(2*x**2 - 2, x).cancel(Poly(x**2 - 2*x + 1, x), include=True) - (Poly(2*x + 2, x, domain='ZZ'), Poly(x - 1, x, domain='ZZ')) - - """ - dom, per, F, G = f._unify(g) - - if hasattr(F, 'cancel'): - result = F.cancel(G, include=include) - else: # pragma: no cover - raise OperationNotSupported(f, 'cancel') - - if not include: - if dom.has_assoc_Ring: - dom = dom.get_ring() - - cp, cq, p, q = result - - cp = dom.to_sympy(cp) - cq = dom.to_sympy(cq) - - return cp/cq, per(p), per(q) - else: - return tuple(map(per, result)) -
    - @property -
    [docs] def is_zero(f): - """ - Returns ``True`` if ``f`` is a zero polynomial. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(0, x).is_zero - True - >>> Poly(1, x).is_zero - False - - """ - return f.rep.is_zero -
    - @property -
    [docs] def is_one(f): - """ - Returns ``True`` if ``f`` is a unit polynomial. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(0, x).is_one - False - >>> Poly(1, x).is_one - True - - """ - return f.rep.is_one -
    - @property -
    [docs] def is_sqf(f): - """ - Returns ``True`` if ``f`` is a square-free polynomial. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 - 2*x + 1, x).is_sqf - False - >>> Poly(x**2 - 1, x).is_sqf - True - - """ - return f.rep.is_sqf -
    - @property -
    [docs] def is_monic(f): - """ - Returns ``True`` if the leading coefficient of ``f`` is one. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x + 2, x).is_monic - True - >>> Poly(2*x + 2, x).is_monic - False - - """ - return f.rep.is_monic -
    - @property -
    [docs] def is_primitive(f): - """ - Returns ``True`` if GCD of the coefficients of ``f`` is one. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(2*x**2 + 6*x + 12, x).is_primitive - False - >>> Poly(x**2 + 3*x + 6, x).is_primitive - True - - """ - return f.rep.is_primitive -
    - @property -
    [docs] def is_ground(f): - """ - Returns ``True`` if ``f`` is an element of the ground domain. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> Poly(x, x).is_ground - False - >>> Poly(2, x).is_ground - True - >>> Poly(y, x).is_ground - True - - """ - return f.rep.is_ground -
    - @property -
    [docs] def is_linear(f): - """ - Returns ``True`` if ``f`` is linear in all its variables. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> Poly(x + y + 2, x, y).is_linear - True - >>> Poly(x*y + 2, x, y).is_linear - False - - """ - return f.rep.is_linear -
    - @property -
    [docs] def is_quadratic(f): - """ - Returns ``True`` if ``f`` is quadratic in all its variables. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> Poly(x*y + 2, x, y).is_quadratic - True - >>> Poly(x*y**2 + 2, x, y).is_quadratic - False - - """ - return f.rep.is_quadratic -
    - @property -
    [docs] def is_monomial(f): - """ - Returns ``True`` if ``f`` is zero or has only one term. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(3*x**2, x).is_monomial - True - >>> Poly(3*x**2 + 1, x).is_monomial - False - - """ - return f.rep.is_monomial -
    - @property -
    [docs] def is_homogeneous(f): - """ - Returns ``True`` if ``f`` is a homogeneous polynomial. - - A homogeneous polynomial is a polynomial whose all monomials with - non-zero coefficients have the same total degree. If you want not - only to check if a polynomial is homogeneous but also compute its - homogeneous order, then use :func:`Poly.homogeneous_order`. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> Poly(x**2 + x*y, x, y).is_homogeneous - True - >>> Poly(x**3 + x*y, x, y).is_homogeneous - False - - """ - return f.rep.is_homogeneous -
    - @property -
    [docs] def is_irreducible(f): - """ - Returns ``True`` if ``f`` has no factors over its domain. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> Poly(x**2 + x + 1, x, modulus=2).is_irreducible - True - >>> Poly(x**2 + 1, x, modulus=2).is_irreducible - False - - """ - return f.rep.is_irreducible -
    - @property -
    [docs] def is_univariate(f): - """ - Returns ``True`` if ``f`` is a univariate polynomial. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> Poly(x**2 + x + 1, x).is_univariate - True - >>> Poly(x*y**2 + x*y + 1, x, y).is_univariate - False - >>> Poly(x*y**2 + x*y + 1, x).is_univariate - True - >>> Poly(x**2 + x + 1, x, y).is_univariate - False - - """ - return len(f.gens) == 1 -
    - @property -
    [docs] def is_multivariate(f): - """ - Returns ``True`` if ``f`` is a multivariate polynomial. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x, y - - >>> Poly(x**2 + x + 1, x).is_multivariate - False - >>> Poly(x*y**2 + x*y + 1, x, y).is_multivariate - True - >>> Poly(x*y**2 + x*y + 1, x).is_multivariate - False - >>> Poly(x**2 + x + 1, x, y).is_multivariate - True - - """ - return len(f.gens) != 1 -
    - @property -
    [docs] def is_cyclotomic(f): - """ - Returns ``True`` if ``f`` is a cyclotomic polnomial. - - Examples - ======== - - >>> from sympy import Poly - >>> from sympy.abc import x - - >>> f = x**16 + x**14 - x**10 + x**8 - x**6 + x**2 + 1 - - >>> Poly(f).is_cyclotomic - False - - >>> g = x**16 + x**14 - x**10 - x**8 - x**6 + x**2 + 1 - - >>> Poly(g).is_cyclotomic - True - - """ - return f.rep.is_cyclotomic -
    - def __abs__(f): - return f.abs() - - def __neg__(f): - return f.neg() - - @_sympifyit('g', NotImplemented) - def __add__(f, g): - if not g.is_Poly: - try: - g = f.__class__(g, *f.gens) - except PolynomialError: - return f.as_expr() + g - - return f.add(g) - - @_sympifyit('g', NotImplemented) - def __radd__(f, g): - if not g.is_Poly: - try: - g = f.__class__(g, *f.gens) - except PolynomialError: - return g + f.as_expr() - - return g.add(f) - - @_sympifyit('g', NotImplemented) - def __sub__(f, g): - if not g.is_Poly: - try: - g = f.__class__(g, *f.gens) - except PolynomialError: - return f.as_expr() - g - - return f.sub(g) - - @_sympifyit('g', NotImplemented) - def __rsub__(f, g): - if not g.is_Poly: - try: - g = f.__class__(g, *f.gens) - except PolynomialError: - return g - f.as_expr() - - return g.sub(f) - - @_sympifyit('g', NotImplemented) - def __mul__(f, g): - if not g.is_Poly: - try: - g = f.__class__(g, *f.gens) - except PolynomialError: - return f.as_expr()*g - - return f.mul(g) - - @_sympifyit('g', NotImplemented) - def __rmul__(f, g): - if not g.is_Poly: - try: - g = f.__class__(g, *f.gens) - except PolynomialError: - return g*f.as_expr() - - return g.mul(f) - - @_sympifyit('n', NotImplemented) - def __pow__(f, n): - if n.is_Integer and n >= 0: - return f.pow(n) - else: - return f.as_expr()**n - - @_sympifyit('g', NotImplemented) - def __divmod__(f, g): - if not g.is_Poly: - g = f.__class__(g, *f.gens) - - return f.div(g) - - @_sympifyit('g', NotImplemented) - def __rdivmod__(f, g): - if not g.is_Poly: - g = f.__class__(g, *f.gens) - - return g.div(f) - - @_sympifyit('g', NotImplemented) - def __mod__(f, g): - if not g.is_Poly: - g = f.__class__(g, *f.gens) - - return f.rem(g) - - @_sympifyit('g', NotImplemented) - def __rmod__(f, g): - if not g.is_Poly: - g = f.__class__(g, *f.gens) - - return g.rem(f) - - @_sympifyit('g', NotImplemented) - def __floordiv__(f, g): - if not g.is_Poly: - g = f.__class__(g, *f.gens) - - return f.quo(g) - - @_sympifyit('g', NotImplemented) - def __rfloordiv__(f, g): - if not g.is_Poly: - g = f.__class__(g, *f.gens) - - return g.quo(f) - - @_sympifyit('g', NotImplemented) - def __div__(f, g): - return f.as_expr()/g.as_expr() - - @_sympifyit('g', NotImplemented) - def __rdiv__(f, g): - return g.as_expr()/f.as_expr() - - __truediv__ = __div__ - __rtruediv__ = __rdiv__ - - @_sympifyit('g', NotImplemented) - def __eq__(f, g): - if not g.is_Poly: - try: - g = f.__class__(g, f.gens, domain=f.get_domain()) - except (PolynomialError, DomainError, CoercionFailed): - return False - - if f.gens != g.gens: - return False - - if f.rep.dom != g.rep.dom: - try: - dom = f.rep.dom.unify(g.rep.dom, f.gens) - except UnificationFailed: - return False - - f = f.set_domain(dom) - g = g.set_domain(dom) - - return f.rep == g.rep - - @_sympifyit('g', NotImplemented) - def __ne__(f, g): - return not f.__eq__(g) - - def __bool__(f): - return not f.is_zero - - def eq(f, g, strict=False): - if not strict: - return f.__eq__(g) - else: - return f._strict_eq(sympify(g)) - - def ne(f, g, strict=False): - return not f.eq(g, strict=strict) - - def _strict_eq(f, g): - return isinstance(g, f.__class__) and f.gens == g.gens and f.rep.eq(g.rep, strict=True) - -
    -
    [docs]class PurePoly(Poly): - """Class for representing pure polynomials. """ - - def _hashable_content(self): - """Allow SymPy to hash Poly instances. """ - return (self.rep,) - - def __hash__(self): - return super(PurePoly, self).__hash__() - - @property -
    [docs] def free_symbols(self): - """ - Free symbols of a polynomial. - - Examples - ======== - - >>> from sympy import PurePoly - >>> from sympy.abc import x, y - - >>> PurePoly(x**2 + 1).free_symbols - set() - >>> PurePoly(x**2 + y).free_symbols - set() - >>> PurePoly(x**2 + y, x).free_symbols - set([y]) - - """ - return self.free_symbols_in_domain -
    - @_sympifyit('g', NotImplemented) - def __eq__(f, g): - if not g.is_Poly: - try: - g = f.__class__(g, f.gens, domain=f.get_domain()) - except (PolynomialError, DomainError, CoercionFailed): - return False - - if len(f.gens) != len(g.gens): - return False - - if f.rep.dom != g.rep.dom: - try: - dom = f.rep.dom.unify(g.rep.dom, f.gens) - except UnificationFailed: - return False - - f = f.set_domain(dom) - g = g.set_domain(dom) - - return f.rep == g.rep - - def _strict_eq(f, g): - return isinstance(g, f.__class__) and f.rep.eq(g.rep, strict=True) - - def _unify(f, g): - g = sympify(g) - - if not g.is_Poly: - try: - return f.rep.dom, f.per, f.rep, f.rep.per(f.rep.dom.from_sympy(g)) - except CoercionFailed: - raise UnificationFailed("can't unify %s with %s" % (f, g)) - - if len(f.gens) != len(g.gens): - raise UnificationFailed("can't unify %s with %s" % (f, g)) - - if not (isinstance(f.rep, DMP) and isinstance(g.rep, DMP)): - raise UnificationFailed("can't unify %s with %s" % (f, g)) - - cls = f.__class__ - gens = f.gens - - dom = f.rep.dom.unify(g.rep.dom, gens) - - F = f.rep.convert(dom) - G = g.rep.convert(dom) - - def per(rep, dom=dom, gens=gens, remove=None): - if remove is not None: - gens = gens[:remove] + gens[remove + 1:] - - if not gens: - return dom.to_sympy(rep) - - return cls.new(rep, *gens) - - return dom, per, F, G - -
    -
    [docs]def poly_from_expr(expr, *gens, **args): - """Construct a polynomial from an expression. """ - opt = options.build_options(gens, args) - return _poly_from_expr(expr, opt) - -
    -def _poly_from_expr(expr, opt): - """Construct a polynomial from an expression. """ - orig, expr = expr, sympify(expr) - - if not isinstance(expr, Basic): - raise PolificationFailed(opt, orig, expr) - elif expr.is_Poly: - poly = expr.__class__._from_poly(expr, opt) - - opt['gens'] = poly.gens - opt['domain'] = poly.domain - - if opt.polys is None: - opt['polys'] = True - - return poly, opt - elif opt.expand: - expr = expr.expand() - - try: - rep, opt = _dict_from_expr(expr, opt) - except GeneratorsNeeded: - raise PolificationFailed(opt, orig, expr) - - monoms, coeffs = list(zip(*list(rep.items()))) - domain = opt.domain - - if domain is None: - domain, coeffs = construct_domain(coeffs, opt=opt) - else: - coeffs = list(map(domain.from_sympy, coeffs)) - - level = len(opt.gens) - 1 - - poly = Poly.new( - DMP.from_monoms_coeffs(monoms, coeffs, level, domain), *opt.gens) - - opt['domain'] = domain - - if opt.polys is None: - opt['polys'] = False - - return poly, opt - - -
    [docs]def parallel_poly_from_expr(exprs, *gens, **args): - """Construct polynomials from expressions. """ - opt = options.build_options(gens, args) - return _parallel_poly_from_expr(exprs, opt) - -
    -def _parallel_poly_from_expr(exprs, opt): - """Construct polynomials from expressions. """ - if len(exprs) == 2: - f, g = exprs - - if isinstance(f, Poly) and isinstance(g, Poly): - f = f.__class__._from_poly(f, opt) - g = g.__class__._from_poly(g, opt) - - f, g = f.unify(g) - - opt['gens'] = f.gens - opt['domain'] = f.domain - - if opt.polys is None: - opt['polys'] = True - - return [f, g], opt - - origs, exprs = list(exprs), [] - _exprs, _polys = [], [] - - failed = False - - for i, expr in enumerate(origs): - expr = sympify(expr) - - if isinstance(expr, Basic): - if expr.is_Poly: - _polys.append(i) - else: - _exprs.append(i) - - if opt.expand: - expr = expr.expand() - else: - failed = True - - exprs.append(expr) - - if failed: - raise PolificationFailed(opt, origs, exprs, True) - - if _polys: - # XXX: this is a temporary solution - for i in _polys: - exprs[i] = exprs[i].as_expr() - - try: - reps, opt = _parallel_dict_from_expr(exprs, opt) - except GeneratorsNeeded: - raise PolificationFailed(opt, origs, exprs, True) - - coeffs_list, lengths = [], [] - - all_monoms = [] - all_coeffs = [] - - for rep in reps: - monoms, coeffs = list(zip(*list(rep.items()))) - - coeffs_list.extend(coeffs) - all_monoms.append(monoms) - - lengths.append(len(coeffs)) - - domain = opt.domain - - if domain is None: - domain, coeffs_list = construct_domain(coeffs_list, opt=opt) - else: - coeffs_list = list(map(domain.from_sympy, coeffs_list)) - - for k in lengths: - all_coeffs.append(coeffs_list[:k]) - coeffs_list = coeffs_list[k:] - - polys, level = [], len(opt.gens) - 1 - - for monoms, coeffs in zip(all_monoms, all_coeffs): - rep = DMP.from_monoms_coeffs(monoms, coeffs, level, domain) - polys.append(Poly.new(rep, *opt.gens)) - - opt['domain'] = domain - - if opt.polys is None: - opt['polys'] = bool(_polys) - - return polys, opt - - -def _update_args(args, key, value): - """Add a new ``(key, value)`` pair to arguments ``dict``. """ - args = dict(args) - - if key not in args: - args[key] = value - - return args - - -
    [docs]def degree(f, *gens, **args): - """ - Return the degree of ``f`` in the given variable. - - Examples - ======== - - >>> from sympy import degree - >>> from sympy.abc import x, y - - >>> degree(x**2 + y*x + 1, gen=x) - 2 - >>> degree(x**2 + y*x + 1, gen=y) - 1 - - """ - options.allowed_flags(args, ['gen', 'polys']) - - try: - F, opt = poly_from_expr(f, *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('degree', 1, exc) - - return Integer(F.degree(opt.gen)) - -
    -
    [docs]def degree_list(f, *gens, **args): - """ - Return a list of degrees of ``f`` in all variables. - - Examples - ======== - - >>> from sympy import degree_list - >>> from sympy.abc import x, y - - >>> degree_list(x**2 + y*x + 1) - (2, 1) - - """ - options.allowed_flags(args, ['polys']) - - try: - F, opt = poly_from_expr(f, *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('degree_list', 1, exc) - - degrees = F.degree_list() - - return tuple(map(Integer, degrees)) - -
    -
    [docs]def LC(f, *gens, **args): - """ - Return the leading coefficient of ``f``. - - Examples - ======== - - >>> from sympy import LC - >>> from sympy.abc import x, y - - >>> LC(4*x**2 + 2*x*y**2 + x*y + 3*y) - 4 - - """ - options.allowed_flags(args, ['polys']) - - try: - F, opt = poly_from_expr(f, *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('LC', 1, exc) - - return F.LC(order=opt.order) - -
    -
    [docs]def LM(f, *gens, **args): - """ - Return the leading monomial of ``f``. - - Examples - ======== - - >>> from sympy import LM - >>> from sympy.abc import x, y - - >>> LM(4*x**2 + 2*x*y**2 + x*y + 3*y) - x**2 - - """ - options.allowed_flags(args, ['polys']) - - try: - F, opt = poly_from_expr(f, *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('LM', 1, exc) - - monom = F.LM(order=opt.order) - return monom.as_expr() - -
    -
    [docs]def LT(f, *gens, **args): - """ - Return the leading term of ``f``. - - Examples - ======== - - >>> from sympy import LT - >>> from sympy.abc import x, y - - >>> LT(4*x**2 + 2*x*y**2 + x*y + 3*y) - 4*x**2 - - """ - options.allowed_flags(args, ['polys']) - - try: - F, opt = poly_from_expr(f, *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('LT', 1, exc) - - monom, coeff = F.LT(order=opt.order) - return coeff*monom.as_expr() - -
    -
    [docs]def pdiv(f, g, *gens, **args): - """ - Compute polynomial pseudo-division of ``f`` and ``g``. - - Examples - ======== - - >>> from sympy import pdiv - >>> from sympy.abc import x - - >>> pdiv(x**2 + 1, 2*x - 4) - (2*x + 4, 20) - - """ - options.allowed_flags(args, ['polys']) - - try: - (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('pdiv', 2, exc) - - q, r = F.pdiv(G) - - if not opt.polys: - return q.as_expr(), r.as_expr() - else: - return q, r - -
    -
    [docs]def prem(f, g, *gens, **args): - """ - Compute polynomial pseudo-remainder of ``f`` and ``g``. - - Examples - ======== - - >>> from sympy import prem - >>> from sympy.abc import x - - >>> prem(x**2 + 1, 2*x - 4) - 20 - - """ - options.allowed_flags(args, ['polys']) - - try: - (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('prem', 2, exc) - - r = F.prem(G) - - if not opt.polys: - return r.as_expr() - else: - return r - -
    -
    [docs]def pquo(f, g, *gens, **args): - """ - Compute polynomial pseudo-quotient of ``f`` and ``g``. - - Examples - ======== - - >>> from sympy import pquo - >>> from sympy.abc import x - - >>> pquo(x**2 + 1, 2*x - 4) - 2*x + 4 - >>> pquo(x**2 - 1, 2*x - 1) - 2*x + 1 - - """ - options.allowed_flags(args, ['polys']) - - try: - (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('pquo', 2, exc) - - try: - q = F.pquo(G) - except ExactQuotientFailed: - raise ExactQuotientFailed(f, g) - - if not opt.polys: - return q.as_expr() - else: - return q - -
    -
    [docs]def pexquo(f, g, *gens, **args): - """ - Compute polynomial exact pseudo-quotient of ``f`` and ``g``. - - Examples - ======== - - >>> from sympy import pexquo - >>> from sympy.abc import x - - >>> pexquo(x**2 - 1, 2*x - 2) - 2*x + 2 - - >>> pexquo(x**2 + 1, 2*x - 4) - Traceback (most recent call last): - ... - ExactQuotientFailed: 2*x - 4 does not divide x**2 + 1 - - """ - options.allowed_flags(args, ['polys']) - - try: - (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('pexquo', 2, exc) - - q = F.pexquo(G) - - if not opt.polys: - return q.as_expr() - else: - return q - -
    -
    [docs]def div(f, g, *gens, **args): - """ - Compute polynomial division of ``f`` and ``g``. - - Examples - ======== - - >>> from sympy import div, ZZ, QQ - >>> from sympy.abc import x - - >>> div(x**2 + 1, 2*x - 4, domain=ZZ) - (0, x**2 + 1) - >>> div(x**2 + 1, 2*x - 4, domain=QQ) - (x/2 + 1, 5) - - """ - options.allowed_flags(args, ['auto', 'polys']) - - try: - (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('div', 2, exc) - - q, r = F.div(G, auto=opt.auto) - - if not opt.polys: - return q.as_expr(), r.as_expr() - else: - return q, r - -
    -
    [docs]def rem(f, g, *gens, **args): - """ - Compute polynomial remainder of ``f`` and ``g``. - - Examples - ======== - - >>> from sympy import rem, ZZ, QQ - >>> from sympy.abc import x - - >>> rem(x**2 + 1, 2*x - 4, domain=ZZ) - x**2 + 1 - >>> rem(x**2 + 1, 2*x - 4, domain=QQ) - 5 - - """ - options.allowed_flags(args, ['auto', 'polys']) - - try: - (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('rem', 2, exc) - - r = F.rem(G, auto=opt.auto) - - if not opt.polys: - return r.as_expr() - else: - return r - -
    -
    [docs]def quo(f, g, *gens, **args): - """ - Compute polynomial quotient of ``f`` and ``g``. - - Examples - ======== - - >>> from sympy import quo - >>> from sympy.abc import x - - >>> quo(x**2 + 1, 2*x - 4) - x/2 + 1 - >>> quo(x**2 - 1, x - 1) - x + 1 - - """ - options.allowed_flags(args, ['auto', 'polys']) - - try: - (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('quo', 2, exc) - - q = F.quo(G, auto=opt.auto) - - if not opt.polys: - return q.as_expr() - else: - return q - -
    -
    [docs]def exquo(f, g, *gens, **args): - """ - Compute polynomial exact quotient of ``f`` and ``g``. - - Examples - ======== - - >>> from sympy import exquo - >>> from sympy.abc import x - - >>> exquo(x**2 - 1, x - 1) - x + 1 - - >>> exquo(x**2 + 1, 2*x - 4) - Traceback (most recent call last): - ... - ExactQuotientFailed: 2*x - 4 does not divide x**2 + 1 - - """ - options.allowed_flags(args, ['auto', 'polys']) - - try: - (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('exquo', 2, exc) - - q = F.exquo(G, auto=opt.auto) - - if not opt.polys: - return q.as_expr() - else: - return q - -
    -
    [docs]def half_gcdex(f, g, *gens, **args): - """ - Half extended Euclidean algorithm of ``f`` and ``g``. - - Returns ``(s, h)`` such that ``h = gcd(f, g)`` and ``s*f = h (mod g)``. - - Examples - ======== - - >>> from sympy import half_gcdex - >>> from sympy.abc import x - - >>> half_gcdex(x**4 - 2*x**3 - 6*x**2 + 12*x + 15, x**3 + x**2 - 4*x - 4) - (-x/5 + 3/5, x + 1) - - """ - options.allowed_flags(args, ['auto', 'polys']) - - try: - (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) - except PolificationFailed as exc: - domain, (a, b) = construct_domain(exc.exprs) - - try: - s, h = domain.half_gcdex(a, b) - except NotImplementedError: - raise ComputationFailed('half_gcdex', 2, exc) - else: - return domain.to_sympy(s), domain.to_sympy(h) - - s, h = F.half_gcdex(G, auto=opt.auto) - - if not opt.polys: - return s.as_expr(), h.as_expr() - else: - return s, h - -
    -
    [docs]def gcdex(f, g, *gens, **args): - """ - Extended Euclidean algorithm of ``f`` and ``g``. - - Returns ``(s, t, h)`` such that ``h = gcd(f, g)`` and ``s*f + t*g = h``. - - Examples - ======== - - >>> from sympy import gcdex - >>> from sympy.abc import x - - >>> gcdex(x**4 - 2*x**3 - 6*x**2 + 12*x + 15, x**3 + x**2 - 4*x - 4) - (-x/5 + 3/5, x**2/5 - 6*x/5 + 2, x + 1) - - """ - options.allowed_flags(args, ['auto', 'polys']) - - try: - (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) - except PolificationFailed as exc: - domain, (a, b) = construct_domain(exc.exprs) - - try: - s, t, h = domain.gcdex(a, b) - except NotImplementedError: - raise ComputationFailed('gcdex', 2, exc) - else: - return domain.to_sympy(s), domain.to_sympy(t), domain.to_sympy(h) - - s, t, h = F.gcdex(G, auto=opt.auto) - - if not opt.polys: - return s.as_expr(), t.as_expr(), h.as_expr() - else: - return s, t, h - -
    -
    [docs]def invert(f, g, *gens, **args): - """ - Invert ``f`` modulo ``g`` when possible. - - Examples - ======== - - >>> from sympy import invert - >>> from sympy.abc import x - - >>> invert(x**2 - 1, 2*x - 1) - -4/3 - - >>> invert(x**2 - 1, x - 1) - Traceback (most recent call last): - ... - NotInvertible: zero divisor - - """ - options.allowed_flags(args, ['auto', 'polys']) - - try: - (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) - except PolificationFailed as exc: - domain, (a, b) = construct_domain(exc.exprs) - - try: - return domain.to_sympy(domain.invert(a, b)) - except NotImplementedError: - raise ComputationFailed('invert', 2, exc) - - h = F.invert(G, auto=opt.auto) - - if not opt.polys: - return h.as_expr() - else: - return h - -
    -
    [docs]def subresultants(f, g, *gens, **args): - """ - Compute subresultant PRS of ``f`` and ``g``. - - Examples - ======== - - >>> from sympy import subresultants - >>> from sympy.abc import x - - >>> subresultants(x**2 + 1, x**2 - 1) - [x**2 + 1, x**2 - 1, -2] - - """ - options.allowed_flags(args, ['polys']) - - try: - (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('subresultants', 2, exc) - - result = F.subresultants(G) - - if not opt.polys: - return [ r.as_expr() for r in result ] - else: - return result - -
    -
    [docs]def resultant(f, g, *gens, **args): - """ - Compute resultant of ``f`` and ``g``. - - Examples - ======== - - >>> from sympy import resultant - >>> from sympy.abc import x - - >>> resultant(x**2 + 1, x**2 - 1) - 4 - - """ - includePRS = args.pop('includePRS', False) - options.allowed_flags(args, ['polys']) - - try: - (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('resultant', 2, exc) - - if includePRS: - result, R = F.resultant(G, includePRS=includePRS) - else: - result = F.resultant(G) - - if not opt.polys: - if includePRS: - return result.as_expr(), [r.as_expr() for r in R] - return result.as_expr() - else: - if includePRS: - return result, R - return result - -
    -
    [docs]def discriminant(f, *gens, **args): - """ - Compute discriminant of ``f``. - - Examples - ======== - - >>> from sympy import discriminant - >>> from sympy.abc import x - - >>> discriminant(x**2 + 2*x + 3) - -8 - - """ - options.allowed_flags(args, ['polys']) - - try: - F, opt = poly_from_expr(f, *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('discriminant', 1, exc) - - result = F.discriminant() - - if not opt.polys: - return result.as_expr() - else: - return result - -
    -
    [docs]def cofactors(f, g, *gens, **args): - """ - Compute GCD and cofactors of ``f`` and ``g``. - - Returns polynomials ``(h, cff, cfg)`` such that ``h = gcd(f, g)``, and - ``cff = quo(f, h)`` and ``cfg = quo(g, h)`` are, so called, cofactors - of ``f`` and ``g``. - - Examples - ======== - - >>> from sympy import cofactors - >>> from sympy.abc import x - - >>> cofactors(x**2 - 1, x**2 - 3*x + 2) - (x - 1, x + 1, x - 2) - - """ - options.allowed_flags(args, ['polys']) - - try: - (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) - except PolificationFailed as exc: - domain, (a, b) = construct_domain(exc.exprs) - - try: - h, cff, cfg = domain.cofactors(a, b) - except NotImplementedError: - raise ComputationFailed('cofactors', 2, exc) - else: - return domain.to_sympy(h), domain.to_sympy(cff), domain.to_sympy(cfg) - - h, cff, cfg = F.cofactors(G) - - if not opt.polys: - return h.as_expr(), cff.as_expr(), cfg.as_expr() - else: - return h, cff, cfg - -
    -
    [docs]def gcd_list(seq, *gens, **args): - """ - Compute GCD of a list of polynomials. - - Examples - ======== - - >>> from sympy import gcd_list - >>> from sympy.abc import x - - >>> gcd_list([x**3 - 1, x**2 - 1, x**2 - 3*x + 2]) - x - 1 - - """ - seq = sympify(seq) - - if not gens and not args: - domain, numbers = construct_domain(seq) - - if not numbers: - return domain.zero - elif domain.is_Numerical: - result, numbers = numbers[0], numbers[1:] - - for number in numbers: - result = domain.gcd(result, number) - - if domain.is_one(result): - break - - return domain.to_sympy(result) - - options.allowed_flags(args, ['polys']) - - try: - polys, opt = parallel_poly_from_expr(seq, *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('gcd_list', len(seq), exc) - - if not polys: - if not opt.polys: - return S.Zero - else: - return Poly(0, opt=opt) - - result, polys = polys[0], polys[1:] - - for poly in polys: - result = result.gcd(poly) - - if result.is_one: - break - - if not opt.polys: - return result.as_expr() - else: - return result - -
    -
    [docs]def gcd(f, g=None, *gens, **args): - """ - Compute GCD of ``f`` and ``g``. - - Examples - ======== - - >>> from sympy import gcd - >>> from sympy.abc import x - - >>> gcd(x**2 - 1, x**2 - 3*x + 2) - x - 1 - - """ - if hasattr(f, '__iter__'): - if g is not None: - gens = (g,) + gens - - return gcd_list(f, *gens, **args) - elif g is None: - raise TypeError("gcd() takes 2 arguments or a sequence of arguments") - - options.allowed_flags(args, ['polys']) - - try: - (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) - except PolificationFailed as exc: - domain, (a, b) = construct_domain(exc.exprs) - - try: - return domain.to_sympy(domain.gcd(a, b)) - except NotImplementedError: - raise ComputationFailed('gcd', 2, exc) - - result = F.gcd(G) - - if not opt.polys: - return result.as_expr() - else: - return result - -
    -
    [docs]def lcm_list(seq, *gens, **args): - """ - Compute LCM of a list of polynomials. - - Examples - ======== - - >>> from sympy import lcm_list - >>> from sympy.abc import x - - >>> lcm_list([x**3 - 1, x**2 - 1, x**2 - 3*x + 2]) - x**5 - x**4 - 2*x**3 - x**2 + x + 2 - - """ - seq = sympify(seq) - - if not gens and not args: - domain, numbers = construct_domain(seq) - - if not numbers: - return domain.one - elif domain.is_Numerical: - result, numbers = numbers[0], numbers[1:] - - for number in numbers: - result = domain.lcm(result, number) - - return domain.to_sympy(result) - - options.allowed_flags(args, ['polys']) - - try: - polys, opt = parallel_poly_from_expr(seq, *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('lcm_list', len(seq), exc) - - if not polys: - if not opt.polys: - return S.One - else: - return Poly(1, opt=opt) - - result, polys = polys[0], polys[1:] - - for poly in polys: - result = result.lcm(poly) - - if not opt.polys: - return result.as_expr() - else: - return result - -
    -
    [docs]def lcm(f, g=None, *gens, **args): - """ - Compute LCM of ``f`` and ``g``. - - Examples - ======== - - >>> from sympy import lcm - >>> from sympy.abc import x - - >>> lcm(x**2 - 1, x**2 - 3*x + 2) - x**3 - 2*x**2 - x + 2 - - """ - if hasattr(f, '__iter__'): - if g is not None: - gens = (g,) + gens - - return lcm_list(f, *gens, **args) - elif g is None: - raise TypeError("lcm() takes 2 arguments or a sequence of arguments") - - options.allowed_flags(args, ['polys']) - - try: - (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) - except PolificationFailed as exc: - domain, (a, b) = construct_domain(exc.exprs) - - try: - return domain.to_sympy(domain.lcm(a, b)) - except NotImplementedError: - raise ComputationFailed('lcm', 2, exc) - - result = F.lcm(G) - - if not opt.polys: - return result.as_expr() - else: - return result - -
    -
    [docs]def terms_gcd(f, *gens, **args): - """ - Remove GCD of terms from ``f``. - - If the ``deep`` flag is True, then the arguments of ``f`` will have - terms_gcd applied to them. - - If a fraction is factored out of ``f`` and ``f`` is an Add, then - an unevaluated Mul will be returned so that automatic simplification - does not redistribute it. The hint ``clear``, when set to False, can be - used to prevent such factoring when all coefficients are not fractions. - - Examples - ======== - - >>> from sympy import terms_gcd, cos, pi - >>> from sympy.abc import x, y - >>> terms_gcd(x**6*y**2 + x**3*y, x, y) - x**3*y*(x**3*y + 1) - - The default action of polys routines is to expand the expression - given to them. terms_gcd follows this behavior: - - >>> terms_gcd((3+3*x)*(x+x*y)) - 3*x*(x*y + x + y + 1) - - If this is not desired then the hint ``expand`` can be set to False. - In this case the expression will be treated as though it were comprised - of one or more terms: - - >>> terms_gcd((3+3*x)*(x+x*y), expand=False) - (3*x + 3)*(x*y + x) - - In order to traverse factors of a Mul or the arguments of other - functions, the ``deep`` hint can be used: - - >>> terms_gcd((3 + 3*x)*(x + x*y), expand=False, deep=True) - 3*x*(x + 1)*(y + 1) - >>> terms_gcd(cos(x + x*y), deep=True) - cos(x*(y + 1)) - - Rationals are factored out by default: - - >>> terms_gcd(x + y/2) - (2*x + y)/2 - - Only the y-term had a coefficient that was a fraction; if one - does not want to factor out the 1/2 in cases like this, the - flag ``clear`` can be set to False: - - >>> terms_gcd(x + y/2, clear=False) - x + y/2 - >>> terms_gcd(x*y/2 + y**2, clear=False) - y*(x/2 + y) - - The ``clear`` flag is ignored if all coefficients are fractions: - - >>> terms_gcd(x/3 + y/2, clear=False) - (2*x + 3*y)/6 - - See Also - ======== - sympy.core.exprtools.gcd_terms, sympy.core.exprtools.factor_terms - - """ - if not isinstance(f, Expr) or f.is_Atom: - return sympify(f) - - if args.get('deep', False): - new = f.func(*[terms_gcd(a, *gens, **args) for a in f.args]) - args.pop('deep') - args['expand'] = False - return terms_gcd(new, *gens, **args) - - clear = args.pop('clear', True) - options.allowed_flags(args, ['polys']) - - try: - F, opt = poly_from_expr(f, *gens, **args) - except PolificationFailed as exc: - return exc.expr - - J, f = F.terms_gcd() - - if opt.domain.has_Ring: - if opt.domain.has_Field: - denom, f = f.clear_denoms(convert=True) - - coeff, f = f.primitive() - - if opt.domain.has_Field: - coeff /= denom - else: - coeff = S.One - - term = Mul(*[ x**j for x, j in zip(f.gens, J) ]) - - if clear: - return _keep_coeff(coeff, term*f.as_expr()) - # base the clearing on the form of the original expression, not - # the (perhaps) Mul that we have now - coeff, f = _keep_coeff(coeff, f.as_expr(), clear=False).as_coeff_Mul() - return _keep_coeff(coeff, term*f, clear=False) - -
    -
    [docs]def trunc(f, p, *gens, **args): - """ - Reduce ``f`` modulo a constant ``p``. - - Examples - ======== - - >>> from sympy import trunc - >>> from sympy.abc import x - - >>> trunc(2*x**3 + 3*x**2 + 5*x + 7, 3) - -x**3 - x + 1 - - """ - options.allowed_flags(args, ['auto', 'polys']) - - try: - F, opt = poly_from_expr(f, *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('trunc', 1, exc) - - result = F.trunc(sympify(p)) - - if not opt.polys: - return result.as_expr() - else: - return result - -
    -
    [docs]def monic(f, *gens, **args): - """ - Divide all coefficients of ``f`` by ``LC(f)``. - - Examples - ======== - - >>> from sympy import monic - >>> from sympy.abc import x - - >>> monic(3*x**2 + 4*x + 2) - x**2 + 4*x/3 + 2/3 - - """ - options.allowed_flags(args, ['auto', 'polys']) - - try: - F, opt = poly_from_expr(f, *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('monic', 1, exc) - - result = F.monic(auto=opt.auto) - - if not opt.polys: - return result.as_expr() - else: - return result - -
    -
    [docs]def content(f, *gens, **args): - """ - Compute GCD of coefficients of ``f``. - - Examples - ======== - - >>> from sympy import content - >>> from sympy.abc import x - - >>> content(6*x**2 + 8*x + 12) - 2 - - """ - options.allowed_flags(args, ['polys']) - - try: - F, opt = poly_from_expr(f, *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('content', 1, exc) - - return F.content() - -
    -
    [docs]def primitive(f, *gens, **args): - """ - Compute content and the primitive form of ``f``. - - Examples - ======== - - >>> from sympy.polys.polytools import primitive - >>> from sympy.abc import x, y - - >>> primitive(6*x**2 + 8*x + 12) - (2, 3*x**2 + 4*x + 6) - - >>> eq = (2 + 2*x)*x + 2 - - Expansion is performed by default: - - >>> primitive(eq) - (2, x**2 + x + 1) - - Set ``expand`` to False to shut this off. Note that the - extraction will not be recursive; use the as_content_primitive method - for recursive, non-destructive Rational extraction. - - >>> primitive(eq, expand=False) - (1, x*(2*x + 2) + 2) - - >>> eq.as_content_primitive() - (2, x*(x + 1) + 1) - - """ - options.allowed_flags(args, ['polys']) - - try: - F, opt = poly_from_expr(f, *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('primitive', 1, exc) - - cont, result = F.primitive() - if not opt.polys: - return cont, result.as_expr() - else: - return cont, result - -
    -
    [docs]def compose(f, g, *gens, **args): - """ - Compute functional composition ``f(g)``. - - Examples - ======== - - >>> from sympy import compose - >>> from sympy.abc import x - - >>> compose(x**2 + x, x - 1) - x**2 - x - - """ - options.allowed_flags(args, ['polys']) - - try: - (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('compose', 2, exc) - - result = F.compose(G) - - if not opt.polys: - return result.as_expr() - else: - return result - -
    -
    [docs]def decompose(f, *gens, **args): - """ - Compute functional decomposition of ``f``. - - Examples - ======== - - >>> from sympy import decompose - >>> from sympy.abc import x - - >>> decompose(x**4 + 2*x**3 - x - 1) - [x**2 - x - 1, x**2 + x] - - """ - options.allowed_flags(args, ['polys']) - - try: - F, opt = poly_from_expr(f, *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('decompose', 1, exc) - - result = F.decompose() - - if not opt.polys: - return [ r.as_expr() for r in result ] - else: - return result - -
    -
    [docs]def sturm(f, *gens, **args): - """ - Compute Sturm sequence of ``f``. - - Examples - ======== - - >>> from sympy import sturm - >>> from sympy.abc import x - - >>> sturm(x**3 - 2*x**2 + x - 3) - [x**3 - 2*x**2 + x - 3, 3*x**2 - 4*x + 1, 2*x/9 + 25/9, -2079/4] - - """ - options.allowed_flags(args, ['auto', 'polys']) - - try: - F, opt = poly_from_expr(f, *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('sturm', 1, exc) - - result = F.sturm(auto=opt.auto) - - if not opt.polys: - return [ r.as_expr() for r in result ] - else: - return result - -
    -
    [docs]def gff_list(f, *gens, **args): - """ - Compute a list of greatest factorial factors of ``f``. - - Examples - ======== - - >>> from sympy import gff_list, ff - >>> from sympy.abc import x - - >>> f = x**5 + 2*x**4 - x**3 - 2*x**2 - - >>> gff_list(f) - [(x, 1), (x + 2, 4)] - - >>> (ff(x, 1)*ff(x + 2, 4)).expand() == f - True - - """ - options.allowed_flags(args, ['polys']) - - try: - F, opt = poly_from_expr(f, *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('gff_list', 1, exc) - - factors = F.gff_list() - - if not opt.polys: - return [ (g.as_expr(), k) for g, k in factors ] - else: - return factors - -
    -
    [docs]def gff(f, *gens, **args): - """Compute greatest factorial factorization of ``f``. """ - raise NotImplementedError('symbolic falling factorial') - -
    -
    [docs]def sqf_norm(f, *gens, **args): - """ - Compute square-free norm of ``f``. - - Returns ``s``, ``f``, ``r``, such that ``g(x) = f(x-sa)`` and - ``r(x) = Norm(g(x))`` is a square-free polynomial over ``K``, - where ``a`` is the algebraic extension of the ground domain. - - Examples - ======== - - >>> from sympy import sqf_norm, sqrt - >>> from sympy.abc import x - - >>> sqf_norm(x**2 + 1, extension=[sqrt(3)]) - (1, x**2 - 2*sqrt(3)*x + 4, x**4 - 4*x**2 + 16) - - """ - options.allowed_flags(args, ['polys']) - - try: - F, opt = poly_from_expr(f, *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('sqf_norm', 1, exc) - - s, g, r = F.sqf_norm() - - if not opt.polys: - return Integer(s), g.as_expr(), r.as_expr() - else: - return Integer(s), g, r - -
    -
    [docs]def sqf_part(f, *gens, **args): - """ - Compute square-free part of ``f``. - - Examples - ======== - - >>> from sympy import sqf_part - >>> from sympy.abc import x - - >>> sqf_part(x**3 - 3*x - 2) - x**2 - x - 2 - - """ - options.allowed_flags(args, ['polys']) - - try: - F, opt = poly_from_expr(f, *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('sqf_part', 1, exc) - - result = F.sqf_part() - - if not opt.polys: - return result.as_expr() - else: - return result - -
    -def _sorted_factors(factors, method): - """Sort a list of ``(expr, exp)`` pairs. """ - if method == 'sqf': - def key(obj): - poly, exp = obj - rep = poly.rep.rep - return (exp, len(rep), rep) - else: - def key(obj): - poly, exp = obj - rep = poly.rep.rep - return (len(rep), exp, rep) - - return sorted(factors, key=key) - - -def _factors_product(factors): - """Multiply a list of ``(expr, exp)`` pairs. """ - return Mul(*[ f.as_expr()**k for f, k in factors ]) - - -def _symbolic_factor_list(expr, opt, method): - """Helper function for :func:`_symbolic_factor`. """ - coeff, factors = S.One, [] - - for arg in Mul.make_args(expr): - if arg.is_Number: - coeff *= arg - continue - elif arg.is_Pow: - base, exp = arg.args - if base.is_Number: - factors.append((base, exp)) - continue - else: - base, exp = arg, S.One - - try: - poly, _ = _poly_from_expr(base, opt) - except PolificationFailed as exc: - factors.append((exc.expr, exp)) - else: - func = getattr(poly, method + '_list') - - _coeff, _factors = func() - if _coeff is not S.One: - if exp.is_Integer: - coeff *= _coeff**exp - elif _coeff.is_positive: - factors.append((_coeff, exp)) - else: - _factors.append((_coeff, None)) - - if exp is S.One: - factors.extend(_factors) - elif exp.is_integer or len(_factors) == 1: - factors.extend([ (f, k*exp) for f, k in _factors ]) - else: - other = [] - - for f, k in _factors: - if f.as_expr().is_positive: - factors.append((f, k*exp)) - elif k is not None: - other.append((f, k)) - else: - other.append((f, S.One)) - - if len(other) == 1: - f, k = other[0] - factors.append((f, k*exp)) - else: - factors.append((_factors_product(other), exp)) - - return coeff, factors - - -def _symbolic_factor(expr, opt, method): - """Helper function for :func:`_factor`. """ - if isinstance(expr, Expr) and not expr.is_Relational: - coeff, factors = _symbolic_factor_list(together(expr), opt, method) - return _keep_coeff(coeff, _factors_product(factors)) - elif hasattr(expr, 'args'): - return expr.func(*[ _symbolic_factor(arg, opt, method) for arg in expr.args ]) - elif hasattr(expr, '__iter__'): - return expr.__class__([ _symbolic_factor(arg, opt, method) for arg in expr ]) - else: - return expr - - -def _generic_factor_list(expr, gens, args, method): - """Helper function for :func:`sqf_list` and :func:`factor_list`. """ - options.allowed_flags(args, ['frac', 'polys']) - opt = options.build_options(gens, args) - - expr = sympify(expr) - - if isinstance(expr, Expr) and not expr.is_Relational: - numer, denom = together(expr).as_numer_denom() - - cp, fp = _symbolic_factor_list(numer, opt, method) - cq, fq = _symbolic_factor_list(denom, opt, method) - - if fq and not opt.frac: - raise PolynomialError("a polynomial expected, got %s" % expr) - - _opt = opt.clone(dict(expand=True)) - - for factors in (fp, fq): - for i, (f, k) in enumerate(factors): - if not f.is_Poly: - f, _ = _poly_from_expr(f, _opt) - factors[i] = (f, k) - - fp = _sorted_factors(fp, method) - fq = _sorted_factors(fq, method) - - if not opt.polys: - fp = [ (f.as_expr(), k) for f, k in fp ] - fq = [ (f.as_expr(), k) for f, k in fq ] - - coeff = cp/cq - - if not opt.frac: - return coeff, fp - else: - return coeff, fp, fq - else: - raise PolynomialError("a polynomial expected, got %s" % expr) - - -def _generic_factor(expr, gens, args, method): - """Helper function for :func:`sqf` and :func:`factor`. """ - options.allowed_flags(args, []) - opt = options.build_options(gens, args) - return _symbolic_factor(sympify(expr), opt, method) - - -
    [docs]def sqf_list(f, *gens, **args): - """ - Compute a list of square-free factors of ``f``. - - Examples - ======== - - >>> from sympy import sqf_list - >>> from sympy.abc import x - - >>> sqf_list(2*x**5 + 16*x**4 + 50*x**3 + 76*x**2 + 56*x + 16) - (2, [(x + 1, 2), (x + 2, 3)]) - - """ - return _generic_factor_list(f, gens, args, method='sqf') - -
    -
    [docs]def sqf(f, *gens, **args): - """ - Compute square-free factorization of ``f``. - - Examples - ======== - - >>> from sympy import sqf - >>> from sympy.abc import x - - >>> sqf(2*x**5 + 16*x**4 + 50*x**3 + 76*x**2 + 56*x + 16) - 2*(x + 1)**2*(x + 2)**3 - - """ - return _generic_factor(f, gens, args, method='sqf') - -
    -
    [docs]def factor_list(f, *gens, **args): - """ - Compute a list of irreducible factors of ``f``. - - Examples - ======== - - >>> from sympy import factor_list - >>> from sympy.abc import x, y - - >>> factor_list(2*x**5 + 2*x**4*y + 4*x**3 + 4*x**2*y + 2*x + 2*y) - (2, [(x + y, 1), (x**2 + 1, 2)]) - - """ - return _generic_factor_list(f, gens, args, method='factor') - -
    -
    [docs]def factor(f, *gens, **args): - """ - Compute the factorization of ``f`` into irreducibles. (Use factorint to - factor an integer.) - - There two modes implemented: symbolic and formal. If ``f`` is not an - instance of :class:`Poly` and generators are not specified, then the - former mode is used. Otherwise, the formal mode is used. - - In symbolic mode, :func:`factor` will traverse the expression tree and - factor its components without any prior expansion, unless an instance - of :class:`Add` is encountered (in this case formal factorization is - used). This way :func:`factor` can handle large or symbolic exponents. - - By default, the factorization is computed over the rationals. To factor - over other domain, e.g. an algebraic or finite field, use appropriate - options: ``extension``, ``modulus`` or ``domain``. - - Examples - ======== - - >>> from sympy import factor, sqrt - >>> from sympy.abc import x, y - - >>> factor(2*x**5 + 2*x**4*y + 4*x**3 + 4*x**2*y + 2*x + 2*y) - 2*(x + y)*(x**2 + 1)**2 - - >>> factor(x**2 + 1) - x**2 + 1 - >>> factor(x**2 + 1, modulus=2) - (x + 1)**2 - >>> factor(x**2 + 1, gaussian=True) - (x - I)*(x + I) - - >>> factor(x**2 - 2, extension=sqrt(2)) - (x - sqrt(2))*(x + sqrt(2)) - - >>> factor((x**2 - 1)/(x**2 + 4*x + 4)) - (x - 1)*(x + 1)/(x + 2)**2 - >>> factor((x**2 + 4*x + 4)**10000000*(x**2 + 1)) - (x + 2)**20000000*(x**2 + 1) - - """ - try: - return _generic_factor(f, gens, args, method='factor') - except PolynomialError as msg: - if not f.is_commutative: - from sympy.core.exprtools import factor_nc - return factor_nc(f) - else: - raise PolynomialError(msg) - -
    -
    [docs]def intervals(F, all=False, eps=None, inf=None, sup=None, strict=False, fast=False, sqf=False): - """ - Compute isolating intervals for roots of ``f``. - - Examples - ======== - - >>> from sympy import intervals - >>> from sympy.abc import x - - >>> intervals(x**2 - 3) - [((-2, -1), 1), ((1, 2), 1)] - >>> intervals(x**2 - 3, eps=1e-2) - [((-26/15, -19/11), 1), ((19/11, 26/15), 1)] - - """ - if not hasattr(F, '__iter__'): - try: - F = Poly(F) - except GeneratorsNeeded: - return [] - - return F.intervals(all=all, eps=eps, inf=inf, sup=sup, fast=fast, sqf=sqf) - else: - polys, opt = parallel_poly_from_expr(F, domain='QQ') - - if len(opt.gens) > 1: - raise MultivariatePolynomialError - - for i, poly in enumerate(polys): - polys[i] = poly.rep.rep - - if eps is not None: - eps = opt.domain.convert(eps) - - if eps <= 0: - raise ValueError("'eps' must be a positive rational") - - if inf is not None: - inf = opt.domain.convert(inf) - if sup is not None: - sup = opt.domain.convert(sup) - - intervals = dup_isolate_real_roots_list(polys, opt.domain, - eps=eps, inf=inf, sup=sup, strict=strict, fast=fast) - - result = [] - - for (s, t), indices in intervals: - s, t = opt.domain.to_sympy(s), opt.domain.to_sympy(t) - result.append(((s, t), indices)) - - return result - -
    -
    [docs]def refine_root(f, s, t, eps=None, steps=None, fast=False, check_sqf=False): - """ - Refine an isolating interval of a root to the given precision. - - Examples - ======== - - >>> from sympy import refine_root - >>> from sympy.abc import x - - >>> refine_root(x**2 - 3, 1, 2, eps=1e-2) - (19/11, 26/15) - - """ - try: - F = Poly(f) - except GeneratorsNeeded: - raise PolynomialError( - "can't refine a root of %s, not a polynomial" % f) - - return F.refine_root(s, t, eps=eps, steps=steps, fast=fast, check_sqf=check_sqf) - -
    -
    [docs]def count_roots(f, inf=None, sup=None): - """ - Return the number of roots of ``f`` in ``[inf, sup]`` interval. - - If one of ``inf`` or ``sup`` is complex, it will return the number of roots - in the complex rectangle with corners at ``inf`` and ``sup``. - - Examples - ======== - - >>> from sympy import count_roots, I - >>> from sympy.abc import x - - >>> count_roots(x**4 - 4, -3, 3) - 2 - >>> count_roots(x**4 - 4, 0, 1 + 3*I) - 1 - - """ - try: - F = Poly(f, greedy=False) - except GeneratorsNeeded: - raise PolynomialError("can't count roots of %s, not a polynomial" % f) - - return F.count_roots(inf=inf, sup=sup) - -
    -
    [docs]def real_roots(f, multiple=True): - """ - Return a list of real roots with multiplicities of ``f``. - - Examples - ======== - - >>> from sympy import real_roots - >>> from sympy.abc import x - - >>> real_roots(2*x**3 - 7*x**2 + 4*x + 4) - [-1/2, 2, 2] - - """ - try: - F = Poly(f, greedy=False) - except GeneratorsNeeded: - raise PolynomialError( - "can't compute real roots of %s, not a polynomial" % f) - - return F.real_roots(multiple=multiple) - -
    -
    [docs]def nroots(f, n=15, maxsteps=50, cleanup=True, error=False): - """ - Compute numerical approximations of roots of ``f``. - - Examples - ======== - - >>> from sympy import nroots - >>> from sympy.abc import x - - >>> nroots(x**2 - 3, n=15) - [-1.73205080756888, 1.73205080756888] - >>> nroots(x**2 - 3, n=30) - [-1.73205080756887729352744634151, 1.73205080756887729352744634151] - - """ - try: - F = Poly(f, greedy=False) - except GeneratorsNeeded: - raise PolynomialError( - "can't compute numerical roots of %s, not a polynomial" % f) - - return F.nroots(n=n, maxsteps=maxsteps, cleanup=cleanup, error=error) - -
    -
    [docs]def ground_roots(f, *gens, **args): - """ - Compute roots of ``f`` by factorization in the ground domain. - - Examples - ======== - - >>> from sympy import ground_roots - >>> from sympy.abc import x - - >>> ground_roots(x**6 - 4*x**4 + 4*x**3 - x**2) - {0: 2, 1: 2} - - """ - options.allowed_flags(args, []) - - try: - F, opt = poly_from_expr(f, *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('ground_roots', 1, exc) - - return F.ground_roots() - -
    -
    [docs]def nth_power_roots_poly(f, n, *gens, **args): - """ - Construct a polynomial with n-th powers of roots of ``f``. - - Examples - ======== - - >>> from sympy import nth_power_roots_poly, factor, roots - >>> from sympy.abc import x - - >>> f = x**4 - x**2 + 1 - >>> g = factor(nth_power_roots_poly(f, 2)) - - >>> g - (x**2 - x + 1)**2 - - >>> R_f = [ (r**2).expand() for r in roots(f) ] - >>> R_g = list(roots(g).keys()) - - >>> set(R_f) == set(R_g) - True - - """ - options.allowed_flags(args, []) - - try: - F, opt = poly_from_expr(f, *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('nth_power_roots_poly', 1, exc) - - result = F.nth_power_roots_poly(n) - - if not opt.polys: - return result.as_expr() - else: - return result - -
    -
    [docs]def cancel(f, *gens, **args): - """ - Cancel common factors in a rational function ``f``. - - Examples - ======== - - >>> from sympy import cancel - >>> from sympy.abc import x - - >>> cancel((2*x**2 - 2)/(x**2 - 2*x + 1)) - (2*x + 2)/(x - 1) - - """ - options.allowed_flags(args, ['polys']) - - f = sympify(f) - - if not isinstance(f, (tuple, Tuple)): - if f.is_Number: - return f - else: - p, q = f.as_numer_denom() - - else: - p, q = f - - try: - (F, G), opt = parallel_poly_from_expr((p, q), *gens, **args) - except PolificationFailed: - if not isinstance(f, (tuple, Tuple)): - return f - else: - return S.One, p, q - - c, P, Q = F.cancel(G) - - if not isinstance(f, (tuple, Tuple)): - return c*(P.as_expr()/Q.as_expr()) - else: - if not opt.polys: - return c, P.as_expr(), Q.as_expr() - else: - return c, P, Q - -
    -
    [docs]def reduced(f, G, *gens, **args): - """ - Reduces a polynomial ``f`` modulo a set of polynomials ``G``. - - Given a polynomial ``f`` and a set of polynomials ``G = (g_1, ..., g_n)``, - computes a set of quotients ``q = (q_1, ..., q_n)`` and the remainder ``r`` - such that ``f = q_1*f_1 + ... + q_n*f_n + r``, where ``r`` vanishes or ``r`` - is a completely reduced polynomial with respect to ``G``. - - Examples - ======== - - >>> from sympy import reduced - >>> from sympy.abc import x, y - - >>> reduced(2*x**4 + y**2 - x**2 + y**3, [x**3 - x, y**3 - y]) - ([2*x, 1], x**2 + y**2 + y) - - """ - options.allowed_flags(args, ['polys', 'auto']) - - try: - polys, opt = parallel_poly_from_expr([f] + list(G), *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('reduced', 0, exc) - - domain = opt.domain - retract = False - - if opt.auto and domain.has_Ring and not domain.has_Field: - opt = opt.clone(dict(domain=domain.get_field())) - retract = True - - for i, poly in enumerate(polys): - poly = poly.set_domain(opt.domain).rep.to_dict() - polys[i] = sdp_from_dict(poly, opt.order) - - level = len(opt.gens) - 1 - - Q, r = sdp_div(polys[0], polys[1:], level, opt.order, opt.domain) - - Q = [ Poly._from_dict(dict(q), opt) for q in Q ] - r = Poly._from_dict(dict(r), opt) - - if retract: - try: - _Q, _r = [ q.to_ring() for q in Q ], r.to_ring() - except CoercionFailed: - pass - else: - Q, r = _Q, _r - - if not opt.polys: - return [ q.as_expr() for q in Q ], r.as_expr() - else: - return Q, r - -
    -
    [docs]def groebner(F, *gens, **args): - """ - Computes the reduced Groebner basis for a set of polynomials. - - Use the ``order`` argument to set the monomial ordering that will be - used to compute the basis. Allowed orders are ``lex``, ``grlex`` and - ``grevlex``. If no order is specified, it defaults to ``lex``. - - For more information on Groebner bases, see the references and the docstring - of `solve_poly_system()`. - - Examples - ======== - - Example taken from [1]. - - >>> from sympy import groebner - >>> from sympy.abc import x, y - - >>> F = [x*y - 2*y, 2*y**2 - x**2] - - >>> groebner(F, x, y, order='lex') - GroebnerBasis([x**2 - 2*y**2, x*y - 2*y, y**3 - 2*y], x, y, - domain='ZZ', order='lex') - >>> groebner(F, x, y, order='grlex') - GroebnerBasis([y**3 - 2*y, x**2 - 2*y**2, x*y - 2*y], x, y, - domain='ZZ', order='grlex') - >>> groebner(F, x, y, order='grevlex') - GroebnerBasis([y**3 - 2*y, x**2 - 2*y**2, x*y - 2*y], x, y, - domain='ZZ', order='grevlex') - - By default, an improved implementation of the Buchberger algorithm is - used. Optionally, an implementation of the F5B algorithm can be used. - The algorithm can be set using ``method`` flag or with the :func:`setup` - function from :mod:`sympy.polys.polyconfig`: - - >>> F = [x**2 - x - 1, (2*x - 1) * y - (x**10 - (1 - x)**10)] - - >>> groebner(F, x, y, method='buchberger') - GroebnerBasis([x**2 - x - 1, y - 55], x, y, domain='ZZ', order='lex') - >>> groebner(F, x, y, method='f5b') - GroebnerBasis([x**2 - x - 1, y - 55], x, y, domain='ZZ', order='lex') - - References - ========== - - 1. [Buchberger01]_ - 2. [Cox97]_ - - """ - return GroebnerBasis(F, *gens, **args) - -
    -
    [docs]def is_zero_dimensional(F, *gens, **args): - """ - Checks if the ideal generated by a Groebner basis is zero-dimensional. - - The algorithm checks if the set of monomials not divisible by the - leading monomial of any element of ``F`` is bounded. - - References - ========== - - David A. Cox, John B. Little, Donal O'Shea. Ideals, Varieties and - Algorithms, 3rd edition, p. 230 - - """ - return GroebnerBasis(F, *gens, **args).is_zero_dimensional - -
    -
    [docs]class GroebnerBasis(Basic): - """Represents a reduced Groebner basis. """ - - __slots__ = ['_basis', '_options'] - - def __new__(cls, F, *gens, **args): - """Compute a reduced Groebner basis for a system of polynomials. """ - options.allowed_flags(args, ['polys', 'method']) - - try: - polys, opt = parallel_poly_from_expr(F, *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('groebner', len(F), exc) - - domain = opt.domain - - if domain.has_assoc_Field: - opt.domain = domain.get_field() - else: - raise DomainError( - "can't compute a Groebner basis over %s" % opt.domain) - - for i, poly in enumerate(polys): - poly = poly.set_domain(opt.domain).rep.to_dict() - polys[i] = sdp_from_dict(poly, opt.order) - - level = len(opt.gens) - 1 - - G = sdp_groebner( - polys, level, opt.order, opt.domain, method=opt.method) - G = [ Poly._from_dict(dict(g), opt) for g in G ] - - if not domain.has_Field: - G = [ g.clear_denoms(convert=True)[1] for g in G ] - opt.domain = domain - - return cls._new(G, opt) - - @classmethod - def _new(cls, basis, options): - obj = Basic.__new__(cls) - - obj._basis = tuple(basis) - obj._options = options - - return obj - - @property - def args(self): - return (Tuple(*self._basis), Tuple(*self._options.gens)) - - @property - def exprs(self): - return [ poly.as_expr() for poly in self._basis ] - - @property - def polys(self): - return list(self._basis) - - @property - def gens(self): - return self._options.gens - - @property - def domain(self): - return self._options.domain - - @property - def order(self): - return self._options.order - - def __len__(self): - return len(self._basis) - - def __iter__(self): - if self._options.polys: - return iter(self.polys) - else: - return iter(self.exprs) - - def __getitem__(self, item): - if self._options.polys: - basis = self.polys - else: - basis = self.exprs - - return basis[item] - - def __hash__(self): - return hash((self._basis, tuple(self._options.items()))) - - def __eq__(self, other): - if isinstance(other, self.__class__): - return self._basis == other._basis and self._options == other._options - elif iterable(other): - return self.polys == list(other) or self.exprs == list(other) - else: - return False - - def __ne__(self, other): - return not self.__eq__(other) - - @property -
    [docs] def is_zero_dimensional(self): - """ - Checks if the ideal generated by a Groebner basis is zero-dimensional. - - The algorithm checks if the set of monomials not divisible by the - leading monomial of any element of ``F`` is bounded. - - References - ========== - - David A. Cox, John B. Little, Donal O'Shea. Ideals, Varieties and - Algorithms, 3rd edition, p. 230 - - """ - def single_var(monomial): - return sum(map(bool, monomial)) == 1 - - exponents = Monomial([0]*len(self.gens)) - order = self._options.order - - for poly in self.polys: - monomial = poly.LM(order=order) - - if single_var(monomial): - exponents *= monomial - - # If any element of the exponents vector is zero, then there's - # a variable for which there's no degree bound and the ideal - # generated by this Groebner basis isn't zero-dimensional. - return all(exponents) -
    -
    [docs] def fglm(self, order): - """ - Convert a Groebner basis from one ordering to another. - - The FGLM algorithm converts reduced Groebner bases of zero-dimensional - ideals from one ordering to another. This method is often used when it - is infeasible to compute a Groebner basis with respect to a particular - ordering directly. - - Examples - ======== - - >>> from sympy.abc import x, y - >>> from sympy import groebner - - >>> F = [x**2 - 3*y - x + 1, y**2 - 2*x + y - 1] - >>> G = groebner(F, x, y, order='grlex') - - >>> list(G.fglm('lex')) - [2*x - y**2 - y + 1, y**4 + 2*y**3 - 3*y**2 - 16*y + 7] - >>> list(groebner(F, x, y, order='lex')) - [2*x - y**2 - y + 1, y**4 + 2*y**3 - 3*y**2 - 16*y + 7] - - References - ========== - - J.C. Faugere, P. Gianni, D. Lazard, T. Mora (1994). Efficient - Computation of Zero-dimensional Groebner Bases by Change of - Ordering - - """ - opt = self._options - - src_order = opt.order - dst_order = monomial_key(order) - - if src_order == dst_order: - return self - - if not self.is_zero_dimensional: - raise NotImplementedError("can't convert Groebner bases of ideals with positive dimension") - - polys = list(self._basis) - domain = opt.domain - - opt = opt.clone(dict( - domain=domain.get_field(), - order=dst_order, - )) - - for i, poly in enumerate(polys): - poly = poly.set_domain(opt.domain).rep.to_dict() - polys[i] = sdp_from_dict(poly, src_order) - - level = len(opt.gens) - 1 - - G = matrix_fglm(polys, level, src_order, dst_order, opt.domain) - G = [ Poly._from_dict(dict(g), opt) for g in G ] - - if not domain.has_Field: - G = [ g.clear_denoms(convert=True)[1] for g in G ] - opt.domain = domain - - return self._new(G, opt) -
    -
    [docs] def reduce(self, expr, auto=True): - """ - Reduces a polynomial modulo a Groebner basis. - - Given a polynomial ``f`` and a set of polynomials ``G = (g_1, ..., g_n)``, - computes a set of quotients ``q = (q_1, ..., q_n)`` and the remainder ``r`` - such that ``f = q_1*f_1 + ... + q_n*f_n + r``, where ``r`` vanishes or ``r`` - is a completely reduced polynomial with respect to ``G``. - - Examples - ======== - - >>> from sympy import groebner, expand - >>> from sympy.abc import x, y - - >>> f = 2*x**4 - x**2 + y**3 + y**2 - >>> G = groebner([x**3 - x, y**3 - y]) - - >>> G.reduce(f) - ([2*x, 1], x**2 + y**2 + y) - >>> Q, r = _ - - >>> expand(sum(q*g for q, g in zip(Q, G)) + r) - 2*x**4 - x**2 + y**3 + y**2 - >>> _ == f - True - - """ - poly = Poly._from_expr(expr, self._options) - polys = [poly] + list(self._basis) - - opt = self._options - domain = opt.domain - - retract = False - - if auto and domain.has_Ring and not domain.has_Field: - opt = opt.clone(dict(domain=domain.get_field())) - retract = True - - for i, poly in enumerate(polys): - poly = poly.set_domain(opt.domain).rep.to_dict() - polys[i] = sdp_from_dict(poly, opt.order) - - level = len(opt.gens) - 1 - - Q, r = sdp_div(polys[0], polys[1:], level, opt.order, opt.domain) - - Q = [ Poly._from_dict(dict(q), opt) for q in Q ] - r = Poly._from_dict(dict(r), opt) - - if retract: - try: - _Q, _r = [ q.to_ring() for q in Q ], r.to_ring() - except CoercionFailed: - pass - else: - Q, r = _Q, _r - - if not opt.polys: - return [ q.as_expr() for q in Q ], r.as_expr() - else: - return Q, r -
    -
    [docs] def contains(self, poly): - """ - Check if ``poly`` belongs the ideal generated by ``self``. - - Examples - ======== - - >>> from sympy import groebner - >>> from sympy.abc import x, y - - >>> f = 2*x**3 + y**3 + 3*y - >>> G = groebner([x**2 + y**2 - 1, x*y - 2]) - - >>> G.contains(f) - True - >>> G.contains(f + 1) - False - - """ - return self.reduce(poly)[1] == 0 - -
    -
    [docs]def poly(expr, *gens, **args): - """ - Efficiently transform an expression into a polynomial. - - Examples - ======== - - >>> from sympy import poly - >>> from sympy.abc import x - - >>> poly(x*(x**2 + x - 1)**2) - Poly(x**5 + 2*x**4 - x**3 - 2*x**2 + x, x, domain='ZZ') - - """ - options.allowed_flags(args, []) - - def _poly(expr, opt): - terms, poly_terms = [], [] - - for term in Add.make_args(expr): - factors, poly_factors = [], [] - - for factor in Mul.make_args(term): - if factor.is_Add: - poly_factors.append(_poly(factor, opt)) - elif factor.is_Pow and factor.base.is_Add and factor.exp.is_Integer: - poly_factors.append( - _poly(factor.base, opt).pow(factor.exp)) - else: - factors.append(factor) - - if not poly_factors: - terms.append(term) - else: - product = poly_factors[0] - - for factor in poly_factors[1:]: - product = product.mul(factor) - - if factors: - factor = Mul(*factors) - - if factor.is_Number: - product = product.mul(factor) - else: - product = product.mul(Poly._from_expr(factor, opt)) - - poly_terms.append(product) - - if not poly_terms: - result = Poly._from_expr(expr, opt) - else: - result = poly_terms[0] - - for term in poly_terms[1:]: - result = result.add(term) - - if terms: - term = Add(*terms) - - if term.is_Number: - result = result.add(term) - else: - result = result.add(Poly._from_expr(term, opt)) - - return result.reorder(*opt.get('gens', ()), **args) - - expr = sympify(expr) - - if expr.is_Poly: - return Poly(expr, *gens, **args) - - if 'expand' not in args: - args['expand'] = False - - opt = options.build_options(gens, args) - - return _poly(expr, opt)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/polys/rationaltools.html b/dev-py3k/_modules/sympy/polys/rationaltools.html deleted file mode 100644 index 0b9c9ce37fc..00000000000 --- a/dev-py3k/_modules/sympy/polys/rationaltools.html +++ /dev/null @@ -1,198 +0,0 @@ - - - - - - - - - - sympy.polys.rationaltools — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.polys.rationaltools

    -"""Tools for manipulation of rational expressions. """
    -
    -from sympy.core import Basic, Add, sympify
    -from sympy.core.exprtools import gcd_terms
    -
    -
    -
    [docs]def together(expr, deep=False): - """ - Denest and combine rational expressions using symbolic methods. - - This function takes an expression or a container of expressions - and puts it (them) together by denesting and combining rational - subexpressions. No heroic measures are taken to minimize degree - of the resulting numerator and denominator. To obtain completely - reduced expression use :func:`cancel`. However, :func:`together` - can preserve as much as possible of the structure of the input - expression in the output (no expansion is performed). - - A wide variety of objects can be put together including lists, - tuples, sets, relational objects, integrals and others. It is - also possible to transform interior of function applications, - by setting ``deep`` flag to ``True``. - - By definition, :func:`together` is a complement to :func:`apart`, - so ``apart(together(expr))`` should return expr unchanged. Note - however, that :func:`together` uses only symbolic methods, so - it might be necessary to use :func:`cancel` to perform algebraic - simplification and minimise degree of the numerator and denominator. - - Examples - ======== - - >>> from sympy import together, exp - >>> from sympy.abc import x, y, z - - >>> together(1/x + 1/y) - (x + y)/(x*y) - >>> together(1/x + 1/y + 1/z) - (x*y + x*z + y*z)/(x*y*z) - - >>> together(1/(x*y) + 1/y**2) - (x + y)/(x*y**2) - - >>> together(1/(1 + 1/x) + 1/(1 + 1/y)) - (x*(y + 1) + y*(x + 1))/((x + 1)*(y + 1)) - - >>> together(exp(1/x + 1/y)) - exp(1/y + 1/x) - >>> together(exp(1/x + 1/y), deep=True) - exp((x + y)/(x*y)) - - >>> together(1/exp(x) + 1/(x*exp(x))) - (x + 1)*exp(-x)/x - - >>> together(1/exp(2*x) + 1/(x*exp(3*x))) - (x*exp(x) + 1)*exp(-3*x)/x - - """ - def _together(expr): - if isinstance(expr, Basic): - if expr.is_Atom or (expr.is_Function and not deep): - return expr - elif expr.is_Add: - return gcd_terms(list(map(_together, Add.make_args(expr)))) - elif expr.is_Pow: - base = _together(expr.base) - - if deep: - exp = _together(expr.exp) - else: - exp = expr.exp - - return expr.__class__(base, exp) - else: - return expr.__class__(*[ _together(arg) for arg in expr.args ]) - elif hasattr(expr, '__iter__'): - return expr.__class__([ _together(ex) for ex in expr ]) - - return expr - - return _together(sympify(expr))
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/polys/rootoftools.html b/dev-py3k/_modules/sympy/polys/rootoftools.html deleted file mode 100644 index 8a7a98602a3..00000000000 --- a/dev-py3k/_modules/sympy/polys/rootoftools.html +++ /dev/null @@ -1,742 +0,0 @@ - - - - - - - - - - sympy.polys.rootoftools — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.polys.rootoftools

    -"""Implementation of RootOf class and related tools. """
    -
    -from sympy.core import S, Expr, Integer, Float, I, Add, Lambda, symbols, sympify
    -
    -from sympy.polys.polytools import Poly, PurePoly, factor
    -from sympy.polys.rationaltools import together
    -from sympy.polys.polyfuncs import symmetrize, viete
    -
    -from sympy.polys.rootisolation import (
    -    dup_isolate_complex_roots_sqf,
    -    dup_isolate_real_roots_sqf)
    -
    -from sympy.polys.polyroots import (
    -    roots_linear, roots_quadratic, roots_binomial,
    -    preprocess_roots, roots)
    -
    -from sympy.polys.polyerrors import (
    -    MultivariatePolynomialError,
    -    GeneratorsNeeded,
    -    PolynomialError,
    -    DomainError)
    -
    -from sympy.polys.domains import QQ
    -
    -from sympy.mpmath import mp, mpf, mpc, findroot
    -from sympy.mpmath.libmp.libmpf import prec_to_dps
    -
    -from sympy.utilities import lambdify
    -
    -_reals_cache = {}
    -_complexes_cache = {}
    -
    -
    -
    [docs]class RootOf(Expr): - """Represents ``k``-th root of a univariate polynomial. """ - - __slots__ = ['poly', 'index'] - is_complex = True - - def __new__(cls, f, x, index=None, radicals=True, expand=True): - """Construct a new ``RootOf`` object for ``k``-th root of ``f``. """ - x = sympify(x) - - if index is None and x.is_Integer: - x, index = None, x - else: - index = sympify(index) - - if index.is_Integer: - index = int(index) - else: - raise ValueError("expected an integer root index, got %d" % index) - - poly = PurePoly(f, x, greedy=False, expand=expand) - - if not poly.is_univariate: - raise PolynomialError("only univariate polynomials are allowed") - - degree = poly.degree() - - if degree <= 0: - raise PolynomialError("can't construct RootOf object for %s" % f) - - if index < -degree or index >= degree: - raise IndexError("root index out of [%d, %d] range, got %d" % - (-degree, degree - 1, index)) - elif index < 0: - index += degree - - dom = poly.get_domain() - - if not dom.is_Exact: - poly = poly.to_exact() - - roots = cls._roots_trivial(poly, radicals) - - if roots is not None: - return roots[index] - - coeff, poly = preprocess_roots(poly) - dom = poly.get_domain() - - if not dom.is_ZZ: - raise NotImplementedError("RootOf is not supported over %s" % dom) - - root = cls._indexed_root(poly, index) - return coeff*cls._postprocess_root(root, radicals) - - @classmethod - def _new(cls, poly, index): - """Construct new ``RootOf`` object from raw data. """ - obj = Expr.__new__(cls) - - obj.poly = poly - obj.index = index - - return obj - - def _hashable_content(self): - return (self.poly, self.index) - - @property - def expr(self): - return self.poly.as_expr() - - @property - def args(self): - return (self.expr, Integer(self.index)) - - @property - def free_symbols(self): - return self.poly.free_symbols - - def _eval_is_real(self): - """Return ``True`` if the root is real. """ - return self.index < len(_reals_cache[self.poly]) - - @classmethod - def real_roots(cls, poly, radicals=True): - """Get real roots of a polynomial. """ - return cls._get_roots("_real_roots", poly, radicals) - - @classmethod - def all_roots(cls, poly, radicals=True): - """Get real and complex roots of a polynomial. """ - return cls._get_roots("_all_roots", poly, radicals) - - @classmethod - def _get_reals_sqf(cls, factor): - """Compute real root isolating intervals for a square-free polynomial. """ - if factor in _reals_cache: - real_part = _reals_cache[factor] - else: - _reals_cache[factor] = real_part = \ - dup_isolate_real_roots_sqf( - factor.rep.rep, factor.rep.dom, blackbox=True) - - return real_part - - @classmethod - def _get_complexes_sqf(cls, factor): - """Compute complex root isolating intervals for a square-free polynomial. """ - if factor in _complexes_cache: - complex_part = _complexes_cache[factor] - else: - _complexes_cache[factor] = complex_part = \ - dup_isolate_complex_roots_sqf( - factor.rep.rep, factor.rep.dom, blackbox=True) - - return complex_part - - @classmethod - def _get_reals(cls, factors): - """Compute real root isolating intervals for a list of factors. """ - reals = [] - - for factor, k in factors: - real_part = cls._get_reals_sqf(factor) - reals.extend([ (root, factor, k) for root in real_part ]) - - return reals - - @classmethod - def _get_complexes(cls, factors): - """Compute complex root isolating intervals for a list of factors. """ - complexes = [] - - for factor, k in factors: - complex_part = cls._get_complexes_sqf(factor) - complexes.extend([ (root, factor, k) for root in complex_part ]) - - return complexes - - @classmethod - def _reals_sorted(cls, reals): - """Make real isolating intervals disjoint and sort roots. """ - cache = {} - - for i, (u, f, k) in enumerate(reals): - for j, (v, g, m) in enumerate(reals[i + 1:]): - u, v = u.refine_disjoint(v) - reals[i + j + 1] = (v, g, m) - - reals[i] = (u, f, k) - - reals = sorted(reals, key=lambda r: r[0].a) - - for root, factor, _ in reals: - if factor in cache: - cache[factor].append(root) - else: - cache[factor] = [root] - - for factor, roots in cache.items(): - _reals_cache[factor] = roots - - return reals - - @classmethod - def _complexes_sorted(cls, complexes): - """Make complex isolating intervals disjoint and sort roots. """ - cache = {} - - for i, (u, f, k) in enumerate(complexes): - for j, (v, g, m) in enumerate(complexes[i + 1:]): - u, v = u.refine_disjoint(v) - complexes[i + j + 1] = (v, g, m) - - complexes[i] = (u, f, k) - - complexes = sorted(complexes, key=lambda r: (r[0].ax, r[0].ay)) - - for root, factor, _ in complexes: - if factor in cache: - cache[factor].append(root) - else: - cache[factor] = [root] - - for factor, roots in cache.items(): - _complexes_cache[factor] = roots - - return complexes - - @classmethod - def _reals_index(cls, reals, index): - """Map initial real root index to an index in a factor where the root belongs. """ - i = 0 - - for j, (_, factor, k) in enumerate(reals): - if index < i + k: - poly, index = factor, 0 - - for _, factor, _ in reals[:j]: - if factor == poly: - index += 1 - - return poly, index - else: - i += k - - @classmethod - def _complexes_index(cls, complexes, index): - """Map initial complex root index to an index in a factor where the root belongs. """ - index, i = index, 0 - - for j, (_, factor, k) in enumerate(complexes): - if index < i + k: - poly, index = factor, 0 - - for _, factor, _ in complexes[:j]: - if factor == poly: - index += 1 - - index += len(_reals_cache[poly]) - - return poly, index - else: - i += k - - @classmethod - def _count_roots(cls, roots): - """Count the number of real or complex roots including multiplicites. """ - return sum([ k for _, _, k in roots ]) - - @classmethod - def _indexed_root(cls, poly, index): - """Get a root of a composite polynomial by index. """ - (_, factors) = poly.factor_list() - - reals = cls._get_reals(factors) - reals_count = cls._count_roots(reals) - - if index < reals_count: - reals = cls._reals_sorted(reals) - return cls._reals_index(reals, index) - else: - complexes = cls._get_complexes(factors) - complexes = cls._complexes_sorted(complexes) - return cls._complexes_index(complexes, index - reals_count) - - @classmethod - def _real_roots(cls, poly): - """Get real roots of a composite polynomial. """ - (_, factors) = poly.factor_list() - - reals = cls._get_reals(factors) - reals = cls._reals_sorted(reals) - reals_count = cls._count_roots(reals) - - roots = [] - - for index in range(0, reals_count): - roots.append(cls._reals_index(reals, index)) - - return roots - - @classmethod - def _all_roots(cls, poly): - """Get real and complex roots of a composite polynomial. """ - (_, factors) = poly.factor_list() - - reals = cls._get_reals(factors) - reals = cls._reals_sorted(reals) - reals_count = cls._count_roots(reals) - - roots = [] - - for index in range(0, reals_count): - roots.append(cls._reals_index(reals, index)) - - complexes = cls._get_complexes(factors) - complexes = cls._complexes_sorted(complexes) - complexes_count = cls._count_roots(complexes) - - for index in range(0, complexes_count): - roots.append(cls._complexes_index(complexes, index)) - - return roots - - @classmethod - def _roots_trivial(cls, poly, radicals): - """Compute roots in linear, quadratic and binomial cases. """ - if poly.degree() == 1: - return roots_linear(poly) - - if not radicals: - return None - - if radicals and poly.degree() == 2: - return roots_quadratic(poly) - elif radicals and poly.length() == 2 and poly.TC(): - return roots_binomial(poly) - else: - return None - - @classmethod - def _preprocess_roots(cls, poly): - """Take heroic measures to make ``poly`` compatible with ``RootOf``. """ - dom = poly.get_domain() - - if not dom.is_Exact: - poly = poly.to_exact() - - coeff, poly = preprocess_roots(poly) - dom = poly.get_domain() - - if not dom.is_ZZ: - raise NotImplementedError("RootOf is not supported over %s" % dom) - - return coeff, poly - - @classmethod - def _postprocess_root(cls, root, radicals): - """Return the root if it is trivial or a ``RootOf`` object. """ - poly, index = root - roots = cls._roots_trivial(poly, radicals) - - if roots is not None: - return roots[index] - else: - return cls._new(poly, index) - - @classmethod - def _get_roots(cls, method, poly, radicals): - """Return postprocessed roots of specified kind. """ - if not poly.is_univariate: - raise PolynomialError("only univariate polynomials are allowed") - - coeff, poly = cls._preprocess_roots(poly) - roots = [] - - for root in getattr(cls, method)(poly): - roots.append(coeff*cls._postprocess_root(root, radicals)) - - return roots - - def _get_interval(self): - """Internal function for retrieving isolation interval from cache. """ - if self.is_real: - return _reals_cache[self.poly][self.index] - else: - reals_count = len(_reals_cache[self.poly]) - return _complexes_cache[self.poly][self.index - reals_count] - - def _set_interval(self, interval): - """Internal function for updating isolation interval in cache. """ - if self.is_real: - _reals_cache[self.poly][self.index] = interval - else: - reals_count = len(_reals_cache[self.poly]) - _complexes_cache[self.poly][self.index - reals_count] = interval - - def _eval_evalf(self, prec): - """Evaluate this complex root to the given precision. """ - _prec, mp.prec = mp.prec, prec - - try: - func = lambdify(self.poly.gen, self.expr) - - interval = self._get_interval() - refined = False - - while True: - if self.is_real: - x0 = mpf(str(interval.center)) - else: - x0 = mpc(*list(map(str, interval.center))) - - try: - root = findroot(func, x0) - except ValueError: - interval = interval.refine() - refined = True - continue - else: - if refined: - self._set_interval(interval) - - break - finally: - mp.prec = _prec - - return Float._new(root.real._mpf_, prec) + I*Float._new(root.imag._mpf_, prec) - -
    -
    [docs]class RootSum(Expr): - """Represents a sum of all roots of a univariate polynomial. """ - - __slots__ = ['poly', 'fun', 'auto'] - - def __new__(cls, expr, func=None, x=None, auto=True, quadratic=False): - """Construct a new ``RootSum`` instance carrying all roots of a polynomial. """ - coeff, poly = cls._transform(expr, x) - - if not poly.is_univariate: - raise MultivariatePolynomialError( - "only univariate polynomials are allowed") - - if func is None: - func = Lambda(poly.gen, poly.gen) - else: - try: - is_func = func.is_Function - except AttributeError: - is_func = False - - if is_func and (func.nargs == 1 or 1 in func.nargs): - if not isinstance(func, Lambda): - func = Lambda(poly.gen, func(poly.gen)) - else: - raise ValueError( - "expected a univariate function, got %s" % func) - - var, expr = func.variables[0], func.expr - - if coeff is not S.One: - expr = expr.subs(var, coeff*var) - - deg = poly.degree() - - if not expr.has(var): - return deg*expr - - if expr.is_Add: - add_const, expr = expr.as_independent(var) - else: - add_const = S.Zero - - if expr.is_Mul: - mul_const, expr = expr.as_independent(var) - else: - mul_const = S.One - - func = Lambda(var, expr) - - rational = cls._is_func_rational(poly, func) - (_, factors), terms = poly.factor_list(), [] - - for poly, k in factors: - if poly.is_linear: - term = func(roots_linear(poly)[0]) - elif quadratic and poly.is_quadratic: - term = sum(map(func, roots_quadratic(poly))) - else: - if not rational or not auto: - term = cls._new(poly, func, auto) - else: - term = cls._rational_case(poly, func) - - terms.append(k*term) - - return mul_const*Add(*terms) + deg*add_const - - @classmethod - def _new(cls, poly, func, auto=True): - """Construct new raw ``RootSum`` instance. """ - obj = Expr.__new__(cls) - - obj.poly = poly - obj.fun = func - obj.auto = auto - - return obj - - @classmethod - def new(cls, poly, func, auto=True): - """Construct new ``RootSum`` instance. """ - if not func.expr.has(*func.variables): - return func.expr - - rational = cls._is_func_rational(poly, func) - - if not rational or not auto: - return cls._new(poly, func, auto) - else: - return cls._rational_case(poly, func) - - @classmethod - def _transform(cls, expr, x): - """Transform an expression to a polynomial. """ - poly = PurePoly(expr, x, greedy=False) - return preprocess_roots(poly) - - @classmethod - def _is_func_rational(cls, poly, func): - """Check if a lambda is areational function. """ - var, expr = func.variables[0], func.expr - return expr.is_rational_function(var) - - @classmethod - def _rational_case(cls, poly, func): - """Handle the rational function case. """ - roots = symbols('r:%d' % poly.degree()) - var, expr = func.variables[0], func.expr - - f = sum(expr.subs(var, r) for r in roots) - p, q = together(f).as_numer_denom() - - domain = QQ[roots] - - p = p.expand() - q = q.expand() - - try: - p = Poly(p, domain=domain, expand=False) - except GeneratorsNeeded: - p, p_coeff = None, (p,) - else: - p_monom, p_coeff = list(zip(*p.terms())) - - try: - q = Poly(q, domain=domain, expand=False) - except GeneratorsNeeded: - q, q_coeff = None, (q,) - else: - q_monom, q_coeff = list(zip(*q.terms())) - - coeffs, mapping = symmetrize(p_coeff + q_coeff, formal=True) - formulas, values = viete(poly, roots), [] - - for (sym, _), (_, val) in zip(mapping, formulas): - values.append((sym, val)) - - for i, (coeff, _) in enumerate(coeffs): - coeffs[i] = coeff.subs(values) - - n = len(p_coeff) - - p_coeff = coeffs[:n] - q_coeff = coeffs[n:] - - if p is not None: - p = Poly(dict(list(zip(p_monom, p_coeff))), *p.gens).as_expr() - else: - (p,) = p_coeff - - if q is not None: - q = Poly(dict(list(zip(q_monom, q_coeff))), *q.gens).as_expr() - else: - (q,) = q_coeff - - return factor(p/q) - - def _hashable_content(self): - return (self.poly, self.fun) - - @property - def expr(self): - return self.poly.as_expr() - - @property - def args(self): - return (self.expr, self.fun, self.poly.gen) - - @property - def free_symbols(self): - return self.poly.free_symbols | self.fun.free_symbols - - @property - def is_commutative(self): - return True - - def doit(self, **hints): - if not hints.get('roots', True): - return self - - _roots = roots(self.poly, multiple=True) - - if len(_roots) < self.poly.degree(): - return self - else: - return Add(*[ self.fun(r) for r in _roots ]) - - def _eval_evalf(self, prec): - try: - _roots = self.poly.nroots(n=prec_to_dps(prec)) - except (DomainError, PolynomialError): - return self - else: - return Add(*[ self.fun(r) for r in _roots ]) - - def _eval_derivative(self, x): - var, expr = self.fun.args - func = Lambda(var, expr.diff(x)) - return self.new(self.poly, func, self.auto)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/polys/specialpolys.html b/dev-py3k/_modules/sympy/polys/specialpolys.html deleted file mode 100644 index 4a60b66cbc7..00000000000 --- a/dev-py3k/_modules/sympy/polys/specialpolys.html +++ /dev/null @@ -1,479 +0,0 @@ - - - - - - - - - - sympy.polys.specialpolys — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.polys.specialpolys

    -"""Functions for generating interesting polynomials, e.g. for benchmarking. """
    -
    -from sympy.core import Add, Mul, Symbol, sympify, Dummy, symbols
    -from sympy.functions.elementary.miscellaneous import sqrt
    -from sympy.core.singleton import S
    -
    -from sympy.polys.polytools import Poly, PurePoly
    -from sympy.polys.polyutils import _analyze_gens
    -
    -from sympy.polys.polyclasses import DMP
    -
    -from sympy.polys.densebasic import (
    -    dmp_zero, dmp_one, dmp_ground, dmp_normal,
    -    dup_from_raw_dict, dmp_raise, dup_random
    -)
    -
    -from sympy.polys.densearith import (
    -    dmp_add_term, dmp_neg, dmp_mul, dmp_sqr
    -)
    -
    -from sympy.polys.factortools import (
    -    dup_zz_cyclotomic_poly
    -)
    -
    -from sympy.polys.domains import ZZ
    -
    -from sympy.ntheory import nextprime
    -
    -from sympy.utilities import cythonized, subsets
    -
    -
    -@cythonized("n,i")
    -
    [docs]def swinnerton_dyer_poly(n, x=None, **args): - """Generates n-th Swinnerton-Dyer polynomial in `x`. """ - if n <= 0: - raise ValueError( - "can't generate Swinnerton-Dyer polynomial of order %s" % n) - - if x is not None: - x, cls = sympify(x), Poly - else: - x, cls = Dummy('x'), PurePoly - - p, elts = 2, [[x, -sqrt(2)], - [x, sqrt(2)]] - - for i in range(2, n + 1): - p, _elts = nextprime(p), [] - - neg_sqrt = -sqrt(p) - pos_sqrt = +sqrt(p) - - for elt in elts: - _elts.append(elt + [neg_sqrt]) - _elts.append(elt + [pos_sqrt]) - - elts = _elts - - poly = [] - - for elt in elts: - poly.append(Add(*elt)) - - if not args.get('polys', False): - return Mul(*poly).expand() - else: - return PurePoly(Mul(*poly), x) - -
    -
    [docs]def cyclotomic_poly(n, x=None, **args): - """Generates cyclotomic polynomial of order `n` in `x`. """ - if n <= 0: - raise ValueError( - "can't generate cyclotomic polynomial of order %s" % n) - - poly = DMP(dup_zz_cyclotomic_poly(int(n), ZZ), ZZ) - - if x is not None: - poly = Poly.new(poly, x) - else: - poly = PurePoly.new(poly, Dummy('x')) - - if not args.get('polys', False): - return poly.as_expr() - else: - return poly - -
    -
    [docs]def symmetric_poly(n, *gens, **args): - """Generates symmetric polynomial of order `n`. """ - gens = _analyze_gens(gens) - - if n < 0 or n > len(gens) or not gens: - raise ValueError("can't generate symmetric polynomial of order %s for %s" % (n, gens)) - elif not n: - poly = S.One - else: - poly = Add(*[ Mul(*s) for s in subsets(gens, int(n)) ]) - - if not args.get('polys', False): - return poly - else: - return Poly(poly, *gens) - -
    -
    [docs]def random_poly(x, n, inf, sup, domain=ZZ, polys=False): - """Return a polynomial of degree ``n`` with coefficients in ``[inf, sup]``. """ - poly = Poly(dup_random(n, inf, sup, domain), x, domain=domain) - - if not polys: - return poly.as_expr() - else: - return poly - -
    -@cythonized("n,i,j") -
    [docs]def interpolating_poly(n, x, X='x', Y='y'): - """Construct Lagrange interpolating polynomial for ``n`` data points. """ - if isinstance(X, str): - X = symbols("%s:%s" % (X, n)) - - if isinstance(Y, str): - Y = symbols("%s:%s" % (Y, n)) - - coeffs = [] - - for i in range(0, n): - numer = [] - denom = [] - - for j in range(0, n): - if i == j: - continue - - numer.append(x - X[j]) - denom.append(X[i] - X[j]) - - numer = Mul(*numer) - denom = Mul(*denom) - - coeffs.append(numer/denom) - - return Add(*[ coeff*y for coeff, y in zip(coeffs, Y) ]) - -
    -@cythonized("n,i") -def fateman_poly_F_1(n): - """Fateman's GCD benchmark: trivial GCD """ - Y = [ Symbol('y_' + str(i)) for i in range(0, n + 1) ] - - y_0, y_1 = Y[0], Y[1] - - u = y_0 + Add(*[ y for y in Y[1:] ]) - v = y_0**2 + Add(*[ y**2 for y in Y[1:] ]) - - F = ((u + 1)*(u + 2)).as_poly(*Y) - G = ((v + 1)*(-3*y_1*y_0**2 + y_1**2 - 1)).as_poly(*Y) - - H = Poly(1, *Y) - - return F, G, H - - -@cythonized("n,m,i") -def dmp_fateman_poly_F_1(n, K): - """Fateman's GCD benchmark: trivial GCD """ - u = [K(1), K(0)] - - for i in range(0, n): - u = [dmp_one(i, K), u] - - v = [K(1), K(0), K(0)] - - for i in range(0, n): - v = [dmp_one(i, K), dmp_zero(i), v] - - m = n - 1 - - U = dmp_add_term(u, dmp_ground(K(1), m), 0, n, K) - V = dmp_add_term(u, dmp_ground(K(2), m), 0, n, K) - - f = [[-K(3), K(0)], [], [K(1), K(0), -K(1)]] - - W = dmp_add_term(v, dmp_ground(K(1), m), 0, n, K) - Y = dmp_raise(f, m, 1, K) - - F = dmp_mul(U, V, n, K) - G = dmp_mul(W, Y, n, K) - - H = dmp_one(n, K) - - return F, G, H - - -@cythonized("n,i") -def fateman_poly_F_2(n): - """Fateman's GCD benchmark: linearly dense quartic inputs """ - Y = [ Symbol('y_' + str(i)) for i in range(0, n + 1) ] - - y_0 = Y[0] - - u = Add(*[ y for y in Y[1:] ]) - - H = Poly((y_0 + u + 1)**2, *Y) - - F = Poly((y_0 - u - 2)**2, *Y) - G = Poly((y_0 + u + 2)**2, *Y) - - return H*F, H*G, H - - -@cythonized("n,m,i") -def dmp_fateman_poly_F_2(n, K): - """Fateman's GCD benchmark: linearly dense quartic inputs """ - u = [K(1), K(0)] - - for i in range(0, n - 1): - u = [dmp_one(i, K), u] - - m = n - 1 - - v = dmp_add_term(u, dmp_ground(K(2), m - 1), 0, n, K) - - f = dmp_sqr([dmp_one(m, K), dmp_neg(v, m, K)], n, K) - g = dmp_sqr([dmp_one(m, K), v], n, K) - - v = dmp_add_term(u, dmp_one(m - 1, K), 0, n, K) - - h = dmp_sqr([dmp_one(m, K), v], n, K) - - return dmp_mul(f, h, n, K), dmp_mul(g, h, n, K), h - - -@cythonized("n,i") -def fateman_poly_F_3(n): - """Fateman's GCD benchmark: sparse inputs (deg f ~ vars f) """ - Y = [ Symbol('y_' + str(i)) for i in range(0, n + 1) ] - - y_0 = Y[0] - - u = Add(*[ y**(n + 1) for y in Y[1:] ]) - - H = Poly((y_0**(n + 1) + u + 1)**2, *Y) - - F = Poly((y_0**(n + 1) - u - 2)**2, *Y) - G = Poly((y_0**(n + 1) + u + 2)**2, *Y) - - return H*F, H*G, H - - -@cythonized("n,i") -def dmp_fateman_poly_F_3(n, K): - """Fateman's GCD benchmark: sparse inputs (deg f ~ vars f) """ - u = dup_from_raw_dict({n + 1: K.one}, K) - - for i in range(0, n - 1): - u = dmp_add_term([u], dmp_one(i, K), n + 1, i + 1, K) - - v = dmp_add_term(u, dmp_ground(K(2), n - 2), 0, n, K) - - f = dmp_sqr( - dmp_add_term([dmp_neg(v, n - 1, K)], dmp_one(n - 1, K), n + 1, n, K), n, K) - g = dmp_sqr(dmp_add_term([v], dmp_one(n - 1, K), n + 1, n, K), n, K) - - v = dmp_add_term(u, dmp_one(n - 2, K), 0, n - 1, K) - - h = dmp_sqr(dmp_add_term([v], dmp_one(n - 1, K), n + 1, n, K), n, K) - - return dmp_mul(f, h, n, K), dmp_mul(g, h, n, K), h - -# A few useful polynomials from Wang's paper ('78). - -f_0 = dmp_normal([ - [[1, 2, 3], [2]], - [[3]], - [[4, 5, 6], [1, 2, 1], [1]] -], 2, ZZ) - -f_1 = dmp_normal([ - [[1, 0], []], - [[1, 0, 1], [20, 30], [1, 10, 0]], - [[1, 0], [30, 20], [1, 10, 1, 610], [20, 230, 300]], - [[1, 10, 0], [30, 320, 200], [600, 6000]] -], 2, ZZ) - -f_2 = dmp_normal([ - [[1], [1, 0], [1, 0, 0], [1, 0, 0, 0]], - [[]], - [[1], [1, 90], [90, 0]], - [[1, -11], [], [1, -11, 0, 0]], - [[]], - [[1, -11], [90, -990]] -], 2, ZZ) - -f_3 = dmp_normal([ - [[1], [], []], - [[1, 0, 0, 0, 1]], - [[1, 0], [], [], [1, 0]], - [[1], [1, 0, 0, 0], [], [1, 0, 0, 0, 1, 0], []], - [[1, 0, 0, 0, 1], [1, 0, 0, 0, 1, 1, 0, 0], []], - [[1, 0], [1, 0, 0, 0, 0], []] -], 2, ZZ) - -f_4 = dmp_normal([ - [[-1, 0], [], [], [], [], [], [], [], []], - [[-1, 0, 0, 0], [], [], [], [], []], - [[-1, 0, 0], [], [], [], [-5], [], [], [], [], [], [], [], []], - [[-1, 0, 0, 0, 0], [], [1, 0, 3, 0], [], [-5, 0, 0], [-1, 0, 0, 0], - [], [], [], []], - [[1, 0, 3, 0, 0, 0], [], [], [-1, 0, 0, 0, 0, 0], []], - [[1, 0, 3, 0, 0], [], [], [-1, 0, 0, 0, 0], [5, 0, 15], [], [], [- - 5, 0, 0], [], [], [], []], - [[1, 0, 3, 0, 0, 0, 0], [], [], [-1, 0, 0, 0, 0, 0, 0], [5, 0, 15, - 0, 0], [1, 0, 3, 0, 0, 0], [], [-5, 0, 0, 0, 0], []], - [[1, 0, 3, 0, 0, 0, 0, 0]], - [[1, 0, 3, 0, 0, 0, 0], [], [], [], [5, 0, 15, 0, 0], [], [], []], - [[1, 0, 3, 0, 0, 0, 0, 0, 0], [], [], [], [5, 0, 15, 0, 0, 0, 0]] -], 2, ZZ) - -f_5 = dmp_normal([ - [[-1]], - [[-3], [3, 0]], - [[-3], [6, 0], [-3, 0, 0]], - [[-1], [3, 0], [-3, 0, 0], [1, 0, 0, 0]] -], 2, ZZ) - -f_6 = dmp_normal([ - [[[2115]], [[]]], - [[[45, 0, 0], [], [], [-45, 0, 0]]], - [[[]]], - [[[-423]], [[-47]], [[]], [[141], [], [94, 0], []], [[]]], - [[[-9, 0, 0], [], [], [9, 0, 0]], - [[-1, 0, 0], [], [], [1, 0, 0]], - [[]], - [[3, 0, 0], [], [2, 0, 0, 0], [-3, 0, 0], [], [-2, 0, 0, 0], []] - ] -], 3, ZZ) - - -w_1 = dmp_normal([ - [[4, 0, 0], [4, 0, 0, 0], [-4, 0, 0, 0, 0], [-4, 0, 0, 0, 0, 0], []], - [[1, 0, 0, 0], [12, 0], [-1, 0, 0, 12, 0, 0], [-12, 0, 0, 0], [-12, - 0, 0, 0, 0]], - [[8], [6, 8, 0], [-4, 4, -8, 0, 0], [-4, -2, -8, 0, 0, 0], []], - [[2, 0], [1, 0, 0, 0], [-1, 0, -2, 0, 9, 0], [-12, 12, 0, 0], [-12, - 3, 0, 0, 0]], - [[6], [-6, 8, 0], [-2, -8, 2, 0, 0], []], - [[2, 0], [-2, 0, 0, 0], [-3, 0], [3, 0, 0, 0]], - [[-2], [2, 0, 0], []] -], 2, ZZ) - -w_2 = dmp_normal([ - [24, 48, 0, 0], - [24, 0, 0, -72, 0, 0], - [25, 2, 0, 4, 8], - [1, 0, 0, 1, 0, 0, -12], - [1, -1, -2, 292, 0, 0], - [-1, 0, 0, 3, 0, 0, 0], - [-1, 0, 12, 0, 0, 48], - [], - [-12, 0, 0, 0] -], 1, ZZ) -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/printing/ccode.html b/dev-py3k/_modules/sympy/printing/ccode.html deleted file mode 100644 index d85880b4f22..00000000000 --- a/dev-py3k/_modules/sympy/printing/ccode.html +++ /dev/null @@ -1,386 +0,0 @@ - - - - - - - - - - sympy.printing.ccode — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.printing.ccode

    -"""
    -C code printer
    -
    -The CCodePrinter converts single sympy expressions into single C expressions,
    -using the functions defined in math.h where possible.
    -
    -A complete code generator, which uses ccode extensively, can be found in
    -sympy.utilities.codegen. The codegen module can be used to generate complete
    -source code files that are compilable without further modifications.
    -"""
    -
    -from sympy.core import S, C
    -from sympy.printing.codeprinter import CodePrinter
    -from sympy.printing.precedence import precedence
    -from sympy.core.compatibility import default_sort_key
    -
    -# dictionary mapping sympy function to (argument_conditions, C_function).
    -# Used in CCodePrinter._print_Function(self)
    -known_functions = {
    -    "ceiling": [(lambda x: True, "ceil")],
    -    "Abs": [(lambda x: not x.is_integer, "fabs")],
    -}
    -
    -
    -
    [docs]class CCodePrinter(CodePrinter): - """A printer to convert python expressions to strings of c code""" - printmethod = "_ccode" - - _default_settings = { - 'order': None, - 'full_prec': 'auto', - 'precision': 15, - 'user_functions': {}, - 'human': True, - } - - def __init__(self, settings={}): - """Register function mappings supplied by user""" - CodePrinter.__init__(self, settings) - self.known_functions = dict(known_functions) - userfuncs = settings.get('user_functions', {}) - for k, v in list(userfuncs.items()): - if not isinstance(v, tuple): - userfuncs[k] = (lambda *x: True, v) - self.known_functions.update(userfuncs) - - def _rate_index_position(self, p): - """function to calculate score based on position among indices - - This method is used to sort loops in an optimized order, see - CodePrinter._sort_optimized() - """ - return p*5 - - def _get_statement(self, codestring): - return "%s;" % codestring - -
    [docs] def doprint(self, expr, assign_to=None): - """ - Actually format the expression as C code. - """ - - if isinstance(assign_to, str): - assign_to = C.Symbol(assign_to) - elif not isinstance(assign_to, (C.Basic, type(None))): - raise TypeError("CCodePrinter cannot assign to object of type %s" % - type(assign_to)) - - # keep a set of expressions that are not strictly translatable to C - # and number constants that must be declared and initialized - not_c = self._not_supported = set() - self._number_symbols = set() - - # We treat top level Piecewise here to get if tests outside loops - lines = [] - if isinstance(expr, C.Piecewise): - for i, (e, c) in enumerate(expr.args): - if i == 0: - lines.append("if (%s) {" % self._print(c)) - elif i == len(expr.args) - 1 and c is True: - lines.append("else {") - else: - lines.append("else if (%s) {" % self._print(c)) - code0 = self._doprint_a_piece(e, assign_to) - lines.extend(code0) - lines.append("}") - else: - code0 = self._doprint_a_piece(expr, assign_to) - lines.extend(code0) - - # format the output - if self._settings["human"]: - frontlines = [] - if len(not_c) > 0: - frontlines.append("// Not C:") - for expr in sorted(not_c, key=str): - frontlines.append("// %s" % repr(expr)) - for name, value in sorted(self._number_symbols, key=str): - frontlines.append("double const %s = %s;" % (name, value)) - lines = frontlines + lines - lines = "\n".join(lines) - result = self.indent_code(lines) - else: - lines = self.indent_code("\n".join(lines)) - result = self._number_symbols, not_c, lines - del self._not_supported - del self._number_symbols - return result -
    - def _get_loop_opening_ending(self, indices): - """Returns a tuple (open_lines, close_lines) containing lists of codelines - """ - open_lines = [] - close_lines = [] - loopstart = "for (int %(var)s=%(start)s; %(var)s<%(end)s; %(var)s++){" - for i in indices: - # C arrays start at 0 and end at dimension-1 - open_lines.append(loopstart % { - 'var': self._print(i.label), - 'start': self._print(i.lower), - 'end': self._print(i.upper + 1)}) - close_lines.append("}") - return open_lines, close_lines - - def _print_Pow(self, expr): - PREC = precedence(expr) - if expr.exp == -1: - return '1.0/%s' % (self.parenthesize(expr.base, PREC)) - elif expr.exp == 0.5: - return 'sqrt(%s)' % self._print(expr.base) - else: - return 'pow(%s, %s)' % (self._print(expr.base), - self._print(expr.exp)) - - def _print_Rational(self, expr): - p, q = int(expr.p), int(expr.q) - return '%d.0/%d.0' % (p, q) - - def _print_Indexed(self, expr): - # calculate index for 1d array - dims = expr.shape - inds = [ i.label for i in expr.indices ] - elem = S.Zero - offset = S.One - for i in reversed(list(range(expr.rank))): - elem += offset*inds[i] - offset *= dims[i] - return "%s[%s]" % (self._print(expr.base.label), self._print(elem)) - - def _print_Exp1(self, expr): - return "M_E" - - def _print_Pi(self, expr): - return 'M_PI' - - def _print_Infinity(self, expr): - return 'HUGE_VAL' - - def _print_NegativeInfinity(self, expr): - return '-HUGE_VAL' - - def _print_Piecewise(self, expr): - # This method is called only for inline if constructs - # Top level piecewise is handled in doprint() - ecpairs = ["(%s) {\n%s\n}\n" % (self._print(c), self._print(e)) - for e, c in expr.args[:-1]] - last_line = "" - if expr.args[-1].cond is True: - last_line = "else {\n%s\n}" % self._print(expr.args[-1].expr) - else: - ecpairs.append("(%s) {\n%s\n" % - (self._print(expr.args[-1].cond), - self._print(expr.args[-1].expr))) - code = "if %s" + last_line - return code % "else if ".join(ecpairs) - - def _print_And(self, expr): - PREC = precedence(expr) - return ' && '.join(self.parenthesize(a, PREC) - for a in sorted(expr.args, key=default_sort_key)) - - def _print_Or(self, expr): - PREC = precedence(expr) - return ' || '.join(self.parenthesize(a, PREC) - for a in sorted(expr.args, key=default_sort_key)) - - def _print_Not(self, expr): - PREC = precedence(expr) - return '!' + self.parenthesize(expr.args[0], PREC) - - def _print_Function(self, expr): - if expr.func.__name__ in self.known_functions: - cond_cfunc = self.known_functions[expr.func.__name__] - for cond, cfunc in cond_cfunc: - if cond(*expr.args): - return "%s(%s)" % (cfunc, self.stringify(expr.args, ", ")) - if hasattr(expr, '_imp_') and isinstance(expr._imp_, C.Lambda): - # inlined function - return self._print(expr._imp_(*expr.args)) - return CodePrinter._print_Function(self, expr) - -
    [docs] def indent_code(self, code): - """Accepts a string of code or a list of code lines""" - - if isinstance(code, str): - code_lines = self.indent_code(code.splitlines(True)) - return ''.join(code_lines) - - tab = " " - inc_token = ('{', '(', '{\n', '(\n') - dec_token = ('}', ')') - - code = [ line.lstrip(' \t') for line in code ] - - increase = [ int(any(map(line.endswith, inc_token))) for line in code ] - decrease = [ int(any(map(line.startswith, dec_token))) - for line in code ] - - pretty = [] - level = 0 - for n, line in enumerate(code): - if line == '' or line == '\n': - pretty.append(line) - continue - level -= decrease[n] - pretty.append("%s%s" % (tab*level, line)) - level += increase[n] - return pretty - -
    -
    [docs]def ccode(expr, assign_to=None, **settings): - r"""Converts an expr to a string of c code - - Parameters - ========== - - expr : sympy.core.Expr - a sympy expression to be converted - precision : optional - the precision for numbers such as pi [default=15] - user_functions : optional - A dictionary where keys are FunctionClass instances and values - are there string representations. Alternatively, the - dictionary value can be a list of tuples i.e. [(argument_test, - cfunction_string)]. See below for examples. - human : optional - If True, the result is a single string that may contain some - constant declarations for the number symbols. If False, the - same information is returned in a more programmer-friendly - data structure. - - Examples - ======== - - >>> from sympy import ccode, symbols, Rational, sin - >>> x, tau = symbols(["x", "tau"]) - >>> ccode((2*tau)**Rational(7,2)) - '8*sqrt(2)*pow(tau, 7.0/2.0)' - >>> ccode(sin(x), assign_to="s") - 's = sin(x);' - - - """ - return CCodePrinter(settings).doprint(expr, assign_to) - -
    - -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/printing/codeprinter.html b/dev-py3k/_modules/sympy/printing/codeprinter.html deleted file mode 100644 index 6ce28c73797..00000000000 --- a/dev-py3k/_modules/sympy/printing/codeprinter.html +++ /dev/null @@ -1,290 +0,0 @@ - - - - - - - - - - sympy.printing.codeprinter — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.printing.codeprinter

    -from sympy.core import C, Add
    -from sympy.printing.str import StrPrinter
    -from sympy.tensor import get_indices, get_contraction_structure
    -
    -
    -
    [docs]class AssignmentError(Exception): - """ - Raised if an assignment variable for a loop is missing. - """ - pass - -
    -
    [docs]class CodePrinter(StrPrinter): - """ - The base class for code-printing subclasses. - """ - - def _doprint_a_piece(self, expr, assign_to=None): - # Here we print an expression that may contain Indexed objects, they - # correspond to arrays in the generated code. The low-level implementation - # involves looping over array elements and possibly storing results in temporary - # variables or accumulate it in the assign_to object. - - lhs_printed = self._print(assign_to) - lines = [] - - # Setup loops over non-dummy indices -- all terms need these - indices = self.get_expression_indices(expr, assign_to) - openloop, closeloop = self._get_loop_opening_ending(indices) - - # Setup loops over dummy indices -- each term needs separate treatment - d = get_contraction_structure(expr) - - # terms with no summations first - if None in d: - text = CodePrinter.doprint(self, Add(*d[None])) - else: - # If all terms have summations we must initialize array to Zero - text = CodePrinter.doprint(self, 0) - # skip redundant assignments - if text != lhs_printed: - lines.extend(openloop) - if assign_to is not None: - text = self._get_statement("%s = %s" % (lhs_printed, text)) - lines.append(text) - lines.extend(closeloop) - - for dummies in d: - # then terms with summations - if isinstance(dummies, tuple): - indices = self._sort_optimized(dummies, expr) - openloop_d, closeloop_d = self._get_loop_opening_ending( - indices) - - for term in d[dummies]: - if term in d and not ([list(f.keys()) for f in d[term]] - == [[None] for f in d[term]]): - # If one factor in the term has it's own internal - # contractions, those must be computed first. - # (temporary variables?) - raise NotImplementedError( - "FIXME: no support for contractions in factor yet") - else: - - # We need the lhs expression as an accumulator for - # the loops, i.e - # - # for (int d=0; d < dim; d++){ - # lhs[] = lhs[] + term[][d] - # } ^.................. the accumulator - # - # We check if the expression already contains the - # lhs, and raise an exception if it does, as that - # syntax is currently undefined. FIXME: What would be - # a good interpretation? - if assign_to is None: - raise AssignmentError( - "need assignment variable for loops") - if term.has(assign_to): - raise ValueError - - lines.extend(openloop) - lines.extend(openloop_d) - text = "%s = %s" % (lhs_printed, CodePrinter.doprint( - self, assign_to + term)) - lines.append(self._get_statement(text)) - lines.extend(closeloop_d) - lines.extend(closeloop) - - return lines - - def get_expression_indices(self, expr, assign_to): - rinds, junk = get_indices(expr) - linds, junk = get_indices(assign_to) - - # support broadcast of scalar - if linds and not rinds: - rinds = linds - if rinds != linds: - raise ValueError("lhs indices must match non-dummy" - " rhs indices in %s" % expr) - - return self._sort_optimized(rinds, assign_to) - - def _sort_optimized(self, indices, expr): - - if not indices: - return [] - - # determine optimized loop order by giving a score to each index - # the index with the highest score are put in the innermost loop. - score_table = {} - for i in indices: - score_table[i] = 0 - - arrays = expr.atoms(C.Indexed) - for arr in arrays: - for p, ind in enumerate(arr.indices): - try: - score_table[ind] += self._rate_index_position(p) - except KeyError: - pass - - return sorted(indices, key=lambda x: score_table[x]) - - def _print_NumberSymbol(self, expr): - # A Number symbol that is not implemented here or with _printmethod - # is registered and evaluated - self._number_symbols.add((expr, - self._print(expr.evalf(self._settings["precision"])))) - return str(expr) - - def _print_Dummy(self, expr): - # dummies must be printed as unique symbols - return "%s_%i" % (expr.name, expr.dummy_index) # Dummy - - _print_Catalan = _print_NumberSymbol - _print_EulerGamma = _print_NumberSymbol - _print_GoldenRatio = _print_NumberSymbol - - def _print_not_supported(self, expr): - self._not_supported.add(expr) - return self.emptyPrinter(expr) - - # The following can not be simply translated into C or Fortran - _print_Basic = _print_not_supported - _print_ComplexInfinity = _print_not_supported - _print_Derivative = _print_not_supported - _print_dict = _print_not_supported - _print_ExprCondPair = _print_not_supported - _print_GeometryEntity = _print_not_supported - _print_Infinity = _print_not_supported - _print_Integral = _print_not_supported - _print_Interval = _print_not_supported - _print_Limit = _print_not_supported - _print_list = _print_not_supported - _print_Matrix = _print_not_supported - _print_DeferredVector = _print_not_supported - _print_NaN = _print_not_supported - _print_NegativeInfinity = _print_not_supported - _print_Normal = _print_not_supported - _print_Order = _print_not_supported - _print_PDF = _print_not_supported - _print_RootOf = _print_not_supported - _print_RootsOf = _print_not_supported - _print_RootSum = _print_not_supported - _print_Sample = _print_not_supported - _print_SparseMatrix = _print_not_supported - _print_tuple = _print_not_supported - _print_Uniform = _print_not_supported - _print_Unit = _print_not_supported - _print_Wild = _print_not_supported - _print_WildFunction = _print_not_supported
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/printing/conventions.html b/dev-py3k/_modules/sympy/printing/conventions.html deleted file mode 100644 index 7532e671a78..00000000000 --- a/dev-py3k/_modules/sympy/printing/conventions.html +++ /dev/null @@ -1,177 +0,0 @@ - - - - - - - - - - sympy.printing.conventions — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.printing.conventions

    -"""
    -A few practical conventions common to all printers.
    -"""
    -
    -
    -import re
    -
    -
    -
    [docs]def split_super_sub(text): - """Split a symbol name into a name, superscripts and subscripts - - The first part of the symbol name is considered to be its actual - 'name', followed by super- and subscripts. Each superscript is - preceded with a "^" character or by "__". Each subscript is preceded - by a "_" character. The three return values are the actual name, a - list with superscripts and a list with subscripts. - - >>> from sympy.printing.conventions import split_super_sub - >>> split_super_sub('a_x^1') - ('a', ['1'], ['x']) - >>> split_super_sub('var_sub1__sup_sub2') - ('var', ['sup'], ['sub1', 'sub2']) - - """ - pos = 0 - name = None - supers = [] - subs = [] - while pos < len(text): - start = pos + 1 - if text[pos:pos + 2] == "__": - start += 1 - pos_hat = text.find("^", start) - if pos_hat < 0: - pos_hat = len(text) - pos_usc = text.find("_", start) - if pos_usc < 0: - pos_usc = len(text) - pos_next = min(pos_hat, pos_usc) - part = text[pos:pos_next] - pos = pos_next - if name is None: - name = part - elif part.startswith("^"): - supers.append(part[1:]) - elif part.startswith("__"): - supers.append(part[2:]) - elif part.startswith("_"): - subs.append(part[1:]) - else: - raise RuntimeError("This should never happen.") - - # make a little exception when a name ends with digits, i.e. treat them - # as a subscript too. - m = re.match('(^[a-zA-Z]+)([0-9]+)$', name) - if m is not None: - name, sub = m.groups() - subs.insert(0, sub) - - return name, supers, subs
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/printing/fcode.html b/dev-py3k/_modules/sympy/printing/fcode.html deleted file mode 100644 index 4ef5a7c03a0..00000000000 --- a/dev-py3k/_modules/sympy/printing/fcode.html +++ /dev/null @@ -1,554 +0,0 @@ - - - - - - - - - - sympy.printing.fcode — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.printing.fcode

    -"""
    -Fortran code printer
    -
    -The FCodePrinter converts single sympy expressions into single Fortran
    -expressions, using the functions defined in the Fortran 77 standard where
    -possible. Some useful pointers to Fortran can be found on wikipedia:
    -
    -http://en.wikipedia.org/wiki/Fortran
    -
    -Most of the code below is based on the "Professional Programmer\'s Guide to
    -Fortran77" by Clive G. Page:
    -
    -http://www.star.le.ac.uk/~cgp/prof77.html
    -
    -Fortran is a case-insensitive language. This might cause trouble because
    -SymPy is case sensitive. The implementation below does not care and leaves
    -the responsibility for generating properly cased Fortran code to the user.
    -"""
    -
    -
    -from sympy.core import S, C, Add
    -from sympy.printing.codeprinter import CodePrinter
    -from sympy.printing.precedence import precedence
    -from sympy.functions import sin, cos, tan, asin, acos, atan, atan2, sinh, \
    -    cosh, tanh, sqrt, log, exp, Abs, sign, conjugate, Piecewise
    -
    -implicit_functions = set([
    -    sin, cos, tan, asin, acos, atan, atan2, sinh, cosh, tanh, sqrt, log, exp,
    -    Abs, sign, conjugate
    -])
    -
    -
    -
    [docs]class FCodePrinter(CodePrinter): - """A printer to convert sympy expressions to strings of Fortran code""" - printmethod = "_fcode" - - _default_settings = { - 'order': None, - 'full_prec': 'auto', - 'assign_to': None, - 'precision': 15, - 'user_functions': {}, - 'human': True, - 'source_format': 'fixed', - } - - def __init__(self, settings=None): - CodePrinter.__init__(self, settings) - self._init_leading_padding() - assign_to = self._settings['assign_to'] - if isinstance(assign_to, str): - self._settings['assign_to'] = C.Symbol(assign_to) - elif not isinstance(assign_to, (C.Basic, type(None))): - raise TypeError("FCodePrinter cannot assign to object of type %s" % - type(assign_to)) - - def _rate_index_position(self, p): - """function to calculate score based on position among indices - - This method is used to sort loops in an optimized order, see - CodePrinter._sort_optimized() - """ - return -p*5 - - def _get_statement(self, codestring): - return codestring - - def _init_leading_padding(self): - # leading columns depend on fixed or free format - if self._settings['source_format'] == 'fixed': - self._lead_code = " " - self._lead_cont = " @ " - self._lead_comment = "C " - elif self._settings['source_format'] == 'free': - self._lead_code = "" - self._lead_cont = " " - self._lead_comment = "! " - else: - raise ValueError( - "Unknown source format: %s" % self._settings[ - 'source_format'] - ) - - def _pad_leading_columns(self, lines): - result = [] - for line in lines: - if line.startswith('!'): - result.append(self._lead_comment + line[1:].lstrip()) - else: - result.append(self._lead_code + line) - return result - - def _get_loop_opening_ending(self, indices): - """Returns a tuple (open_lines, close_lines) containing lists of codelines - """ - open_lines = [] - close_lines = [] - for i in indices: - # fortran arrays start at 1 and end at dimension - var, start, stop = list(map(self._print, - [i.label, i.lower + 1, i.upper + 1])) - open_lines.append("do %s = %s, %s" % (var, start, stop)) - close_lines.append("end do") - return open_lines, close_lines - -
    [docs] def doprint(self, expr): - """Returns Fortran code for expr (as a string)""" - # find all number symbols - self._number_symbols = set() - - # keep a set of expressions that are not strictly translatable to - # Fortran. - self._not_supported = set() - - lines = [] - if isinstance(expr, Piecewise): - # support for top-level Piecewise function - for i, (e, c) in enumerate(expr.args): - if i == 0: - lines.append("if (%s) then" % self._print(c)) - elif i == len(expr.args) - 1 and c is True: - lines.append("else") - else: - lines.append("else if (%s) then" % self._print(c)) - lines.extend( - self._doprint_a_piece(e, self._settings['assign_to'])) - lines.append("end if") - else: - lines.extend( - self._doprint_a_piece(expr, self._settings['assign_to'])) - - # format the output - if self._settings["human"]: - frontlines = [] - if len(self._not_supported) > 0: - frontlines.append("! Not Fortran:") - for expr in sorted(self._not_supported, key=self._print): - frontlines.append("! %s" % repr(expr)) - for name, value in sorted(self._number_symbols, key=str): - frontlines.append("parameter (%s = %s)" % (str(name), value)) - frontlines.extend(lines) - lines = frontlines - lines = self.indent_code(lines) - lines = self._wrap_fortran(lines) - result = "\n".join(lines) - else: - lines = self.indent_code(lines) - lines = self._wrap_fortran(lines) - result = self._number_symbols, self._not_supported, "\n".join( - lines) - - del self._not_supported - del self._number_symbols - return result -
    - def _print_Add(self, expr): - # purpose: print complex numbers nicely in Fortran. - # collect the purely real and purely imaginary parts: - pure_real = [] - pure_imaginary = [] - mixed = [] - for arg in expr.args: - if arg.is_number and arg.is_real: - pure_real.append(arg) - elif arg.is_number and arg.is_imaginary: - pure_imaginary.append(arg) - else: - mixed.append(arg) - if len(pure_imaginary) > 0: - if len(mixed) > 0: - PREC = precedence(expr) - term = Add(*mixed) - t = self._print(term) - if t.startswith('-'): - sign = "-" - t = t[1:] - else: - sign = "+" - if precedence(term) < PREC: - t = "(%s)" % t - - return "cmplx(%s,%s) %s %s" % ( - self._print(Add(*pure_real)), - self._print(-S.ImaginaryUnit*Add(*pure_imaginary)), - sign, t, - ) - else: - return "cmplx(%s,%s)" % ( - self._print(Add(*pure_real)), - self._print(-S.ImaginaryUnit*Add(*pure_imaginary)), - ) - else: - return CodePrinter._print_Add(self, expr) - - def _print_Function(self, expr): - name = self._settings["user_functions"].get(expr.__class__) - if name is None: - if expr.func == conjugate: - name = "conjg" - else: - name = expr.func.__name__ - if hasattr(expr, '_imp_') and isinstance(expr._imp_, C.Lambda): - # inlined function. - # the expression is printed with _print to avoid loops - return self._print(expr._imp_(*expr.args)) - if expr.func not in implicit_functions: - self._not_supported.add(expr) - return "%s(%s)" % (name, self.stringify(expr.args, ", ")) - - _print_factorial = _print_Function - - def _print_ImaginaryUnit(self, expr): - # purpose: print complex numbers nicely in Fortran. - return "cmplx(0,1)" - - def _print_int(self, expr): - return str(expr) - - def _print_Mul(self, expr): - # purpose: print complex numbers nicely in Fortran. - if expr.is_number and expr.is_imaginary: - return "cmplx(0,%s)" % ( - self._print(-S.ImaginaryUnit*expr) - ) - else: - return CodePrinter._print_Mul(self, expr) - - _print_Exp1 = CodePrinter._print_NumberSymbol - _print_Pi = CodePrinter._print_NumberSymbol - - def _print_Pow(self, expr): - PREC = precedence(expr) - if expr.exp == -1: - return '1.0/%s' % (self.parenthesize(expr.base, PREC)) - elif expr.exp == 0.5: - if expr.base.is_integer: - # Fortan intrinsic sqrt() does not accept integer argument - if expr.base.is_Number: - return 'sqrt(%s.0d0)' % self._print(expr.base) - else: - return 'sqrt(dble(%s))' % self._print(expr.base) - else: - return 'sqrt(%s)' % self._print(expr.base) - else: - return CodePrinter._print_Pow(self, expr) - - def _print_Rational(self, expr): - p, q = int(expr.p), int(expr.q) - return "%d.0d0/%d.0d0" % (p, q) - - def _print_Float(self, expr): - printed = CodePrinter._print_Float(self, expr) - e = printed.find('e') - if e > -1: - return "%sd%s" % (printed[:e], printed[e + 1:]) - return "%sd0" % printed - - def _print_Indexed(self, expr): - inds = [ self._print(i) for i in expr.indices ] - return "%s(%s)" % (self._print(expr.base.label), ", ".join(inds)) - - def _print_Idx(self, expr): - return self._print(expr.label) - - def _wrap_fortran(self, lines): - """Wrap long Fortran lines - - Argument: - lines -- a list of lines (without \\n character) - - A comment line is split at white space. Code lines are split with a more - complex rule to give nice results. - """ - # routine to find split point in a code line - my_alnum = set("_+-.0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_") - my_white = set(" \t()") - - def split_pos_code(line, endpos): - if len(line) <= endpos: - return len(line) - pos = endpos - split = lambda pos: \ - (line[pos] in my_alnum and line[pos - 1] not in my_alnum) or \ - (line[pos] not in my_alnum and line[pos - 1] in my_alnum) or \ - (line[pos] in my_white and line[pos - 1] not in my_white) or \ - (line[pos] not in my_white and line[pos - 1] in my_white) - while not split(pos): - pos -= 1 - if pos == 0: - return endpos - return pos - # split line by line and add the splitted lines to result - result = [] - if self._settings['source_format'] == 'free': - trailing = ' &' - else: - trailing = '' - for line in lines: - if line.startswith(self._lead_comment): - # comment line - if len(line) > 72: - pos = line.rfind(" ", 6, 72) - if pos == -1: - pos = 72 - hunk = line[:pos] - line = line[pos:].lstrip() - result.append(hunk) - while len(line) > 0: - pos = line.rfind(" ", 0, 66) - if pos == -1 or len(line) < 66: - pos = 66 - hunk = line[:pos] - line = line[pos:].lstrip() - result.append("%s%s" % (self._lead_comment, hunk)) - else: - result.append(line) - elif line.startswith(self._lead_code): - # code line - pos = split_pos_code(line, 72) - hunk = line[:pos].rstrip() - line = line[pos:].lstrip() - if line: - hunk += trailing - result.append(hunk) - while len(line) > 0: - pos = split_pos_code(line, 65) - hunk = line[:pos].rstrip() - line = line[pos:].lstrip() - if line: - hunk += trailing - result.append("%s%s" % (self._lead_cont, hunk)) - else: - result.append(line) - return result - -
    [docs] def indent_code(self, code): - """Accepts a string of code or a list of code lines""" - if isinstance(code, str): - code_lines = self.indent_code(code.splitlines(True)) - return ''.join(code_lines) - - free = self._settings['source_format'] == 'free' - code = [ line.lstrip(' \t') for line in code ] - - inc_keyword = ('do ', 'if(', 'if ', 'do\n', 'else') - dec_keyword = ('end do', 'enddo', 'end if', 'endif', 'else') - - increase = [ int(any(map(line.startswith, inc_keyword))) - for line in code ] - decrease = [ int(any(map(line.startswith, dec_keyword))) - for line in code ] - continuation = [ int(any(map(line.endswith, ['&', '&\n']))) - for line in code ] - - level = 0 - cont_padding = 0 - tabwidth = 3 - new_code = [] - for i, line in enumerate(code): - if line == '' or line == '\n': - new_code.append(line) - continue - level -= decrease[i] - - if free: - padding = " "*(level*tabwidth + cont_padding) - else: - padding = " "*level*tabwidth - - line = "%s%s" % (padding, line) - if not free: - line = self._pad_leading_columns([line])[0] - - new_code.append(line) - - if continuation[i]: - cont_padding = 2*tabwidth - else: - cont_padding = 0 - level += increase[i] - - if not free: - return self._wrap_fortran(new_code) - return new_code - -
    -
    [docs]def fcode(expr, **settings): - """Converts an expr to a string of Fortran 77 code - - Parameters - ========== - - expr : sympy.core.Expr - a sympy expression to be converted - assign_to : optional - When given, the argument is used as the name of the - variable to which the Fortran expression is assigned. - (This is helpful in case of line-wrapping.) - precision : optional - the precision for numbers such as pi [default=15] - user_functions : optional - A dictionary where keys are FunctionClass instances and values - are there string representations. - human : optional - If True, the result is a single string that may contain some - parameter statements for the number symbols. If False, the same - information is returned in a more programmer-friendly data - structure. - source_format : optional - The source format can be either 'fixed' or 'free'. - [default='fixed'] - - Examples - ======== - - >>> from sympy import fcode, symbols, Rational, pi, sin - >>> x, tau = symbols('x,tau') - >>> fcode((2*tau)**Rational(7,2)) - ' 8*sqrt(2.0d0)*tau**(7.0d0/2.0d0)' - >>> fcode(sin(x), assign_to="s") - ' s = sin(x)' - >>> print(fcode(pi)) - parameter (pi = 3.14159265358979d0) - pi - - """ - # run the printer - printer = FCodePrinter(settings) - return printer.doprint(expr) - -
    - -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/printing/gtk.html b/dev-py3k/_modules/sympy/printing/gtk.html deleted file mode 100644 index 1bb7e2f94da..00000000000 --- a/dev-py3k/_modules/sympy/printing/gtk.html +++ /dev/null @@ -1,134 +0,0 @@ - - - - - - - - - - sympy.printing.gtk — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.printing.gtk

    -from sympy.printing.mathml import mathml
    -import tempfile
    -import os
    -
    -
    -def print_gtk(x, start_viewer=True):
    -    """Print to Gtkmathview, a gtk widget capable of rendering MathML.
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    - - - - - diff --git a/dev-py3k/_modules/sympy/printing/lambdarepr.html b/dev-py3k/_modules/sympy/printing/lambdarepr.html deleted file mode 100644 index ab85e891954..00000000000 --- a/dev-py3k/_modules/sympy/printing/lambdarepr.html +++ /dev/null @@ -1,196 +0,0 @@ - - - - - - - - - - sympy.printing.lambdarepr — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.printing.lambdarepr

    -from .str import StrPrinter
    -from sympy.utilities import default_sort_key
    -
    -
    -def _find_first_symbol(expr):
    -    for atom in expr.atoms():
    -        if atom.is_Symbol:
    -            return atom
    -    raise ValueError('expression must contain a Symbol: %r' % expr)
    -
    -
    -
    [docs]class LambdaPrinter(StrPrinter): - """ - This printer converts expressions into strings that can be used by - lambdify. - """ - - def _print_MatrixBase(self, expr): - return "%s([%s])" % (expr.__class__.__name__, - expr._format_str(self._print, ",")) - _print_SparseMatrix = \ - _print_MutableSparseMatrix = \ - _print_ImmutableSparseMatrix = \ - _print_Matrix = \ - _print_DenseMatrix = \ - _print_MutableDenseMatrix = \ - _print_ImmutableMatrix = \ - _print_ImmutableDenseMatrix = \ - _print_MatrixBase - - def _print_Piecewise(self, expr): - from sympy.core.sets import Interval - result = [] - i = 0 - for arg in expr.args: - e = arg.expr - c = arg.cond - result.append('((') - result.append(self._print(e)) - result.append(') if (') - if isinstance(c, Interval): - result.append(self._print(c.contains(_find_first_symbol(e)))) - else: - result.append(self._print(c)) - result.append(') else (') - i += 1 - result = result[:-1] - result.append(') else None)') - result.append(')'*(2*i - 2)) - return ''.join(result) - - def _print_And(self, expr): - result = ['('] - for arg in sorted(expr.args, key=default_sort_key): - result.extend(['(', self._print(arg), ')']) - result.append(' and ') - result = result[:-1] - result.append(')') - return ''.join(result) - - def _print_Or(self, expr): - result = ['('] - for arg in sorted(expr.args, key=default_sort_key): - result.extend(['(', self._print(arg), ')']) - result.append(' or ') - result = result[:-1] - result.append(')') - return ''.join(result) - - def _print_Not(self, expr): - result = ['(', 'not (', self._print(expr.args[0]), '))'] - return ''.join(result) - -
    -
    [docs]def lambdarepr(expr, **settings): - """ - Returns a string usable for lambdifying. - """ - return LambdaPrinter(settings).doprint(expr)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/printing/latex.html b/dev-py3k/_modules/sympy/printing/latex.html deleted file mode 100644 index 5df2caea254..00000000000 --- a/dev-py3k/_modules/sympy/printing/latex.html +++ /dev/null @@ -1,1675 +0,0 @@ - - - - - - - - - - sympy.printing.latex — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.printing.latex

    -"""
    -A Printer which converts an expression into its LaTeX equivalent.
    -"""
    -
    -from sympy.core import S, C, Add, Symbol
    -from sympy.core.function import _coeff_isneg
    -from .printer import Printer
    -from .conventions import split_super_sub
    -from sympy.simplify import fraction
    -from sympy.core.sympify import SympifyError
    -
    -import sympy.mpmath.libmp as mlib
    -from sympy.mpmath.libmp import prec_to_dps
    -
    -from sympy.core.compatibility import default_sort_key
    -from sympy.utilities.iterables import has_variety
    -
    -import re
    -
    -# Hand-picked functions which can be used directly in both LaTeX and MathJax
    -# Complete list at http://www.mathjax.org/docs/1.1/tex.html#supported-latex-commands
    -# This variable only contains those functions which sympy uses.
    -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', 'psi']
    -
    -
    -
    [docs]class LatexPrinter(Printer): - printmethod = "_latex" - - _default_settings = { - "order": None, - "mode": "plain", - "itex": False, - "fold_frac_powers": False, - "fold_func_brackets": False, - "mul_symbol": None, - "inv_trig_style": "abbreviated", - "mat_str": "smallmatrix", - "mat_delim": "[", - "symbol_names": {}, - } - - def __init__(self, settings=None): - if settings is not None and 'inline' in settings and not settings['inline']: - # Change to "good" defaults for inline=False - settings['mat_str'] = 'bmatrix' - settings['mat_delim'] = None - Printer.__init__(self, settings) - - if 'mode' in self._settings: - valid_modes = ['inline', 'plain', 'equation', - 'equation*'] - if self._settings['mode'] not in valid_modes: - raise ValueError("'mode' must be one of 'inline', 'plain', " - "'equation' or 'equation*'") - - mul_symbol_table = { - None: r" ", - "ldot": r" \,.\, ", - "dot": r" \cdot ", - "times": r" \times " - } - - self._settings['mul_symbol_latex'] = \ - mul_symbol_table[self._settings['mul_symbol']] - - self._delim_dict = {'(': ')', '[': ']'} - - def doprint(self, expr): - tex = Printer.doprint(self, expr) - - if self._settings['mode'] == 'plain': - return tex - elif self._settings['mode'] == 'inline': - return r"$%s$" % tex - elif self._settings['itex']: - return r"$$%s$$" % tex - else: - env_str = self._settings['mode'] - return r"\begin{%s}%s\end{%s}" % (env_str, tex, env_str) - - def _needs_brackets(self, expr): - """ - Returns True if the expression needs to be wrapped in brackets when - printed, False otherwise. For example: a + b => True; a => False; - 10 => False; -10 => True. - """ - return not ((expr.is_Integer and expr.is_nonnegative) - or (expr.is_Atom and expr is not S.NegativeOne)) - - def _needs_function_brackets(self, expr): - """ - Returns True if the expression needs to be wrapped in brackets when - passed as an argument to a function, False otherwise. This is a more - liberal version of _needs_brackets, in that many expressions which need - to be wrapped in brackets when added/subtracted/raised to a power do - not need them when passed to a function. Such an example is a*b. - """ - if not self._needs_brackets(expr): - return False - else: - # Muls of the form a*b*c... can be folded - if expr.is_Mul and not self._mul_is_clean(expr): - return True - # Pows which don't need brackets can be folded - elif expr.is_Pow and not self._pow_is_clean(expr): - return True - # Add and Function always need brackets - elif expr.is_Add or expr.is_Function: - return True - else: - return False - - def _mul_is_clean(self, expr): - for arg in expr.args: - if arg.is_Function: - return False - return True - - def _pow_is_clean(self, expr): - return not self._needs_brackets(expr.base) - - def _do_exponent(self, expr, exp): - if exp is not None: - return r"\left(%s\right)^{%s}" % (expr, exp) - else: - return expr - - def _print_Add(self, expr, order=None): - if self.order == 'none': - terms = list(expr.args) - else: - terms = self._as_ordered_terms(expr, order=order) - tex = self._print(terms[0]) - - for term in terms[1:]: - if not _coeff_isneg(term): - tex += " + " + self._print(term) - else: - tex += " - " + self._print(-term) - - return tex - - def _print_Float(self, expr): - # Based off of that in StrPrinter - dps = prec_to_dps(expr._prec) - str_real = mlib.to_str(expr._mpf_, dps, strip_zeros=True) - - # Must always have a mul symbol (as 2.5 10^{20} just looks odd) - separator = r" \times " - - if self._settings['mul_symbol'] is not None: - separator = self._settings['mul_symbol_latex'] - - if 'e' in str_real: - (mant, exp) = str_real.split('e') - - if exp[0] == '+': - exp = exp[1:] - - return r"%s%s10^{%s}" % (mant, separator, exp) - elif str_real == "+inf": - return r"\infty" - elif str_real == "-inf": - return r"- \infty" - else: - return str_real - - def _print_Mul(self, expr): - coeff, tail = expr.as_coeff_Mul() - - if not coeff.is_negative: - tex = "" - else: - coeff = -coeff - tex = "- " - - numer, denom = fraction(tail, exact=True) - separator = self._settings['mul_symbol_latex'] - - def convert(expr): - if not expr.is_Mul: - return str(self._print(expr)) - else: - _tex = last_term_tex = "" - - if self.order not in ('old', 'none'): - args = expr.as_ordered_factors() - else: - args = expr.args - - from sympy import Integral, Piecewise, Product, Sum - - for i, term in enumerate(args): - term_tex = self._print(term) - - if term.is_Add or (i != len(args) - 1 and - isinstance(term, (Integral, Piecewise, Product, Sum))): - term_tex = r"\left(%s\right)" % term_tex - - # between two digits, \times must always be used, - # to avoid confusion - if separator == " " and \ - re.search("[0-9][} ]*$", last_term_tex) and \ - re.match("[{ ]*[-+0-9]", term_tex): - _tex += r" \times " - elif _tex: - _tex += separator - - _tex += term_tex - last_term_tex = term_tex - return _tex - - if denom is S.One: - if numer.is_Add: - _tex = r"\left(%s\right)" % convert(numer) - else: - _tex = r"%s" % convert(numer) - - if coeff is not S.One: - tex += str(self._print(coeff)) - - # between two digits, \times must always be used, to avoid - # confusion - if separator == " " and re.search("[0-9][} ]*$", tex) and \ - re.match("[{ ]*[-+0-9]", _tex): - tex += r" \times " + _tex - else: - tex += separator + _tex - else: - tex += _tex - - else: - if numer is S.One: - if coeff.is_Integer: - numer *= coeff.p - elif coeff.is_Rational: - if coeff.p != 1: - numer *= coeff.p - - denom *= coeff.q - elif coeff is not S.One: - tex += str(self._print(coeff)) + " " - else: - if coeff.is_Rational and coeff.p == 1: - denom *= coeff.q - elif coeff is not S.One: - tex += str(self._print(coeff)) + " " - - tex += r"\frac{%s}{%s}" % \ - (convert(numer), convert(denom)) - - return tex - - def _print_Pow(self, expr): - # Treat x**Rational(1,n) as special case - if expr.exp.is_Rational and abs(expr.exp.p) == 1 and expr.exp.q != 1: - base = self._print(expr.base) - 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 - 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 1030 - #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: - tex = r"%s^{%s}" - - return tex % (self._print(expr.base), - self._print(expr.exp)) - - def _print_Sum(self, expr): - if len(expr.limits) == 1: - tex = r"\sum_{%s=%s}^{%s} " % \ - tuple([ self._print(i) for i in expr.limits[0] ]) - else: - def _format_ineq(l): - return r"%s \leq %s \leq %s" % \ - tuple([self._print(s) for s in (l[1], l[0], l[2])]) - - tex = r"\sum_{\substack{%s}} " % \ - str.join('\\\\', [ _format_ineq(l) for l in expr.limits ]) - - if isinstance(expr.function, Add): - tex += r"\left(%s\right)" % self._print(expr.function) - else: - tex += self._print(expr.function) - - return tex - - def _print_Product(self, expr): - if len(expr.limits) == 1: - tex = r"\prod_{%s=%s}^{%s} " % \ - tuple([ self._print(i) for i in expr.limits[0] ]) - else: - def _format_ineq(l): - return r"%s \leq %s \leq %s" % \ - tuple([self._print(s) for s in (l[1], l[0], l[2])]) - - tex = r"\prod_{\substack{%s}} " % \ - str.join('\\\\', [ _format_ineq(l) for l in expr.limits ]) - - if isinstance(expr.function, Add): - tex += r"\left(%s\right)" % self._print(expr.function) - else: - tex += self._print(expr.function) - - return tex - - def _print_Derivative(self, expr): - dim = len(expr.variables) - - if dim == 1: - 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: - multiplicity.append((current, i)) - - 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_Subs(self, subs): - expr, old, new = subs.args - latex_expr = self._print(expr) - latex_old = (self._print(e) for e in old) - latex_new = (self._print(e) for e in new) - latex_subs = r'\\ '.join( - e[0] + '=' + e[1] for e in zip(latex_old, latex_new)) - return r'\left. %s \right|_{\substack{ %s }}' % (latex_expr, latex_subs) - - def _print_Integral(self, expr): - tex, symbols = "", [] - - # Only up to \iiiint exists - if len(expr.limits) <= 4 and all(len(lim) == 1 for lim in expr.limits): - # Use len(expr.limits)-1 so that syntax highlighters don't think - # \" is an escaped quote - tex = r"\i" + "i"*(len(expr.limits) - 1) + "nt" - symbols = [r"\, d%s" % self._print(symbol[0]) - for symbol in expr.limits] - - else: - for lim in reversed(expr.limits): - symbol = lim[0] - tex += r"\int" - - if len(lim) > 1: - if self._settings['mode'] in ['equation', 'equation*'] \ - and not self._settings['itex']: - tex += r"\limits" - - if len(lim) == 3: - tex += "_{%s}^{%s}" % (self._print(lim[1]), - self._print(lim[2])) - if len(lim) == 2: - tex += "^{%s}" % (self._print(lim[1])) - - symbols.insert(0, r"\, d%s" % self._print(symbol)) - - return r"%s %s%s" % (tex, - str(self._print(expr.function)), "".join(symbols)) - - def _print_Limit(self, expr): - e, z, z0, dir = expr.args - - tex = r"\lim_{%s \to %s}" % (self._print(z), - self._print(z0)) - - if isinstance(e, C.AssocOp): - return r"%s\left(%s\right)" % (tex, self._print(e)) - else: - return r"%s %s" % (tex, self._print(e)) - - def _print_Function(self, expr, exp=None): - func = expr.func.__name__ - - 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: - # If the generic function name contains an underscore, handle it - name = r"\operatorname{%s}^{%s}" % ( - func.replace("_", r"\_"), exp) - else: - if func in accepted_latex_functions: - name = r"\%s" % func - else: - # If the generic function name contains an underscore, handle it - name = r"\operatorname{%s}" % func.replace("_", r"\_") - - 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: - name += r"%s" - else: - name += r"{\left (%s \right )}" - - if inv_trig_power_case and exp is not None: - name += r"^{%s}" % exp - - return name % ",".join(args) - - def _print_Lambda(self, expr): - symbols, expr = expr.args - - if len(symbols) == 1: - symbols = self._print(symbols[0]) - else: - symbols = self._print(tuple(symbols)) - - args = (symbols, self._print(expr)) - tex = r"\Lambda {\left (%s \right )}" % ", ".join(args) - - return tex - - def _print_Min(self, expr, exp=None): - args = sorted(expr.args, key=default_sort_key) - texargs = [r"%s" % self._print(symbol) for symbol in args] - tex = r"\min\left(%s\right)" % ", ".join(texargs) - - if exp is not None: - return r"%s^{%s}" % (tex, exp) - else: - return tex - - def _print_Max(self, expr, exp=None): - args = sorted(expr.args, key=default_sort_key) - texargs = [r"%s" % self._print(symbol) for symbol in args] - tex = r"\max\left(%s\right)" % ", ".join(texargs) - - if exp is not None: - return r"%s^{%s}" % (tex, exp) - else: - return tex - - def _print_floor(self, expr, exp=None): - tex = r"\lfloor{%s}\rfloor" % self._print(expr.args[0]) - - if exp is not None: - return r"%s^{%s}" % (tex, exp) - else: - return tex - - def _print_ceiling(self, expr, exp=None): - tex = r"\lceil{%s}\rceil" % self._print(expr.args[0]) - - if exp is not None: - return r"%s^{%s}" % (tex, exp) - else: - return tex - - def _print_Abs(self, expr, exp=None): - tex = r"\lvert{%s}\rvert" % self._print(expr.args[0]) - - if exp is not None: - return r"%s^{%s}" % (tex, exp) - else: - return tex - - def _print_re(self, expr, exp=None): - if self._needs_brackets(expr.args[0]): - tex = r"\Re {\left (%s \right )}" % self._print(expr.args[0]) - else: - tex = r"\Re{%s}" % self._print(expr.args[0]) - - return self._do_exponent(tex, exp) - - def _print_im(self, expr, exp=None): - if self._needs_brackets(expr.args[0]): - tex = r"\Im {\left ( %s \right )}" % self._print(expr.args[0]) - else: - tex = r"\Im{%s}" % self._print(expr.args[0]) - - return self._do_exponent(tex, exp) - - def _print_Not(self, e): - if (e.args[0].is_Boolean): - return r"\neg (%s)" % self._print(e.args[0]) - else: - return r"\neg %s" % self._print(e.args[0]) - - def _print_And(self, e): - args = sorted(e.args, key=default_sort_key) - arg = args[0] - if arg.is_Boolean and not arg.is_Not: - tex = r"\left(%s\right)" % self._print(arg) - else: - tex = r"%s" % self._print(arg) - - for arg in args[1:]: - if arg.is_Boolean and not arg.is_Not: - tex += r" \wedge \left(%s\right)" % (self._print(arg)) - else: - tex += r" \wedge %s" % (self._print(arg)) - - return tex - - def _print_Or(self, e): - args = sorted(e.args, key=default_sort_key) - arg = args[0] - if arg.is_Boolean and not arg.is_Not: - tex = r"\left(%s\right)" % self._print(arg) - else: - tex = r"%s" % self._print(arg) - - for arg in args[1:]: - if arg.is_Boolean and not arg.is_Not: - tex += r" \vee \left(%s\right)" % (self._print(arg)) - else: - tex += r" \vee %s" % (self._print(arg)) - - return tex - - def _print_Implies(self, e): - return r"%s \Rightarrow %s" % (self._print(e.args[0]), self._print(e.args[1])) - - def _print_Equivalent(self, e): - return r"%s \Leftrightarrow %s" % (self._print(e.args[0]), self._print(e.args[1])) - - def _print_conjugate(self, expr, exp=None): - tex = r"\overline{%s}" % self._print(expr.args[0]) - - if exp is not None: - return r"%s^{%s}" % (tex, exp) - else: - return tex - - def _print_ExpBase(self, expr, exp=None): - # TODO should exp_polar be printed differently? - # what about exp_polar(0), exp_polar(1)? - tex = r"e^{%s}" % self._print(expr.args[0]) - return self._do_exponent(tex, exp) - - def _print_gamma(self, expr, exp=None): - tex = r"\left(%s\right)" % self._print(expr.args[0]) - - if exp is not None: - return r"\Gamma^{%s}%s" % (exp, tex) - else: - return r"\Gamma%s" % tex - - def _print_uppergamma(self, expr, exp=None): - tex = r"\left(%s, %s\right)" % (self._print(expr.args[0]), - self._print(expr.args[1])) - - if exp is not None: - return r"\Gamma^{%s}%s" % (exp, tex) - else: - return r"\Gamma%s" % tex - - def _print_lowergamma(self, expr, exp=None): - tex = r"\left(%s, %s\right)" % (self._print(expr.args[0]), - self._print(expr.args[1])) - - if exp is not None: - return r"\gamma^{%s}%s" % (exp, tex) - else: - return r"\gamma%s" % tex - - def _print_expint(self, expr, exp=None): - tex = r"\left(%s\right)" % self._print(expr.args[1]) - nu = self._print(expr.args[0]) - - if exp is not None: - return r"\operatorname{E}_{%s}^{%s}%s" % (nu, exp, tex) - else: - return r"\operatorname{E}_{%s}%s" % (nu, tex) - - def _print_subfactorial(self, expr, exp=None): - x = expr.args[0] - if self._needs_brackets(x): - tex = r"!\left(%s\right)" % self._print(x) - else: - tex = "!" + self._print(x) - - if exp is not None: - return r"%s^{%s}" % (tex, exp) - else: - return tex - - def _print_factorial(self, expr, exp=None): - x = expr.args[0] - if self._needs_brackets(x): - tex = r"\left(%s\right)!" % self._print(x) - else: - tex = self._print(x) + "!" - - if exp is not None: - return r"%s^{%s}" % (tex, exp) - else: - return tex - - def _print_factorial2(self, expr, exp=None): - x = expr.args[0] - if self._needs_brackets(x): - tex = r"\left(%s\right)!!" % self._print(x) - else: - tex = self._print(x) + "!!" - - if exp is not None: - return r"%s^{%s}" % (tex, exp) - else: - return tex - - def _print_binomial(self, expr, exp=None): - tex = r"{\binom{%s}{%s}}" % (self._print(expr.args[0]), - self._print(expr.args[1])) - - if exp is not None: - return r"%s^{%s}" % (tex, exp) - else: - return tex - - def _print_RisingFactorial(self, expr, exp=None): - tex = r"{\left(%s\right)}^{\left(%s\right)}" % \ - (self._print(expr.args[0]), self._print(expr.args[1])) - - return self._do_exponent(tex, exp) - - def _print_FallingFactorial(self, expr, exp=None): - tex = r"{\left(%s\right)}_{\left(%s\right)}" % \ - (self._print(expr.args[0]), self._print(expr.args[1])) - - return self._do_exponent(tex, exp) - - def _hprint_BesselBase(self, expr, exp, sym): - tex = r"%s" % (sym) - - need_exp = False - if exp is not None: - if tex.find('^') == -1: - tex = r"%s^{%s}" % (tex, self._print(exp)) - else: - need_exp = True - - tex = r"%s_{%s}\left(%s\right)" % (tex, self._print(expr.order), - self._print(expr.argument)) - - if need_exp: - tex = self._do_exponent(tex, exp) - return tex - - def _hprint_vec(self, vec): - if len(vec) == 0: - return "" - s = "" - for i in vec[:-1]: - s += "%s, " % self._print(i) - s += self._print(vec[-1]) - return s - - def _print_besselj(self, expr, exp=None): - return self._hprint_BesselBase(expr, exp, 'J') - - def _print_besseli(self, expr, exp=None): - return self._hprint_BesselBase(expr, exp, 'I') - - def _print_besselk(self, expr, exp=None): - return self._hprint_BesselBase(expr, exp, 'K') - - def _print_bessely(self, expr, exp=None): - return self._hprint_BesselBase(expr, exp, 'Y') - - def _print_yn(self, expr, exp=None): - return self._hprint_BesselBase(expr, exp, 'y') - - def _print_jn(self, expr, exp=None): - return self._hprint_BesselBase(expr, exp, 'j') - - def _print_hankel1(self, expr, exp=None): - return self._hprint_BesselBase(expr, exp, 'H^{(1)}') - - def _print_hankel2(self, expr, exp=None): - return self._hprint_BesselBase(expr, exp, 'H^{(2)}') - - def _print_fresnels(self, expr, exp=None): - tex = r"\left(%s\right)" % self._print(expr.args[0]) - - if exp is not None: - return r"S^{%s}%s" % (exp, tex) - else: - return r"S%s" % tex - - def _print_fresnelc(self, expr, exp=None): - tex = r"\left(%s\right)" % self._print(expr.args[0]) - - if exp is not None: - return r"C^{%s}%s" % (exp, tex) - else: - return r"C%s" % tex - - def _print_hyper(self, expr, exp=None): - tex = r"{{}_{%s}F_{%s}\left(\begin{matrix} %s \\ %s \end{matrix}" \ - r"\middle| {%s} \right)}" % \ - (self._print(len(expr.ap)), self._print(len(expr.bq)), - self._hprint_vec(expr.ap), self._hprint_vec(expr.bq), - self._print(expr.argument)) - - if exp is not None: - tex = r"{%s}^{%s}" % (tex, self._print(exp)) - return tex - - def _print_meijerg(self, expr, exp=None): - tex = r"{G_{%s, %s}^{%s, %s}\left(\begin{matrix} %s & %s \\" \ - r"%s & %s \end{matrix} \middle| {%s} \right)}" % \ - (self._print(len(expr.ap)), self._print(len(expr.bq)), - self._print(len(expr.bm)), self._print(len(expr.an)), - self._hprint_vec(expr.an), self._hprint_vec(expr.aother), - self._hprint_vec(expr.bm), self._hprint_vec(expr.bother), - self._print(expr.argument)) - - if exp is not None: - tex = r"{%s}^{%s}" % (tex, self._print(exp)) - return tex - - def _print_dirichlet_eta(self, expr, exp=None): - tex = r"\left(%s\right)" % self._print(expr.args[0]) - if exp is not None: - return r"\eta^{%s}%s" % (self._print(exp), tex) - return r"\eta%s" % tex - - def _print_zeta(self, expr, exp=None): - if len(expr.args) == 2: - tex = r"\left(%s, %s\right)" % tuple(map(self._print, expr.args)) - else: - tex = r"\left(%s\right)" % self._print(expr.args[0]) - if exp is not None: - return r"\zeta^{%s}%s" % (self._print(exp), tex) - return r"\zeta%s" % tex - - def _print_lerchphi(self, expr, exp=None): - tex = r"\left(%s, %s, %s\right)" % tuple(map(self._print, expr.args)) - if exp is None: - return r"\Phi%s" % tex - return r"\Phi^{%s}%s" % (self._print(exp), tex) - - def _print_polylog(self, expr, exp=None): - s, z = list(map(self._print, expr.args)) - tex = r"\left(%s\right)" % z - if exp is None: - return r"\operatorname{Li}_{%s}%s" % (s, tex) - return r"\operatorname{Li}_{%s}^{%s}%s" % (s, self._print(exp), tex) - - def _print_jacobi(self, expr, exp=None): - n, a, b, x = list(map(self._print, expr.args)) - tex = r"P_{%s}^{\left(%s,%s\right)}\left(%s\right)" % (n, a, b, x) - if exp is not None: - tex = r"\left(" + tex + r"\right)^{%s}" % (self._print(exp)) - return tex - - def _print_gegenbauer(self, expr, exp=None): - n, a, x = list(map(self._print, expr.args)) - tex = r"C_{%s}^{\left(%s\right)}\left(%s\right)" % (n, a, x) - if exp is not None: - tex = r"\left(" + tex + r"\right)^{%s}" % (self._print(exp)) - return tex - - def _print_chebyshevt(self, expr, exp=None): - n, x = list(map(self._print, expr.args)) - tex = r"T_{%s}\left(%s\right)" % (n, x) - if exp is not None: - tex = r"\left(" + tex + r"\right)^{%s}" % (self._print(exp)) - return tex - - def _print_chebyshevu(self, expr, exp=None): - n, x = list(map(self._print, expr.args)) - tex = r"U_{%s}\left(%s\right)" % (n, x) - if exp is not None: - tex = r"\left(" + tex + r"\right)^{%s}" % (self._print(exp)) - return tex - - def _print_legendre(self, expr, exp=None): - n, x = list(map(self._print, expr.args)) - tex = r"P_{%s}\left(%s\right)" % (n, x) - if exp is not None: - tex = r"\left(" + tex + r"\right)^{%s}" % (self._print(exp)) - return tex - - def _print_assoc_legendre(self, expr, exp=None): - n, a, x = list(map(self._print, expr.args)) - tex = r"P_{%s}^{\left(%s\right)}\left(%s\right)" % (n, a, x) - if exp is not None: - tex = r"\left(" + tex + r"\right)^{%s}" % (self._print(exp)) - return tex - - def _print_hermite(self, expr, exp=None): - n, x = list(map(self._print, expr.args)) - tex = r"H_{%s}\left(%s\right)" % (n, x) - if exp is not None: - tex = r"\left(" + tex + r"\right)^{%s}" % (self._print(exp)) - return tex - - def _print_laguerre(self, expr, exp=None): - n, x = list(map(self._print, expr.args)) - tex = r"L_{%s}\left(%s\right)" % (n, x) - if exp is not None: - tex = r"\left(" + tex + r"\right)^{%s}" % (self._print(exp)) - return tex - - def _print_assoc_laguerre(self, expr, exp=None): - n, a, x = list(map(self._print, expr.args)) - tex = r"L_{%s}^{\left(%s\right)}\left(%s\right)" % (n, a, x) - if exp is not None: - tex = r"\left(" + tex + r"\right)^{%s}" % (self._print(exp)) - return tex - - def _print_Rational(self, expr): - if expr.q != 1: - sign = "" - p = expr.p - if expr.p < 0: - sign = "- " - p = -p - return r"%s\frac{%d}{%d}" % (sign, p, expr.q) - else: - return self._print(expr.p) - - def _print_Infinity(self, expr): - return r"\infty" - - def _print_NegativeInfinity(self, expr): - return r"-\infty" - - def _print_ComplexInfinity(self, expr): - return r"\tilde{\infty}" - - def _print_ImaginaryUnit(self, expr): - return r"\mathbf{\imath}" - - def _print_NaN(self, expr): - return r"\bot" - - def _print_Pi(self, expr): - return r"\pi" - - def _print_Exp1(self, expr): - return r"e" - - def _print_EulerGamma(self, expr): - return r"\gamma" - - def _print_Order(self, expr): - return r"\mathcal{O}\left(%s\right)" % \ - self._print(expr.args[0]) - - def _print_Symbol(self, expr): - if expr in self._settings['symbol_names']: - return self._settings['symbol_names'][expr] - - name, supers, subs = split_super_sub(expr.name) - - # 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) - supers = [translate(sup) for sup in supers] - subs = [translate(sub) for sub in subs] - - # glue all items together: - if len(supers) > 0: - name += "^{%s}" % " ".join(supers) - if len(subs) > 0: - name += "_{%s}" % " ".join(subs) - - return name - - def _print_Relational(self, expr): - if self._settings['itex']: - gt = r"\gt" - lt = r"\lt" - else: - gt = ">" - lt = "<" - - charmap = { - "==": "=", - ">": gt, - "<": lt, - ">=": r"\geq", - "<=": r"\leq", - "!=": r"\neq", - } - - return "%s %s %s" % (self._print(expr.lhs), - charmap[expr.rel_op], self._print(expr.rhs)) - - def _print_Piecewise(self, expr): - ecpairs = [r"%s & \text{for}\: %s" % (self._print(e), self._print(c)) - for e, c in expr.args[:-1]] - if expr.args[-1].cond is True: - ecpairs.append(r"%s & \text{otherwise}" % - self._print(expr.args[-1].expr)) - else: - ecpairs.append(r"%s & \text{for}\: %s" % - (self._print(expr.args[-1].expr), - self._print(expr.args[-1].cond))) - tex = r"\begin{cases} %s \end{cases}" - return tex % r" \\".join(ecpairs) - - def _print_MatrixBase(self, expr): - lines = [] - - for line in range(expr.rows): # horrible, should be 'rows' - lines.append(" & ".join([ self._print(i) for i in expr[line, :] ])) - - out_str = r'\begin{%MATSTR%}%s\end{%MATSTR%}' - out_str = out_str.replace('%MATSTR%', self._settings['mat_str']) - if self._settings['mat_delim']: - left_delim = self._settings['mat_delim'] - right_delim = self._delim_dict[left_delim] - out_str = r'\left' + left_delim + out_str + \ - r'\right' + right_delim - return out_str % r"\\".join(lines) - _print_ImmutableMatrix = _print_MatrixBase - _print_Matrix = _print_MatrixBase - - def _print_BlockMatrix(self, expr): - return self._print(expr.blocks) - - def _print_Transpose(self, expr): - mat = expr.arg - from sympy.matrices import MatrixSymbol - if not isinstance(mat, MatrixSymbol): - return r"\left(%s\right)^T" % self._print(mat) - else: - return "%s^T" % self._print(mat) - - def _print_Adjoint(self, expr): - mat = expr.arg - from sympy.matrices import MatrixSymbol - if not isinstance(mat, MatrixSymbol): - return r"\left(%s\right)^\dag" % self._print(mat) - else: - return "%s^\dag" % self._print(mat) - - def _print_MatAdd(self, expr): - terms = list(expr.args) - tex = " + ".join(map(self._print, terms)) - return tex - - def _print_MatMul(self, expr): - from sympy import Add, MatAdd, HadamardProduct - - def parens(x): - if isinstance(x, (Add, MatAdd, HadamardProduct)): - return r"\left(%s\right)" % self._print(x) - return self._print(x) - return ' '.join(map(parens, expr.args)) - - def _print_HadamardProduct(self, expr): - from sympy import Add, MatAdd, MatMul - - def parens(x): - if isinstance(x, (Add, MatAdd, MatMul)): - return r"\left(%s\right)" % self._print(x) - return self._print(x) - return ' \circ '.join(map(parens, expr.args)) - - def _print_MatPow(self, expr): - base, exp = expr.base, expr.exp - from sympy.matrices import MatrixSymbol - if not isinstance(base, MatrixSymbol): - return r"\left(%s\right)^{%s}" % (self._print(base), self._print(exp)) - else: - return "%s^{%s}" % (self._print(base), self._print(exp)) - - def _print_ZeroMatrix(self, Z): - return r"\bold{0}" - - def _print_Identity(self, I): - return r"\mathbb{I}" - - def _print_tuple(self, expr): - return r"\begin{pmatrix}%s\end{pmatrix}" % \ - r", & ".join([ self._print(i) for i in expr ]) - - def _print_Tuple(self, expr): - return self._print_tuple(expr) - - def _print_list(self, expr): - return r"\begin{bmatrix}%s\end{bmatrix}" % \ - r", & ".join([ self._print(i) for i in expr ]) - - def _print_dict(self, d): - keys = sorted(list(d.keys()), key=default_sort_key) - items = [] - - for key in keys: - val = d[key] - items.append("%s : %s" % (self._print(key), self._print(val))) - - return r"\begin{Bmatrix}%s\end{Bmatrix}" % r", & ".join(items) - - def _print_Dict(self, expr): - return self._print_dict(expr) - - def _print_DiracDelta(self, expr, exp=None): - if len(expr.args) == 1 or expr.args[1] == 0: - tex = r"\delta\left(%s\right)" % self._print(expr.args[0]) - else: - tex = r"\delta^{\left( %s \right)}\left( %s \right)" % ( - self._print(expr.args[1]), self._print(expr.args[0])) - if exp: - tex = r"\left(%s\right)^{%s}" % (tex, exp) - return tex - - def _print_Heaviside(self, expr, exp=None): - tex = r"\theta\left(%s\right)" % self._print(expr.args[0]) - if exp: - tex = r"\left(%s\right)^{%s}" % (tex, exp) - return tex - - def _print_KroneckerDelta(self, expr, exp=None): - i = self._print(expr.args[0]) - j = self._print(expr.args[1]) - if expr.args[0].is_Atom and expr.args[1].is_Atom: - tex = r'\delta_{%s %s}' % (i, j) - else: - tex = r'\delta_{%s, %s}' % (i, j) - if exp: - tex = r'\left(%s\right)^{%s}' % (tex, exp) - return tex - - def _print_LeviCivita(self, expr, exp=None): - indices = list(map(self._print, expr.args)) - if all([x.is_Atom for x in expr.args]): - tex = r'\varepsilon_{%s}' % " ".join(indices) - else: - tex = r'\varepsilon_{%s}' % ", ".join(indices) - if exp: - tex = r'\left(%s\right)^{%s}' % (tex, exp) - return tex - - def _print_ProductSet(self, p): - if len(p.sets) > 1 and not has_variety(p.sets): - return self._print(p.sets[0]) + "^%d" % len(p.sets) - else: - return r" \times ".join(self._print(set) for set in p.sets) - - def _print_RandomDomain(self, d): - try: - return 'Domain: ' + self._print(d.as_boolean()) - except: - try: - return ('Domain: ' + self._print(d.symbols) + ' in ' + - self._print(d.set)) - except: - return 'Domain on ' + self._print(d.symbols) - - def _print_FiniteSet(self, s): - items = sorted(s.args, key=default_sort_key) - return self._print_set(items) - - def _print_set(self, s): - items = sorted(s, key=default_sort_key) - items = ", ".join(map(self._print, items)) - return r"\left\{%s\right\}" % items - - _print_frozenset = _print_set - - def _print_Range(self, s): - if len(s) > 4: - it = iter(s) - printset = next(it), next(it), '\ldots', s._last_element - else: - printset = tuple(s) - - return (r"\left\{" - + r", ".join(self._print(el) for el in printset) - + r"\right\}") - - def _print_Interval(self, i): - if i.start == i.end: - return r"\left\{%s\right\}" % self._print(i.start) - - else: - if i.left_open: - left = '(' - else: - left = '[' - - if i.right_open: - right = ')' - else: - right = ']' - - return r"\left%s%s, %s\right%s" % \ - (left, self._print(i.start), self._print(i.end), right) - - def _print_Union(self, u): - return r" \cup ".join([self._print(i) for i in u.args]) - - def _print_Intersection(self, u): - return r" \cap ".join([self._print(i) for i in u.args]) - - def _print_EmptySet(self, e): - return r"\emptyset" - - def _print_Naturals(self, n): - return r"\mathbb{N}" - - def _print_Integers(self, i): - return r"\mathbb{Z}" - - def _print_Reals(self, i): - return r"\mathbb{R}" - - def _print_TransformationSet(self, s): - return r"\left\{%s\; |\; %s \in %s\right\}" % ( - self._print(s.lamda.expr), - ', '.join([self._print(var) for var in s.lamda.variables]), - self._print(s.base_set)) - - def _print_FiniteField(self, expr): - return r"\mathbb{F}_{%s}" % expr.mod - - def _print_IntegerRing(self, expr): - return r"\mathbb{Z}" - - def _print_RationalField(self, expr): - return r"\mathbb{Q}" - - def _print_RealDomain(self, expr): - return r"\mathbb{R}" - - def _print_ComplexDomain(self, expr): - return r"\mathbb{C}" - - def _print_PolynomialRingBase(self, expr): - domain = self._print(expr.dom) - gens = ", ".join(map(self._print, expr.gens)) - inv = "" - if not expr.is_Poly: - inv = r"S_<^{-1}" - return r"%s%s\left[%s\right]" % (inv, domain, gens) - - def _print_FractionField(self, expr): - domain = self._print(expr.dom) - gens = ", ".join(map(self._print, expr.gens)) - return r"%s\left(%s\right)" % (domain, gens) - - def _print_Poly(self, poly): - cls = poly.__class__.__name__ - expr = self._print(poly.as_expr()) - gens = list(map(self._print, poly.gens)) - domain = "domain=%s" % self._print(poly.get_domain()) - - args = ", ".join([expr] + gens + [domain]) - if cls in accepted_latex_functions: - tex = r"\%s {\left (%s \right )}" % (cls, args) - else: - tex = r"\operatorname{%s}{\left( %s \right)}" % (cls, args) - - return tex - - def _print_RootOf(self, root): - cls = root.__class__.__name__ - expr = self._print(root.expr) - index = root.index - if cls in accepted_latex_functions: - return r"\%s {\left(%s, %d\right)}" % (cls, expr, index) - else: - return r"\operatorname{%s} {\left(%s, %d\right)}" % (cls, expr, index) - - def _print_RootSum(self, expr): - cls = expr.__class__.__name__ - args = [self._print(expr.expr)] - - if expr.fun is not S.IdentityFunction: - args.append(self._print(expr.fun)) - - if cls in accepted_latex_functions: - return r"\%s {\left(%s\right)}" % (cls, ", ".join(args)) - else: - return r"\operatorname{%s} {\left(%s\right)}" % (cls, ", ".join(args)) - - def _print_euler(self, expr): - return r"E_{%s}" % self._print(expr.args[0]) - - def _print_catalan(self, expr): - return r"C_{%s}" % self._print(expr.args[0]) - - def _print_MellinTransform(self, expr): - return r"\mathcal{M}_{%s}\left[%s\right]\left(%s\right)" % (self._print(expr.args[1]), self._print(expr.args[0]), self._print(expr.args[2])) - - def _print_InverseMellinTransform(self, expr): - return r"\mathcal{M}^{-1}_{%s}\left[%s\right]\left(%s\right)" % (self._print(expr.args[1]), self._print(expr.args[0]), self._print(expr.args[2])) - - def _print_LaplaceTransform(self, expr): - return r"\mathcal{L}_{%s}\left[%s\right]\left(%s\right)" % (self._print(expr.args[1]), self._print(expr.args[0]), self._print(expr.args[2])) - - def _print_InverseLaplaceTransform(self, expr): - return r"\mathcal{L}^{-1}_{%s}\left[%s\right]\left(%s\right)" % (self._print(expr.args[1]), self._print(expr.args[0]), self._print(expr.args[2])) - - def _print_FourierTransform(self, expr): - return r"\mathcal{F}_{%s}\left[%s\right]\left(%s\right)" % (self._print(expr.args[1]), self._print(expr.args[0]), self._print(expr.args[2])) - - def _print_InverseFourierTransform(self, expr): - return r"\mathcal{F}^{-1}_{%s}\left[%s\right]\left(%s\right)" % (self._print(expr.args[1]), self._print(expr.args[0]), self._print(expr.args[2])) - - def _print_SineTransform(self, expr): - return r"\mathcal{SIN}_{%s}\left[%s\right]\left(%s\right)" % (self._print(expr.args[1]), self._print(expr.args[0]), self._print(expr.args[2])) - - def _print_InverseSineTransform(self, expr): - return r"\mathcal{SIN}^{-1}_{%s}\left[%s\right]\left(%s\right)" % (self._print(expr.args[1]), self._print(expr.args[0]), self._print(expr.args[2])) - - def _print_CosineTransform(self, expr): - return r"\mathcal{COS}_{%s}\left[%s\right]\left(%s\right)" % (self._print(expr.args[1]), self._print(expr.args[0]), self._print(expr.args[2])) - - def _print_InverseCosineTransform(self, expr): - return r"\mathcal{COS}^{-1}_{%s}\left[%s\right]\left(%s\right)" % (self._print(expr.args[1]), self._print(expr.args[0]), self._print(expr.args[2])) - - def _print_DMP(self, p): - try: - if p.ring is not None: - # TODO incorporate order - return self._print(p.ring.to_sympy(p)) - except SympifyError: - pass - return self._print(repr(p)) - - def _print_DMF(self, p): - return self._print_DMP(p) - - def _print_Object(self, object): - return self._print(Symbol(object.name)) - - def _print_Morphism(self, morphism): - domain = self._print(morphism.domain) - codomain = self._print(morphism.codomain) - return "%s\\rightarrow %s" % (domain, codomain) - - def _print_NamedMorphism(self, morphism): - pretty_name = self._print(Symbol(morphism.name)) - pretty_morphism = self._print_Morphism(morphism) - return "%s:%s" % (pretty_name, pretty_morphism) - - def _print_IdentityMorphism(self, morphism): - from sympy.categories import NamedMorphism - return self._print_NamedMorphism(NamedMorphism( - morphism.domain, morphism.codomain, "id")) - - def _print_CompositeMorphism(self, morphism): - # All components of the morphism have names and it is thus - # possible to build the name of the composite. - component_names_list = [self._print(Symbol(component.name)) for - component in morphism.components] - component_names_list.reverse() - component_names = "\\circ ".join(component_names_list) + ":" - - pretty_morphism = self._print_Morphism(morphism) - return component_names + pretty_morphism - - def _print_Category(self, morphism): - return "\\mathbf{%s}" % self._print(Symbol(morphism.name)) - - def _print_Diagram(self, diagram): - if not diagram.premises: - # This is an empty diagram. - return self._print(S.EmptySet) - - latex_result = self._print(diagram.premises) - if diagram.conclusions: - latex_result += "\\Longrightarrow %s" % \ - self._print(diagram.conclusions) - - return latex_result - - def _print_DiagramGrid(self, grid): - latex_result = "\\begin{array}{%s}\n" % ("c" * grid.width) - - for i in range(grid.height): - for j in range(grid.width): - if grid[i, j]: - latex_result += latex(grid[i, j]) - latex_result += " " - if j != grid.width - 1: - latex_result += "& " - - if i != grid.height - 1: - latex_result += "\\\\" - latex_result += "\n" - - latex_result += "\\end{array}\n" - return latex_result - - def _print_FreeModule(self, M): - return '{%s}^{%s}' % (self._print(M.ring), self._print(M.rank)) - - def _print_FreeModuleElement(self, m): - # Print as row vector for convenience, for now. - return r"\left[ %s \right]" % ",".join( - '{' + self._print(x) + '}' for x in m) - - def _print_SubModule(self, m): - return r"\left< %s \right>" % ",".join( - '{' + self._print(x) + '}' for x in m.gens) - - def _print_ModuleImplementedIdeal(self, m): - return r"\left< %s \right>" % ",".join( - '{' + self._print(x) + '}' for [x] in m._module.gens) - - def _print_QuotientRing(self, R): - # TODO nicer fractions for few generators... - return r"\frac{%s}{%s}" % (self._print(R.ring), self._print(R.base_ideal)) - - def _print_QuotientRingElement(self, x): - return r"{%s} + {%s}" % (self._print(x.data), self._print(x.ring.base_ideal)) - - def _print_QuotientModuleElement(self, m): - return r"{%s} + {%s}" % (self._print(m.data), - self._print(m.module.killed_module)) - - def _print_QuotientModule(self, M): - # TODO nicer fractions for few generators... - return r"\frac{%s}{%s}" % (self._print(M.base), - self._print(M.killed_module)) - - def _print_MatrixHomomorphism(self, h): - return r"{%s} : {%s} \to {%s}" % (self._print(h._sympy_matrix()), - self._print(h.domain), self._print(h.codomain)) - - def _print_BaseScalarField(self, field): - string = field._coord_sys._names[field._index] - return r'\boldsymbol{\mathrm{%s}}' % self._print(Symbol(string)) - - def _print_BaseVectorField(self, field): - string = field._coord_sys._names[field._index] - return r'\partial_{%s}' % self._print(Symbol(string)) - - def _print_Differential(self, diff): - field = diff._form_field - if hasattr(field, '_coord_sys'): - string = field._coord_sys._names[field._index] - return r'\mathbb{d}%s' % self._print(Symbol(string)) - else: - return 'd(%s)' % self._print(field) - string = self._print(field) - return r'\mathbb{d}\left(%s\right)' % string - - def _print_Tr(self, p): - #Todo: Handle indices - contents = self._print(p.args[0]) - return r'\mbox{Tr}\left(%s\right)' % (contents) - -
    -
    [docs]def latex(expr, **settings): - r""" - Convert the given expression to LaTeX representation. - - >>> from sympy import latex, sin, asin, Matrix, Rational - >>> from sympy.abc import x, y, mu, tau - - >>> latex((2*tau)**Rational(7,2)) - '8 \\sqrt{2} \\tau^{\\frac{7}{2}}' - - order: Any of the supported monomial orderings (currently "lex", "grlex", or - "grevlex"), "old", and "none". This parameter does nothing for Mul objects. - Setting order to "old" uses the compatibility ordering for Add defined in - Printer. For very large expressions, set the 'order' keyword to 'none' if - speed is a concern. - - mode: Specifies how the generated code will be delimited. 'mode' can be one - of 'plain', 'inline', 'equation' or 'equation*'. If 'mode' is set to - 'plain', then the resulting code will not be delimited at all (this is the - default). If 'mode' is set to 'inline' then inline LaTeX $ $ will be used. - If 'mode' is set to 'equation' or 'equation*', the resulting code will be - enclosed in the 'equation' or 'equation*' environment (remember to import - 'amsmath' for 'equation*'), unless the 'itex' option is set. In the latter - case, the ``$$ $$`` syntax is used. - - >>> latex((2*mu)**Rational(7,2), mode='plain') - '8 \\sqrt{2} \\mu^{\\frac{7}{2}}' - - >>> latex((2*tau)**Rational(7,2), mode='inline') - '$8 \\sqrt{2} \\tau^{\\frac{7}{2}}$' - - >>> latex((2*mu)**Rational(7,2), mode='equation*') - '\\begin{equation*}8 \\sqrt{2} \\mu^{\\frac{7}{2}}\\end{equation*}' - - >>> latex((2*mu)**Rational(7,2), mode='equation') - '\\begin{equation}8 \\sqrt{2} \\mu^{\\frac{7}{2}}\\end{equation}' - - itex: Specifies if itex-specific syntax is used, including emitting ``$$ $$``. - - >>> latex((2*mu)**Rational(7,2), mode='equation', itex=True) - '$$8 \\sqrt{2} \\mu^{\\frac{7}{2}}$$' - - fold_frac_powers: Emit "^{p/q}" instead of "^{\frac{p}{q}}" for fractional - powers. - - >>> latex((2*tau)**Rational(7,2), fold_frac_powers=True) - '8 \\sqrt{2} \\tau^{7/2}' - - fold_func_brackets: Fold function brackets where applicable. - - >>> latex((2*tau)**sin(Rational(7,2))) - '\\left(2 \\tau\\right)^{\\sin{\\left (\\frac{7}{2} \\right )}}' - >>> latex((2*tau)**sin(Rational(7,2)), fold_func_brackets = True) - '\\left(2 \\tau\\right)^{\\sin {\\frac{7}{2}}}' - - mul_symbol: The symbol to use for multiplication. Can be one of None, - "ldot", "dot", or "times". - - >>> latex((2*tau)**sin(Rational(7,2)), mul_symbol="times") - '\\left(2 \\times \\tau\\right)^{\\sin{\\left (\\frac{7}{2} \\right )}}' - - inv_trig_style: How inverse trig functions should be displayed. Can be one - of "abbreviated", "full", or "power". Defaults to "abbreviated". - - >>> latex(asin(Rational(7,2))) - '\\operatorname{asin}{\\left (\\frac{7}{2} \\right )}' - >>> latex(asin(Rational(7,2)), inv_trig_style="full") - '\\arcsin{\\left (\\frac{7}{2} \\right )}' - >>> latex(asin(Rational(7,2)), inv_trig_style="power") - '\\sin^{-1}{\\left (\\frac{7}{2} \\right )}' - - mat_str: Which matrix environment string to emit. "smallmatrix", "bmatrix", - etc. Defaults to "smallmatrix". - - >>> latex(Matrix(2, 1, [x, y]), mat_str = "array") - '\\left[\\begin{array}x\\\\y\\end{array}\\right]' - - mat_delim: The delimiter to wrap around matrices. Can be one of "[", "(", - or the empty string. Defaults to "[". - - >>> latex(Matrix(2, 1, [x, y]), mat_delim="(") - '\\left(\\begin{smallmatrix}x\\\\y\\end{smallmatrix}\\right)' - - symbol_names: Dictionary of symbols and the custom strings they should be - emitted as. - - >>> latex(x**2, symbol_names={x:'x_i'}) - 'x_i^{2}' - - Besides all Basic based expressions, you can recursively - convert Python containers (lists, tuples and dicts) and - also SymPy matrices: - - >>> latex([2/x, y], mode='inline') - '$\\begin{bmatrix}\\frac{2}{x}, & y\\end{bmatrix}$' - - """ - - return LatexPrinter(settings).doprint(expr) - -
    - -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/printing/mathml.html b/dev-py3k/_modules/sympy/printing/mathml.html deleted file mode 100644 index 6af0425d724..00000000000 --- a/dev-py3k/_modules/sympy/printing/mathml.html +++ /dev/null @@ -1,608 +0,0 @@ - - - - - - - - - - sympy.printing.mathml — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.printing.mathml

    -"""
    -A MathML printer.
    -"""
    -
    -from sympy import sympify, S, Mul
    -from sympy.core.function import _coeff_isneg
    -from sympy.simplify import fraction
    -from .printer import Printer
    -from .conventions import split_super_sub
    -
    -
    -
    [docs]class MathMLPrinter(Printer): - """Prints an expression to the MathML markup language - - Whenever possible tries to use Content markup and not Presentation markup. - - References: http://www.w3.org/TR/MathML2/ - """ - printmethod = "_mathml" - _default_settings = { - "order": None, - "encoding": "utf-8" - } - - def __init__(self, settings=None): - Printer.__init__(self, settings) - from xml.dom.minidom import Document - self.dom = Document() - -
    [docs] def doprint(self, expr): - """ - Prints the expression as MathML. - """ - mathML = Printer._print(self, expr) - unistr = mathML.toxml() - xmlbstr = unistr.encode('ascii', 'xmlcharrefreplace') - res = xmlbstr.decode() - return res -
    -
    [docs] def mathml_tag(self, e): - """Returns the MathML tag for an expression.""" - translate = { - 'Add': 'plus', - 'Mul': 'times', - 'Derivative': 'diff', - 'Number': 'cn', - 'int': 'cn', - 'Pow': 'power', - 'Symbol': 'ci', - 'Integral': 'int', - 'Sum': 'sum', - 'sin': 'sin', - 'cos': 'cos', - 'tan': 'tan', - 'cot': 'cot', - 'asin': 'arcsin', - 'asinh': 'arcsinh', - 'acos': 'arccos', - 'acosh': 'arccosh', - 'atan': 'arctan', - 'atanh': 'arctanh', - 'acot': 'arccot', - 'atan2': 'arctan', - 'log': 'ln', - 'Equality': 'eq', - 'Unequality': 'neq', - 'GreaterThan': 'geq', - 'LessThan': 'leq', - 'StrictGreaterThan': 'gt', - 'StrictLessThan': 'lt', - } - - for cls in e.__class__.__mro__: - n = cls.__name__ - if n in translate: - return translate[n] - # Not found in the MRO set - n = e.__class__.__name__ - return n.lower() -
    - def _print_Mul(self, expr): - - if _coeff_isneg(expr): - x = self.dom.createElement('apply') - x.appendChild(self.dom.createElement('minus')) - x.appendChild(self._print_Mul(-expr)) - return x - - numer, denom = fraction(expr) - - if denom is not S.One: - x = self.dom.createElement('apply') - x.appendChild(self.dom.createElement('divide')) - x.appendChild(self._print(numer)) - x.appendChild(self._print(denom)) - return x - - coeff, terms = expr.as_coeff_mul() - if coeff is S.One and len(terms) == 1: - # XXX since the negative coefficient has been handled, I don't - # thing a coeff of 1 can remain - return self._print(terms[0]) - - if self.order != 'old': - terms = Mul._from_args(terms).as_ordered_factors() - - x = self.dom.createElement('apply') - x.appendChild(self.dom.createElement('times')) - if(coeff != 1): - x.appendChild(self._print(coeff)) - for term in terms: - x.appendChild(self._print(term)) - return x - - def _print_Add(self, expr, order=None): - args = self._as_ordered_terms(expr, order=order) - lastProcessed = self._print(args[0]) - plusNodes = [] - for arg in args[1:]: - if _coeff_isneg(arg): - #use minus - x = self.dom.createElement('apply') - x.appendChild(self.dom.createElement('minus')) - x.appendChild(lastProcessed) - x.appendChild(self._print(-arg)) - #invert expression since this is now minused - lastProcessed = x - if(arg == args[-1]): - plusNodes.append(lastProcessed) - else: - plusNodes.append(lastProcessed) - lastProcessed = self._print(arg) - if(arg == args[-1]): - plusNodes.append(self._print(arg)) - if len(plusNodes) == 1: - return lastProcessed - x = self.dom.createElement('apply') - x.appendChild(self.dom.createElement('plus')) - while len(plusNodes) > 0: - x.appendChild(plusNodes.pop(0)) - return x - - def _print_MatrixBase(self, m): - x = self.dom.createElement('matrix') - for i in range(m.lines): - x_r = self.dom.createElement('matrixrow') - for j in range(m.cols): - x_r.appendChild(self._print(m[i, j])) - x.appendChild(x_r) - return x - - def _print_Rational(self, e): - if e.q == 1: - #don't divide - x = self.dom.createElement('cn') - x.appendChild(self.dom.createTextNode(str(e.p))) - return x - x = self.dom.createElement('apply') - x.appendChild(self.dom.createElement('divide')) - #numerator - xnum = self.dom.createElement('cn') - xnum.appendChild(self.dom.createTextNode(str(e.p))) - #denomenator - xdenom = self.dom.createElement('cn') - xdenom.appendChild(self.dom.createTextNode(str(e.q))) - x.appendChild(xnum) - x.appendChild(xdenom) - return x - - def _print_Limit(self, e): - x = self.dom.createElement('apply') - x.appendChild(self.dom.createElement(self.mathml_tag(e))) - - x_1 = self.dom.createElement('bvar') - x_2 = self.dom.createElement('lowlimit') - x_1.appendChild(self._print(e.args[1])) - x_2.appendChild(self._print(e.args[2])) - - x.appendChild(x_1) - x.appendChild(x_2) - x.appendChild(self._print(e.args[0])) - return x - - def _print_ImaginaryUnit(self, e): - return self.dom.createElement('imaginaryi') - - def _print_EulerGamma(self, e): - return self.dom.createElement('eulergamma') - - def _print_GoldenRatio(self, e): - """We use unicode #x3c6 for Greek letter phi as defined here - http://www.w3.org/2003/entities/2007doc/isogrk1.html""" - x = self.dom.createElement('cn') - x.appendChild(self.dom.createTextNode("\u03c6")) - return x - - def _print_Exp1(self, e): - return self.dom.createElement('exponentiale') - - def _print_Pi(self, e): - return self.dom.createElement('pi') - - def _print_Infinity(self, e): - return self.dom.createElement('infinity') - - def _print_Negative_Infinity(self, e): - x = self.dom.createElement('apply') - x.appendChild(self.dom.createElement('minus')) - x.appendChild(self.dom.createElement('infinity')) - return x - - def _print_Integral(self, e): - def lime_recur(limits): - x = self.dom.createElement('apply') - x.appendChild(self.dom.createElement(self.mathml_tag(e))) - bvar_elem = self.dom.createElement('bvar') - bvar_elem.appendChild(self._print(limits[0][0])) - x.appendChild(bvar_elem) - - if len(limits[0]) == 3: - low_elem = self.dom.createElement('lowlimit') - low_elem.appendChild(self._print(limits[0][1])) - x.appendChild(low_elem) - up_elem = self.dom.createElement('uplimit') - up_elem.appendChild(self._print(limits[0][2])) - x.appendChild(up_elem) - if len(limits[0]) == 2: - up_elem = self.dom.createElement('uplimit') - up_elem.appendChild(self._print(limits[0][1])) - x.appendChild(up_elem) - if len(limits) == 1: - x.appendChild(self._print(e.function)) - else: - x.appendChild(lime_recur(limits[1:])) - return x - - limits = list(e.limits) - limits.reverse() - return lime_recur(limits) - - def _print_Sum(self, e): - # Printer can be shared because Sum and Integral have the - # same internal representation. - return self._print_Integral(e) - - def _print_Symbol(self, sym): - ci = self.dom.createElement(self.mathml_tag(sym)) - - def join(items): - if len(items) > 1: - mrow = self.dom.createElement('mml:mrow') - for i, item in enumerate(items): - if i > 0: - mo = self.dom.createElement('mml:mo') - mo.appendChild(self.dom.createTextNode(" ")) - mrow.appendChild(mo) - mi = self.dom.createElement('mml:mi') - mi.appendChild(self.dom.createTextNode(item)) - mrow.appendChild(mi) - return mrow - else: - mi = self.dom.createElement('mml:mi') - mi.appendChild(self.dom.createTextNode(items[0])) - return mi - - # translate name, supers and subs to unicode characters - # taken from http://www.w3.org/2003/entities/2007doc/isogrk1.html - unitr = { - 'Alpha': '\u0391', 'Beta': '\u0392', - 'Gamma': '\u0393', 'Delta': '\u0394', - 'Epsilon': '\u0395', 'Zeta': '\u0396', - 'Eta': '\u0397', 'Theta': '\u0398', - 'Iota': '\u0399', 'Kappa': '\u039A', - 'Lambda': '\u039B', 'Mu': '\u039C', - 'Nu': '\u039D', 'Xi': '\u039E', - 'Omicron': '\u039F', 'Pi': '\u03A0', - 'Rho': '\u03A1', 'Sigma': '\u03A3', - 'Tau': '\u03A4', 'Upsilon': '\u03A5', - 'Phi': '\u03A6', 'Chi': '\u03A7', - 'Psi': '\u03A8', 'Omega': '\u03A9', - 'alpha': '\u03B1', 'beta': '\u03B2', - 'gamma': '\u03B3', 'delta': '\u03B4', - 'epsilon': '\u03B5', 'zeta': '\u03B6', - 'eta': '\u03B7', 'theta': '\u03B8', - 'iota': '\u03B9', 'kappa': '\u03BA', - 'lambda': '\u03BB', 'mu': '\u03BC', - 'nu': '\u03BD', 'xi': '\u03BE', - 'omicron': '\u03BF', 'pi': '\u03C0', - 'rho': '\u03C1', 'varsigma': '\u03C2', - 'sigma': '\u03C3', 'tau': '\u03C4', - 'upsilon': '\u03C5', 'phi': '\u03C6', - 'chi': '\u03C7', 'psi': '\u03C8', - 'omega': '\u03C9', - } - - def translate(s): - if s in unitr: - return unitr[s] - else: - return s - - name, supers, subs = split_super_sub(sym.name) - name = translate(name) - supers = [translate(sup) for sup in supers] - subs = [translate(sub) for sub in subs] - - mname = self.dom.createElement('mml:mi') - mname.appendChild(self.dom.createTextNode(name)) - if len(supers) == 0: - if len(subs) == 0: - ci.appendChild(self.dom.createTextNode(name)) - else: - msub = self.dom.createElement('mml:msub') - msub.appendChild(mname) - msub.appendChild(join(subs)) - ci.appendChild(msub) - else: - if len(subs) == 0: - msup = self.dom.createElement('mml:msup') - msup.appendChild(mname) - msup.appendChild(join(supers)) - ci.appendChild(msup) - else: - msubsup = self.dom.createElement('mml:msubsup') - msubsup.appendChild(mname) - msubsup.appendChild(join(subs)) - msubsup.appendChild(join(supers)) - ci.appendChild(msubsup) - return ci - - def _print_Pow(self, e): - #Here we use root instead of power if the exponent is the reciprocal of an integer - if e.exp.is_Rational and e.exp.p == 1: - x = self.dom.createElement('apply') - x.appendChild(self.dom.createElement('root')) - if e.exp.q != 2: - xmldeg = self.dom.createElement('degree') - xmlci = self.dom.createElement('ci') - xmlci.appendChild(self.dom.createTextNode(str(e.exp.q))) - xmldeg.appendChild(xmlci) - x.appendChild(xmldeg) - x.appendChild(self._print(e.base)) - return x - - x = self.dom.createElement('apply') - x_1 = self.dom.createElement(self.mathml_tag(e)) - x.appendChild(x_1) - x.appendChild(self._print(e.base)) - x.appendChild(self._print(e.exp)) - return x - - def _print_Number(self, e): - x = self.dom.createElement(self.mathml_tag(e)) - x.appendChild(self.dom.createTextNode(str(e))) - return x - - def _print_Derivative(self, e): - x = self.dom.createElement('apply') - x.appendChild(self.dom.createElement(self.mathml_tag(e))) - - x_1 = self.dom.createElement('bvar') - for sym in e.variables: - x_1.appendChild(self._print(sym)) - - x.appendChild(x_1) - x.appendChild(self._print(e.expr)) - return x - - def _print_Function(self, e): - x = self.dom.createElement("apply") - x.appendChild(self.dom.createElement(self.mathml_tag(e))) - for arg in e.args: - x.appendChild(self._print(arg)) - return x - - def _print_Basic(self, e): - x = self.dom.createElement(self.mathml_tag(e)) - for arg in e: - x.appendChild(self._print(arg)) - return x - - def _print_AssocOp(self, e): - x = self.dom.createElement('apply') - x_1 = self.dom.createElement(self.mathml_tag(e)) - x.appendChild(x_1) - for arg in e.args: - x.appendChild(self._print(arg)) - return x - - def _print_Relational(self, e): - x = self.dom.createElement('apply') - x.appendChild(self.dom.createElement(self.mathml_tag(e))) - x.appendChild(self._print(e.lhs)) - x.appendChild(self._print(e.rhs)) - return x - - def _print_list(self, seq): - """MathML reference for the <list> element: - http://www.w3.org/TR/MathML2/chapter4.html#contm.list""" - dom_element = self.dom.createElement('list') - for item in seq: - dom_element.appendChild(self._print(item)) - return dom_element - - def _print_int(self, p): - dom_element = self.dom.createElement(self.mathml_tag(p)) - dom_element.appendChild(self.dom.createTextNode(str(p))) - return dom_element - - def apply_patch(self): - # Applying the patch of xml.dom.minidom bug - # Date: 2011-11-18 - # Description: http://ronrothman.com/public/leftbraned/xml-dom-minidom-\ - # toprettyxml-and-silly-whitespace/#best-solution - # Issue: http://bugs.python.org/issue4147 - # Patch: http://hg.python.org/cpython/rev/7262f8f276ff/ - - from xml.dom.minidom import Element, Text, Node, _write_data - - def writexml(self, writer, indent="", addindent="", newl=""): - # indent = current indentation - # addindent = indentation to add to higher levels - # newl = newline string - writer.write(indent + "<" + self.tagName) - - attrs = self._get_attributes() - a_names = list(attrs.keys()) - a_names.sort() - - for a_name in a_names: - writer.write(" %s=\"" % a_name) - _write_data(writer, attrs[a_name].value) - writer.write("\"") - if self.childNodes: - writer.write(">") - if (len(self.childNodes) == 1 and - self.childNodes[0].nodeType == Node.TEXT_NODE): - self.childNodes[0].writexml(writer, '', '', '') - else: - writer.write(newl) - for node in self.childNodes: - node.writexml( - writer, indent + addindent, addindent, newl) - writer.write(indent) - writer.write("</%s>%s" % (self.tagName, newl)) - else: - writer.write("/>%s" % (newl)) - self._Element_writexml_old = Element.writexml - Element.writexml = writexml - - def writexml(self, writer, indent="", addindent="", newl=""): - _write_data(writer, "%s%s%s" % (indent, self.data, newl)) - self._Text_writexml_old = Text.writexml - Text.writexml = writexml - - def restore_patch(self): - from xml.dom.minidom import Element, Text - Element.writexml = self._Element_writexml_old - Text.writexml = self._Text_writexml_old - -
    -
    [docs]def mathml(expr, **settings): - """Returns the MathML representation of expr""" - return MathMLPrinter(settings).doprint(expr) - -
    - -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/printing/precedence.html b/dev-py3k/_modules/sympy/printing/precedence.html deleted file mode 100644 index f5048fb62c9..00000000000 --- a/dev-py3k/_modules/sympy/printing/precedence.html +++ /dev/null @@ -1,209 +0,0 @@ - - - - - - - - - - sympy.printing.precedence — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.printing.precedence

    -"""A module providing information about the necessity of brackets"""
    -
    -from sympy.core.function import _coeff_isneg
    -
    -# Default precedence values for some basic types
    -PRECEDENCE = {
    -    "Lambda": 1,
    -    "Relational": 20,
    -    "Or": 20,
    -    "And": 30,
    -    "Add": 40,
    -    "Mul": 50,
    -    "Pow": 60,
    -    "Not": 100,
    -    "Atom": 1000
    -}
    -
    -# A dictionary assigning precedence values to certain classes. These values are
    -# treated like they were inherited, so not every single class has to be named
    -# here.
    -PRECEDENCE_VALUES = {
    -    "Or": PRECEDENCE["Or"],
    -    "And": PRECEDENCE["And"],
    -    "Add": PRECEDENCE["Add"],
    -    "Pow": PRECEDENCE["Pow"],
    -    "Relational": PRECEDENCE["Relational"],
    -    "Sub": PRECEDENCE["Add"],
    -    "Not": PRECEDENCE["Not"],
    -    "factorial": PRECEDENCE["Pow"],
    -    "factorial2": PRECEDENCE["Pow"],
    -    "NegativeInfinity": PRECEDENCE["Add"],
    -    "MatAdd": PRECEDENCE["Add"],
    -    "MatMul": PRECEDENCE["Mul"],
    -    "HadamardProduct": PRECEDENCE["Mul"]
    -}
    -
    -# Sometimes it's not enough to assign a fixed precedence value to a
    -# class. Then a function can be inserted in this dictionary that takes
    -# an instance of this class as argument and returns the appropriate
    -# precedence value.
    -
    -# Precedence functions
    -
    -
    -def precedence_Mul(item):
    -    if _coeff_isneg(item):
    -        return PRECEDENCE["Add"]
    -    return PRECEDENCE["Mul"]
    -
    -
    -def precedence_Rational(item):
    -    if item.p < 0:
    -        return PRECEDENCE["Add"]
    -    return PRECEDENCE["Mul"]
    -
    -
    -def precedence_Integer(item):
    -    if item.p < 0:
    -        return PRECEDENCE["Add"]
    -    return PRECEDENCE["Atom"]
    -
    -
    -def precedence_Float(item):
    -    if item < 0:
    -        return PRECEDENCE["Add"]
    -    return PRECEDENCE["Atom"]
    -
    -PRECEDENCE_FUNCTIONS = {
    -    "Integer": precedence_Integer,
    -    "Mul": precedence_Mul,
    -    "Rational": precedence_Rational,
    -    "Float": precedence_Float,
    -}
    -
    -
    -
    [docs]def precedence(item): - """ - Returns the precedence of a given object. - """ - if hasattr(item, "precedence"): - return item.precedence - try: - mro = item.__class__.__mro__ - except AttributeError: - return PRECEDENCE["Atom"] - for i in mro: - n = i.__name__ - if n in PRECEDENCE_FUNCTIONS: - return PRECEDENCE_FUNCTIONS[n](item) - elif n in PRECEDENCE_VALUES: - return PRECEDENCE_VALUES[n] - return PRECEDENCE["Atom"]
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/printing/pretty/pretty.html b/dev-py3k/_modules/sympy/printing/pretty/pretty.html deleted file mode 100644 index be4b13ff18a..00000000000 --- a/dev-py3k/_modules/sympy/printing/pretty/pretty.html +++ /dev/null @@ -1,1802 +0,0 @@ - - - - - - - - - - sympy.printing.pretty.pretty — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.printing.pretty.pretty

    -from sympy.core import S, C
    -from sympy.core.function import _coeff_isneg
    -from sympy.utilities import group
    -from sympy.utilities.iterables import has_variety
    -from sympy.core.sympify import SympifyError
    -
    -from sympy.printing.printer import Printer
    -from sympy.printing.str import sstr
    -
    -from .stringpict import prettyForm, stringPict
    -from .pretty_symbology import xstr, hobj, vobj, xobj, xsym, pretty_symbol, \
    -    pretty_atom, pretty_use_unicode, pretty_try_use_unicode, greek, U, \
    -    annotated
    -
    -from sympy.utilities import default_sort_key
    -
    -# rename for usage from outside
    -pprint_use_unicode = pretty_use_unicode
    -pprint_try_use_unicode = pretty_try_use_unicode
    -
    -
    -
    [docs]class PrettyPrinter(Printer): - """Printer, which converts an expression into 2D ASCII-art figure.""" - printmethod = "_pretty" - - _default_settings = { - "order": None, - "full_prec": "auto", - "use_unicode": None, - "wrap_line": True, - "num_columns": None, - } - - def __init__(self, settings=None): - Printer.__init__(self, settings) - self.emptyPrinter = lambda x: prettyForm(xstr(x)) - - @property - def _use_unicode(self): - if self._settings['use_unicode']: - return True - else: - return pretty_use_unicode() - - def doprint(self, expr): - return self._print(expr).render(**self._settings) - - # empty op so _print(stringPict) returns the same - def _print_stringPict(self, e): - return e - - def _print_basestring(self, e): - return prettyForm(e) - - def _print_atan2(self, e): - pform = prettyForm(*self._print_seq(e.args).parens()) - pform = prettyForm(*pform.left('atan2')) - return pform - - def _print_Symbol(self, e): - symb = pretty_symbol(e.name) - return prettyForm(symb) - - def _print_Float(self, e): - # we will use StrPrinter's Float printer, but we need to handle the - # full_prec ourselves, according to the self._print_level - full_prec = self._settings["full_prec"] - if full_prec == "auto": - full_prec = self._print_level == 1 - return prettyForm(sstr(e, full_prec=full_prec)) - - def _print_Atom(self, e): - try: - # print atoms like Exp1 or Pi - return prettyForm(pretty_atom(e.__class__.__name__)) - except KeyError: - return self.emptyPrinter(e) - - # Infinity inherits from Number, so we have to override _print_XXX order - _print_Infinity = _print_Atom - _print_NegativeInfinity = _print_Atom - _print_EmptySet = _print_Atom - _print_Naturals = _print_Atom - _print_Integers = _print_Atom - _print_Reals = _print_Atom - - def _print_subfactorial(self, e): - x = e.args[0] - pform = self._print(x) - # Add parentheses if needed - if not ((x.is_Integer and x.is_nonnegative) or x.is_Symbol): - pform = prettyForm(*pform.parens()) - pform = prettyForm(*pform.left('!')) - return pform - - def _print_factorial(self, e): - x = e.args[0] - pform = self._print(x) - # Add parentheses if needed - if not ((x.is_Integer and x.is_nonnegative) or x.is_Symbol): - pform = prettyForm(*pform.parens()) - pform = prettyForm(*pform.right('!')) - return pform - - def _print_factorial2(self, e): - x = e.args[0] - pform = self._print(x) - # Add parentheses if needed - if not ((x.is_Integer and x.is_nonnegative) or x.is_Symbol): - pform = prettyForm(*pform.parens()) - pform = prettyForm(*pform.right('!!')) - return pform - - def _print_binomial(self, e): - n, k = e.args - - n_pform = self._print(n) - k_pform = self._print(k) - - bar = ' '*max(n_pform.width(), k_pform.width()) - - pform = prettyForm(*k_pform.above(bar)) - pform = prettyForm(*pform.above(n_pform)) - pform = prettyForm(*pform.parens('(', ')')) - - pform.baseline = (pform.baseline + 1)//2 - - return pform - - def _print_Relational(self, e): - op = prettyForm(' ' + xsym(e.rel_op) + ' ') - - l = self._print(e.lhs) - r = self._print(e.rhs) - pform = prettyForm(*stringPict.next(l, op, r)) - return pform - - def _print_Not(self, e): - if self._use_unicode: - arg = e.args[0] - pform = self._print(arg) - - if arg.is_Boolean and not arg.is_Not: - pform = prettyForm(*pform.parens()) - - return prettyForm(*pform.left("\u00ac ")) - else: - return self._print_Function(e) - - def __print_Boolean(self, e, char, sort=True): - args = e.args - if sort: - args = sorted(e.args, key=default_sort_key) - arg = args[0] - pform = self._print(arg) - - if arg.is_Boolean and not arg.is_Not: - pform = prettyForm(*pform.parens()) - - for arg in args[1:]: - pform_arg = self._print(arg) - - if arg.is_Boolean and not arg.is_Not: - pform_arg = prettyForm(*pform_arg.parens()) - - pform = prettyForm(*pform.right(' %s ' % char)) - pform = prettyForm(*pform.right(pform_arg)) - - return pform - - def _print_And(self, e): - if self._use_unicode: - return self.__print_Boolean(e, "\u2227") - else: - return self._print_Function(e, sort=True) - - def _print_Or(self, e): - if self._use_unicode: - return self.__print_Boolean(e, "\u2228") - else: - return self._print_Function(e, sort=True) - - def _print_Xor(self, e): - if self._use_unicode: - return self.__print_Boolean(e, "\u22bb") - else: - return self._print_Function(e, sort=True) - - def _print_Nand(self, e): - if self._use_unicode: - return self.__print_Boolean(e, "\u22bc") - else: - return self._print_Function(e, sort=True) - - def _print_Nor(self, e): - if self._use_unicode: - return self.__print_Boolean(e, "\u22bd") - else: - return self._print_Function(e, sort=True) - - def _print_Implies(self, e): - if self._use_unicode: - return self.__print_Boolean(e, "\u2192", sort=False) - else: - return self._print_Function(e) - - def _print_Equivalent(self, e): - if self._use_unicode: - return self.__print_Boolean(e, "\u2261") - else: - return self._print_Function(e, sort=True) - - def _print_conjugate(self, e): - pform = self._print(e.args[0]) - return prettyForm( *pform.above( hobj('_', pform.width())) ) - - def _print_Abs(self, e): - pform = self._print(e.args[0]) - pform = prettyForm(*pform.parens('|', '|')) - return pform - - def _print_floor(self, e): - if self._use_unicode: - pform = self._print(e.args[0]) - pform = prettyForm(*pform.parens('lfloor', 'rfloor')) - return pform - else: - return self._print_Function(e) - - def _print_ceiling(self, e): - if self._use_unicode: - pform = self._print(e.args[0]) - pform = prettyForm(*pform.parens('lceil', 'rceil')) - return pform - else: - return self._print_Function(e) - - def _print_Derivative(self, deriv): - # XXX use U('PARTIAL DIFFERENTIAL') here ? - syms = list(reversed(deriv.variables)) - x = None - - for sym, num in group(syms, multiple=False): - s = self._print(sym) - ds = prettyForm(*s.left('d')) - - if num > 1: - ds = ds**prettyForm(str(num)) - - if x is None: - x = ds - else: - x = prettyForm(*x.right(' ')) - x = prettyForm(*x.right(ds)) - - f = prettyForm( - binding=prettyForm.FUNC, *self._print(deriv.expr).parens()) - - pform = prettyForm('d') - - if len(syms) > 1: - pform = pform**prettyForm(str(len(syms))) - - pform = prettyForm(*pform.below(stringPict.LINE, x)) - pform.baseline = pform.baseline + 1 - pform = prettyForm(*stringPict.next(pform, f)) - - return pform - - def _print_Cycle(self, dc): - from sympy.combinatorics.permutations import Permutation - return self._print_tuple(Permutation(dc.as_list()).cyclic_form) - - def _print_PDF(self, pdf): - lim = self._print(pdf.pdf.args[0]) - lim = prettyForm(*lim.right(', ')) - lim = prettyForm(*lim.right(self._print(pdf.domain[0]))) - lim = prettyForm(*lim.right(', ')) - lim = prettyForm(*lim.right(self._print(pdf.domain[1]))) - lim = prettyForm(*lim.parens()) - - f = self._print(pdf.pdf.args[1]) - f = prettyForm(*f.right(', ')) - f = prettyForm(*f.right(lim)) - f = prettyForm(*f.parens()) - - pform = prettyForm('PDF') - pform = prettyForm(*pform.right(f)) - return pform - - def _print_Integral(self, integral): - f = integral.function - - # Add parentheses if arg involves addition of terms and - # create a pretty form for the argument - prettyF = self._print(f) - # XXX generalize parens - if f.is_Add: - prettyF = prettyForm(*prettyF.parens()) - - # dx dy dz ... - arg = prettyF - for x in integral.limits: - prettyArg = self._print(x[0]) - # XXX qparens (parens if needs-parens) - if prettyArg.width() > 1: - prettyArg = prettyForm(*prettyArg.parens()) - - arg = prettyForm(*arg.right(' d', prettyArg)) - - # \int \int \int ... - firstterm = True - s = None - for lim in integral.limits: - x = lim[0] - # Create bar based on the height of the argument - h = arg.height() - H = h + 2 - - # XXX hack! - ascii_mode = not self._use_unicode - if ascii_mode: - H += 2 - - vint = vobj('int', H) - - # Construct the pretty form with the integral sign and the argument - pform = prettyForm(vint) - #pform.baseline = pform.height()//2 # vcenter - pform.baseline = arg.baseline + ( - H - h)//2 # covering the whole argument - - if len(lim) > 1: - # Create pretty forms for endpoints, if definite integral. - # Do not print empty endpoints. - if len(lim) == 2: - prettyA = prettyForm("") - prettyB = self._print(lim[1]) - if len(lim) == 3: - prettyA = self._print(lim[1]) - prettyB = self._print(lim[2]) - - if ascii_mode: # XXX hack - # Add spacing so that endpoint can more easily be - # identified with the correct integral sign - spc = max(1, 3 - prettyB.width()) - prettyB = prettyForm(*prettyB.left(' ' * spc)) - - spc = max(1, 4 - prettyA.width()) - prettyA = prettyForm(*prettyA.right(' ' * spc)) - - pform = prettyForm(*pform.above(prettyB)) - pform = prettyForm(*pform.below(prettyA)) - - #if ascii_mode: # XXX hack - # # too much vspace beetween \int and argument - # # but I left it as is - # pform = prettyForm(*pform.right(' ')) - - if not ascii_mode: # XXX hack - pform = prettyForm(*pform.right(' ')) - - if firstterm: - s = pform # first term - firstterm = False - else: - s = prettyForm(*s.left(pform)) - - pform = prettyForm(*arg.left(s)) - return pform - - def _print_Product(self, expr): - func = expr.term - pretty_func = self._print(func) - - horizontal_chr = xobj('_', 1) - corner_chr = xobj('_', 1) - vertical_chr = xobj('|', 1) - - if self._use_unicode: - # use unicode corners - horizontal_chr = xobj('-', 1) - corner_chr = '\u252c' - - func_height = pretty_func.height() - - first = True - max_upper = 0 - sign_height = 0 - - for lim in expr.limits: - width = (func_height + 2) * 5 // 3 - 2 - sign_lines = [] - sign_lines.append(corner_chr + (horizontal_chr*width) + corner_chr) - for i in range(func_height + 1): - sign_lines.append(vertical_chr + (' '*width) + vertical_chr) - - pretty_sign = stringPict('') - pretty_sign = prettyForm(*pretty_sign.stack(*sign_lines)) - - pretty_upper = self._print(lim[2]) - pretty_lower = self._print(C.Equality(lim[0], lim[1])) - - max_upper = max(max_upper, pretty_upper.height()) - - if first: - sign_height = pretty_sign.height() - - pretty_sign = prettyForm(*pretty_sign.above(pretty_upper)) - pretty_sign = prettyForm(*pretty_sign.below(pretty_lower)) - - if first: - pretty_func.baseline = 0 - first = False - - height = pretty_sign.height() - padding = stringPict('') - padding = prettyForm(*padding.stack(*[' ']*(height - 1))) - pretty_sign = prettyForm(*pretty_sign.right(padding)) - - pretty_func = prettyForm(*pretty_sign.right(pretty_func)) - - #pretty_func.baseline = 0 - - pretty_func.baseline = max_upper + sign_height//2 - return pretty_func - - def _print_Sum(self, expr): - ascii_mode = not self._use_unicode - - def asum(hrequired, lower, upper, use_ascii): - def adjust(s, wid=None, how='<^>'): - if not wid or len(s) > wid: - return s - need = wid - len(s) - if how == '<^>' or how == "<" or how not in list('<^>'): - return s + ' '*need - half = need//2 - lead = ' '*half - if how == ">": - return " "*need + s - return lead + s + ' '*(need - len(lead)) - - h = max(hrequired, 2) - d = h//2 - w = d + 1 - more = hrequired % 2 - - lines = [] - if use_ascii: - lines.append("_"*(w) + ' ') - lines.append("\%s`" % (' '*(w - 1))) - for i in range(1, d): - lines.append('%s\\%s' % (' '*i, ' '*(w - i))) - if more: - lines.append('%s)%s' % (' '*(d), ' '*(w - d))) - for i in reversed(list(range(1, d))): - lines.append('%s/%s' % (' '*i, ' '*(w - i))) - lines.append("/" + "_"*(w - 1) + ',') - return d, h + more, lines, 0 - else: - w = w + more - d = d + more - vsum = vobj('sum', 4) - lines.append("_"*(w)) - for i in range(0, d): - lines.append('%s%s%s' % (' '*i, vsum[2], ' '*(w - i - 1))) - for i in reversed(list(range(0, d))): - lines.append('%s%s%s' % (' '*i, vsum[4], ' '*(w - i - 1))) - lines.append(vsum[8]*(w)) - return d, h + 2*more, lines, more - - f = expr.function - - prettyF = self._print(f) - - if f.is_Add: # add parens - prettyF = prettyForm(*prettyF.parens()) - - H = prettyF.height() + 2 - - # \sum \sum \sum ... - first = True - max_upper = 0 - sign_height = 0 - - for lim in expr.limits: - if len(lim) == 3: - prettyUpper = self._print(lim[2]) - prettyLower = self._print(C.Equality(lim[0], lim[1])) - elif len(lim) == 2: - prettyUpper = self._print("") - prettyLower = self._print(C.Equality(lim[0], lim[1])) - elif len(lim) == 1: - prettyUpper = self._print("") - prettyLower = self._print(lim[0]) - - max_upper = max(max_upper, prettyUpper.height()) - - # Create sum sign based on the height of the argument - d, h, slines, adjustment = asum( - H, prettyLower.width(), prettyUpper.width(), ascii_mode) - prettySign = stringPict('') - prettySign = prettyForm(*prettySign.stack(*slines)) - - if first: - sign_height = prettySign.height() - - prettySign = prettyForm(*prettySign.above(prettyUpper)) - prettySign = prettyForm(*prettySign.below(prettyLower)) - - if first: - # change F baseline so it centers on the sign - prettyF.baseline -= d - (prettyF.height()//2 - - prettyF.baseline) - adjustment - first = False - - # put padding to the right - pad = stringPict('') - pad = prettyForm(*pad.stack(*[' ']*h)) - prettySign = prettyForm(*prettySign.right(pad)) - # put the present prettyF to the right - prettyF = prettyForm(*prettySign.right(prettyF)) - - prettyF.baseline = max_upper + sign_height//2 - return prettyF - - def _print_Limit(self, l): - # XXX we do not print dir ... - e, z, z0, dir = l.args - - E = self._print(e) - Lim = prettyForm('lim') - - LimArg = self._print(z) - LimArg = prettyForm(*LimArg.right('->')) - LimArg = prettyForm(*LimArg.right(self._print(z0))) - - Lim = prettyForm(*Lim.below(LimArg)) - Lim = prettyForm(*Lim.right(E)) - - return Lim - - def _print_matrix_contents(self, e): - """ - This method factors out what is essentially grid printing. - """ - M = e # matrix - Ms = {} # i,j -> pretty(M[i,j]) - for i in range(M.rows): - for j in range(M.cols): - Ms[i, j] = self._print(M[i, j]) - - # h- and v- spacers - hsep = 2 - vsep = 1 - - # max width for columns - maxw = [-1] * M.cols - - for j in range(M.cols): - maxw[j] = max([Ms[i, j].width() for i in range(M.rows)] or [0]) - - # drawing result - D = None - - for i in range(M.rows): - - D_row = None - for j in range(M.cols): - s = Ms[i, j] - - # reshape s to maxw - # XXX this should be generalized, and go to stringPict.reshape ? - assert s.width() <= maxw[j] - - # hcenter it, +0.5 to the right 2 - # ( it's better to align formula starts for say 0 and r ) - # XXX this is not good in all cases -- maybe introduce vbaseline? - wdelta = maxw[j] - s.width() - wleft = wdelta // 2 - wright = wdelta - wleft - - s = prettyForm(*s.right(' '*wright)) - s = prettyForm(*s.left(' '*wleft)) - - # we don't need vcenter cells -- this is automatically done in - # a pretty way because when their baselines are taking into - # account in .right() - - if D_row is None: - D_row = s # first box in a row - continue - - D_row = prettyForm(*D_row.right(' '*hsep)) # h-spacer - D_row = prettyForm(*D_row.right(s)) - - if D is None: - D = D_row # first row in a picture - continue - - # v-spacer - for _ in range(vsep): - D = prettyForm(*D.below(' ')) - - D = prettyForm(*D.below(D_row)) - - if D is None: - D = prettyForm('') # Empty Matrix - - return D - - def _print_MatrixBase(self, e): - D = self._print_matrix_contents(e) - D = prettyForm(*D.parens('[', ']')) - return D - _print_ImmutableMatrix = _print_MatrixBase - _print_Matrix = _print_MatrixBase - - def _print_Transpose(self, expr): - pform = self._print(expr.arg) - from sympy.matrices import MatrixSymbol - if not isinstance(expr.arg, MatrixSymbol): - pform = prettyForm(*pform.parens()) - pform = pform**(prettyForm('T')) - return pform - - def _print_Adjoint(self, expr): - pform = self._print(expr.arg) - if self._use_unicode: - dag = prettyForm('\u2020') - else: - dag = prettyForm('+') - from sympy.matrices import MatrixSymbol - if not isinstance(expr.arg, MatrixSymbol): - pform = prettyForm(*pform.parens()) - pform = pform**dag - return pform - - def _print_BlockMatrix(self, B): - if B.blocks.shape == (1, 1): - return self._print(B.blocks[0, 0]) - return self._print(B.blocks) - - def _print_MatAdd(self, expr): - return self._print_seq(expr.args, None, None, ' + ') - - def _print_MatMul(self, expr): - args = list(expr.args) - from sympy import Add, MatAdd, HadamardProduct - for i, a in enumerate(args): - if (isinstance(a, (Add, MatAdd, HadamardProduct)) - and len(expr.args) > 1): - args[i] = prettyForm(*self._print(a).parens()) - else: - args[i] = self._print(a) - - return prettyForm.__mul__(*args) - - def _print_MatPow(self, expr): - pform = self._print(expr.base) - from sympy.matrices import MatrixSymbol - if not isinstance(expr.base, MatrixSymbol): - pform = prettyForm(*pform.parens()) - pform = pform**(self._print(expr.exp)) - return pform - - def _print_HadamardProduct(self, expr): - from sympy import MatAdd, MatMul - if self._use_unicode: - delim = pretty_atom('Ring') - else: - delim = '.*' - return self._print_seq(expr.args, None, None, delim, - parenthesize=lambda x: isinstance(x, (MatAdd, MatMul))) - - _print_MatrixSymbol = _print_Symbol - - def _print_FunctionMatrix(self, X): - D = self._print(X.lamda.expr) - D = prettyForm(*D.parens('[', ']')) - return D - - def _print_Piecewise(self, pexpr): - - P = {} - for n, ec in enumerate(pexpr.args): - P[n, 0] = self._print(ec.expr) - if ec.cond is True: - P[n, 1] = prettyForm('otherwise') - else: - P[n, 1] = prettyForm( - *prettyForm('for ').right(self._print(ec.cond))) - hsep = 2 - vsep = 1 - len_args = len(pexpr.args) - - # max widths - maxw = [max([P[i, j].width() for i in range(len_args)]) - for j in range(2)] - - # FIXME: Refactor this code and matrix into some tabular environment. - # drawing result - D = None - - for i in range(len_args): - D_row = None - for j in range(2): - p = P[i, j] - assert p.width() <= maxw[j] - - wdelta = maxw[j] - p.width() - wleft = wdelta // 2 - wright = wdelta - wleft - - p = prettyForm(*p.right(' '*wright)) - p = prettyForm(*p.left(' '*wleft)) - - if D_row is None: - D_row = p - continue - - D_row = prettyForm(*D_row.right(' '*hsep)) # h-spacer - D_row = prettyForm(*D_row.right(p)) - if D is None: - D = D_row # first row in a picture - continue - - # v-spacer - for _ in range(vsep): - D = prettyForm(*D.below(' ')) - - D = prettyForm(*D.below(D_row)) - - D = prettyForm(*D.parens('{', '')) - return D - - def _hprint_vec(self, v): - D = None - - for a in v: - p = a - if D is None: - D = p - else: - D = prettyForm(*D.right(', ')) - D = prettyForm(*D.right(p)) - if D is None: - D = stringPict(' ') - - return D - - def _hprint_vseparator(self, p1, p2): - tmp = prettyForm(*p1.right(p2)) - sep = stringPict(vobj('|', tmp.height()), baseline=tmp.baseline) - return prettyForm(*p1.right(sep, p2)) - - def _print_hyper(self, e): - # FIXME refactor Matrix, Piecewise, and this into a tabular environment - ap = [self._print(a) for a in e.ap] - bq = [self._print(b) for b in e.bq] - - P = self._print(e.argument) - P.baseline = P.height()//2 - - # Drawing result - first create the ap, bq vectors - D = None - for v in [ap, bq]: - D_row = self._hprint_vec(v) - if D is None: - D = D_row # first row in a picture - else: - D = prettyForm(*D.below(' ')) - D = prettyForm(*D.below(D_row)) - - # make sure that the argument `z' is centred vertically - D.baseline = D.height()//2 - - # insert horizontal separator - P = prettyForm(*P.left(' ')) - D = prettyForm(*D.right(' ')) - - # insert separating `|` - D = self._hprint_vseparator(D, P) - - # add parens - D = prettyForm(*D.parens('(', ')')) - - # create the F symbol - above = D.height()//2 - 1 - below = D.height() - above - 1 - - sz, t, b, add, img = annotated('F') - F = prettyForm('\n' * (above - t) + img + '\n' * (below - b), - baseline=above + sz) - add = (sz + 1)//2 - - F = prettyForm(*F.left(self._print(len(e.ap)))) - F = prettyForm(*F.right(self._print(len(e.bq)))) - F.baseline = above + add - - D = prettyForm(*F.right(' ', D)) - - return D - - def _print_meijerg(self, e): - # FIXME refactor Matrix, Piecewise, and this into a tabular environment - - v = {} - v[(0, 0)] = [self._print(a) for a in e.an] - v[(0, 1)] = [self._print(a) for a in e.aother] - v[(1, 0)] = [self._print(b) for b in e.bm] - v[(1, 1)] = [self._print(b) for b in e.bother] - - P = self._print(e.argument) - P.baseline = P.height()//2 - - vp = {} - for idx in v: - vp[idx] = self._hprint_vec(v[idx]) - - for i in range(2): - maxw = max(vp[(0, i)].width(), vp[(1, i)].width()) - for j in range(2): - s = vp[(j, i)] - left = (maxw - s.width()) // 2 - right = maxw - left - s.width() - s = prettyForm(*s.left(' ' * left)) - s = prettyForm(*s.right(' ' * right)) - vp[(j, i)] = s - - D1 = prettyForm(*vp[(0, 0)].right(' ', vp[(0, 1)])) - D1 = prettyForm(*D1.below(' ')) - D2 = prettyForm(*vp[(1, 0)].right(' ', vp[(1, 1)])) - D = prettyForm(*D1.below(D2)) - - # make sure that the argument `z' is centred vertically - D.baseline = D.height()//2 - - # insert horizontal separator - P = prettyForm(*P.left(' ')) - D = prettyForm(*D.right(' ')) - - # insert separating `|` - D = self._hprint_vseparator(D, P) - - # add parens - D = prettyForm(*D.parens('(', ')')) - - # create the G symbol - above = D.height()//2 - 1 - below = D.height() - above - 1 - - sz, t, b, add, img = annotated('G') - F = prettyForm('\n' * (above - t) + img + '\n' * (below - b), - baseline=above + sz) - - pp = self._print(len(e.ap)) - pq = self._print(len(e.bq)) - pm = self._print(len(e.bm)) - pn = self._print(len(e.an)) - - def adjust(p1, p2): - diff = p1.width() - p2.width() - if diff == 0: - return p1, p2 - elif diff > 0: - return p1, prettyForm(*p2.left(' '*diff)) - else: - return prettyForm(*p1.left(' '*-diff)), p2 - pp, pm = adjust(pp, pm) - pq, pn = adjust(pq, pn) - pu = prettyForm(*pm.right(', ', pn)) - pl = prettyForm(*pp.right(', ', pq)) - - ht = F.baseline - above - 2 - if ht > 0: - pu = prettyForm(*pu.below('\n'*ht)) - p = prettyForm(*pu.below(pl)) - - F.baseline = above - F = prettyForm(*F.right(p)) - - F.baseline = above + add - - D = prettyForm(*F.right(' ', D)) - - return D - - def _print_ExpBase(self, e): - # TODO should exp_polar be printed differently? - # what about exp_polar(0), exp_polar(1)? - base = prettyForm(pretty_atom('Exp1', 'e')) - return base ** self._print(e.args[0]) - - def _print_Function(self, e, sort=False): - # XXX works only for applied functions - func = e.func - args = e.args - if sort: - args = sorted(args, key=default_sort_key) - - func_name = func.__name__ - - prettyFunc = self._print(C.Symbol(func_name)) - prettyArgs = prettyForm(*self._print_seq(args).parens()) - - pform = prettyForm( - binding=prettyForm.FUNC, *stringPict.next(prettyFunc, prettyArgs)) - - # store pform parts so it can be reassembled e.g. when powered - pform.prettyFunc = prettyFunc - pform.prettyArgs = prettyArgs - - return pform - - def _print_GeometryEntity(self, expr): - # GeometryEntity is based on Tuple but should not print like a Tuple - return self.emptyPrinter(expr) - - def _print_Lambda(self, e): - symbols, expr = e.args - - if len(symbols) == 1: - symbols = self._print(symbols[0]) - else: - symbols = self._print(tuple(symbols)) - - args = (symbols, self._print(expr)) - - prettyFunc = self._print(C.Symbol("Lambda")) - prettyArgs = prettyForm(*self._print_seq(args).parens()) - - pform = prettyForm( - binding=prettyForm.FUNC, *stringPict.next(prettyFunc, prettyArgs)) - - # store pform parts so it can be reassembled e.g. when powered - pform.prettyFunc = prettyFunc - pform.prettyArgs = prettyArgs - - return pform - - def _print_Order(self, e): - pform = self._print(e.expr) - pform = prettyForm(*pform.parens()) - pform = prettyForm(*pform.left('O')) - return pform - - def _print_gamma(self, e): - if self._use_unicode: - pform = self._print(e.args[0]) - pform = prettyForm(*pform.parens()) - pform = prettyForm(*pform.left(greek['gamma'][1])) - return pform - else: - return self._print_Function(e) - - def _print_uppergamma(self, e): - if self._use_unicode: - pform = self._print(e.args[0]) - pform = prettyForm(*pform.right(', ', self._print(e.args[1]))) - pform = prettyForm(*pform.parens()) - pform = prettyForm(*pform.left(greek['gamma'][1])) - return pform - else: - return self._print_Function(e) - - def _print_lowergamma(self, e): - if self._use_unicode: - pform = self._print(e.args[0]) - pform = prettyForm(*pform.right(', ', self._print(e.args[1]))) - pform = prettyForm(*pform.parens()) - pform = prettyForm(*pform.left(greek['gamma'][0])) - return pform - else: - return self._print_Function(e) - - def _print_expint(self, e): - from sympy import Function - if e.args[0].is_Integer and self._use_unicode: - return self._print_Function(Function('E_%s' % e.args[0])(e.args[1])) - return self._print_Function(e) - - def _print_Chi(self, e): - # This needs a special case since otherwise it comes out as greek - # letter chi... - prettyFunc = prettyForm("Chi") - prettyArgs = prettyForm(*self._print_seq(e.args).parens()) - - pform = prettyForm( - binding=prettyForm.FUNC, *stringPict.next(prettyFunc, prettyArgs)) - - # store pform parts so it can be reassembled e.g. when powered - pform.prettyFunc = prettyFunc - pform.prettyArgs = prettyArgs - - return pform - - def _print_Add(self, expr, order=None): - if self.order == 'none': - terms = list(expr.args) - else: - terms = self._as_ordered_terms(expr, order=order) - pforms, indices = [], [] - - def pretty_negative(pform, index): - """Prepend a minus sign to a pretty form. """ - if index == 0: - if pform.height() > 1: - pform_neg = '- ' - else: - pform_neg = '-' - else: - pform_neg = ' - ' - - pform = stringPict.next(pform_neg, pform) - return prettyForm(binding=prettyForm.NEG, *pform) - - for i, term in enumerate(terms): - if term.is_Mul and _coeff_isneg(term): - pform = self._print(-term) - pforms.append(pretty_negative(pform, i)) - elif term.is_Rational and term.q > 1: - pforms.append(None) - indices.append(i) - elif term.is_Number and term < 0: - pform = self._print(-term) - pforms.append(pretty_negative(pform, i)) - else: - pforms.append(self._print(term)) - - if indices: - large = True - - for pform in pforms: - if pform is not None and pform.height() > 1: - break - else: - large = False - - for i in indices: - term, negative = terms[i], False - - if term < 0: - term, negative = -term, True - - if large: - pform = prettyForm(str(term.p))/prettyForm(str(term.q)) - else: - pform = self._print(term) - - if negative: - pform = pretty_negative(pform, i) - - pforms[i] = pform - - return prettyForm.__add__(*pforms) - - def _print_Mul(self, product): - a = [] # items in the numerator - b = [] # items that are in the denominator (if any) - - if self.order not in ('old', 'none'): - args = product.as_ordered_factors() - else: - args = product.args - - # Gather terms for numerator/denominator - for item in args: - if item.is_commutative and item.is_Pow and item.exp.is_Rational and item.exp.is_negative: - b.append(C.Pow(item.base, -item.exp)) - elif item.is_Rational and item is not S.Infinity: - if item.p != 1: - a.append( C.Rational(item.p) ) - if item.q != 1: - b.append( C.Rational(item.q) ) - else: - a.append(item) - - from sympy import Integral, Piecewise, Product, Sum - - # Convert to pretty forms. Add parens to Add instances if there - # is more than one term in the numer/denom - for i in range(0, len(a)): - if (a[i].is_Add and len(a) > 1) or (i != len(a) - 1 and - isinstance(a[i], (Integral, Piecewise, Product, Sum))): - a[i] = prettyForm(*self._print(a[i]).parens()) - else: - a[i] = self._print(a[i]) - - for i in range(0, len(b)): - if (b[i].is_Add and len(b) > 1) or (i != len(b) - 1 and - isinstance(b[i], (Integral, Piecewise, Product, Sum))): - b[i] = prettyForm(*self._print(b[i]).parens()) - else: - b[i] = self._print(b[i]) - - # Construct a pretty form - if len(b) == 0: - return prettyForm.__mul__(*a) - else: - if len(a) == 0: - a.append( self._print(S.One) ) - return prettyForm.__mul__(*a)/prettyForm.__mul__(*b) - - # A helper function for _print_Pow to print x**(1/n) - def _print_nth_root(self, base, expt): - bpretty = self._print(base) - - # Construct root sign, start with the \/ shape - _zZ = xobj('/', 1) - rootsign = xobj('\\', 1) + _zZ - # Make exponent number to put above it - if isinstance(expt, C.Rational): - exp = str(expt.q) - if exp == '2': - exp = '' - else: - exp = str(expt.args[0]) - exp = exp.ljust(2) - if len(exp) > 2: - rootsign = ' '*(len(exp) - 2) + rootsign - # Stack the exponent - rootsign = stringPict(exp + '\n' + rootsign) - rootsign.baseline = 0 - # Diagonal: length is one less than height of base - linelength = bpretty.height() - 1 - diagonal = stringPict('\n'.join( - ' '*(linelength - i - 1) + _zZ + ' '*i - for i in range(linelength) - )) - # Put baseline just below lowest line: next to exp - diagonal.baseline = linelength - 1 - # Make the root symbol - rootsign = prettyForm(*rootsign.right(diagonal)) - # Det the baseline to match contents to fix the height - # but if the height of bpretty is one, the rootsign must be one higher - rootsign.baseline = max(1, bpretty.baseline) - #build result - s = prettyForm(hobj('_', 2 + bpretty.width())) - s = prettyForm(*bpretty.above(s)) - s = prettyForm(*s.left(rootsign)) - return s - - def _print_Pow(self, power): - from sympy import fraction - b, e = power.as_base_exp() - if power.is_commutative: - if e is S.NegativeOne: - return prettyForm("1")/self._print(b) - n, d = fraction(e) - if n is S.One and d.is_Atom and not e.is_Integer: - return self._print_nth_root(b, e) - if e.is_Rational and e < 0: - return prettyForm("1")/self._print(b)**self._print(-e) - - # None of the above special forms, do a standard power - return self._print(b)**self._print(e) - - def __print_numer_denom(self, p, q): - if q == 1: - if p < 0: - return prettyForm(str(p), binding=prettyForm.NEG) - else: - return prettyForm(str(p)) - elif abs(p) >= 10 and abs(q) >= 10: - # If more than one digit in numer and denom, print larger fraction - if p < 0: - pform = prettyForm(str(-p))/prettyForm(str(q)) - return prettyForm(binding=prettyForm.NEG, *pform.left('- ')) - else: - return prettyForm(str(p))/prettyForm(str(q)) - else: - return None - - def _print_Rational(self, expr): - result = self.__print_numer_denom(expr.p, expr.q) - - if result is not None: - return result - else: - return self.emptyPrinter(expr) - - def _print_Fraction(self, expr): - result = self.__print_numer_denom(expr.numerator, expr.denominator) - - if result is not None: - return result - else: - return self.emptyPrinter(expr) - - def _print_ProductSet(self, p): - if len(p.sets) > 1 and not has_variety(p.sets): - from sympy import Pow - return self._print(Pow(p.sets[0], len(p.sets), evaluate=False)) - else: - prod_char = '\xd7' - return self._print_seq(p.sets, None, None, ' %s ' % prod_char, - parenthesize=lambda set: set.is_Union or set.is_Intersection) - - def _print_FiniteSet(self, s): - items = sorted(s.args, key=default_sort_key) - return self._print_seq(items, '{', '}', ', ' ) - - def _print_Range(self, s): - - if self._use_unicode: - dots = "\u2026" - else: - dots = '...' - - if len(s) > 4: - it = iter(s) - printset = next(it), next(it), dots, s._last_element - else: - printset = tuple(s) - - return self._print_seq(printset, '{', '}', ', ' ) - - def _print_Interval(self, i): - if i.start == i.end: - return self._print_seq(i.args[:1], '{', '}') - - else: - if i.left_open: - left = '(' - else: - left = '[' - - if i.right_open: - right = ')' - else: - right = ']' - - return self._print_seq(i.args[:2], left, right) - - def _print_Intersection(self, u): - - delimiter = ' %s ' % pretty_atom('Intersection') - - return self._print_seq(u.args, None, None, delimiter, - parenthesize=lambda set: set.is_ProductSet or set.is_Union) - - def _print_Union(self, u): - - union_delimiter = ' %s ' % pretty_atom('Union') - - return self._print_seq(u.args, None, None, union_delimiter, - parenthesize=lambda set: set.is_ProductSet or set.is_Intersection) - - def _print_TransformationSet(self, ts): - if self._use_unicode: - inn = "\u220a" - else: - inn = 'in' - variables = self._print_seq(ts.lamda.variables) - expr = self._print(ts.lamda.expr) - bar = self._print("|") - base = self._print(ts.base_set) - - return self._print_seq((expr, bar, variables, inn, base), "{", "}", ' ') - - def _print_seq(self, seq, left=None, right=None, delimiter=', ', - parenthesize=lambda x: False): - s = None - - for item in seq: - pform = self._print(item) - - if parenthesize(item): - pform = prettyForm(*pform.parens()) - if s is None: - # first element - s = pform - else: - s = prettyForm(*stringPict.next(s, delimiter)) - s = prettyForm(*stringPict.next(s, pform)) - - if s is None: - s = stringPict('') - - s = prettyForm(*s.parens(left, right, ifascii_nougly=True)) - return s - - def join(self, delimiter, args): - pform = None - - for arg in args: - if pform is None: - pform = arg - else: - pform = prettyForm(*pform.right(delimiter)) - pform = prettyForm(*pform.right(arg)) - - if pform is None: - return prettyForm("") - else: - return pform - - def _print_list(self, l): - return self._print_seq(l, '[', ']') - - def _print_tuple(self, t): - if len(t) == 1: - ptuple = prettyForm(*stringPict.next(self._print(t[0]), ',')) - return prettyForm(*ptuple.parens('(', ')', ifascii_nougly=True)) - else: - return self._print_seq(t, '(', ')') - - def _print_Tuple(self, expr): - return self._print_tuple(expr) - - def _print_dict(self, d): - keys = sorted(list(d.keys()), key=default_sort_key) - items = [] - - for k in keys: - K = self._print(k) - V = self._print(d[k]) - s = prettyForm(*stringPict.next(K, ': ', V)) - - items.append(s) - - return self._print_seq(items, '{', '}') - - def _print_Dict(self, d): - return self._print_dict(d) - - def _print_set(self, s): - items = sorted(s, key=default_sort_key) - pretty = self._print_seq(items, '[', ']') - pretty = prettyForm(*pretty.parens('(', ')', ifascii_nougly=True)) - pretty = prettyForm(*stringPict.next(type(s).__name__, pretty)) - return pretty - - _print_frozenset = _print_set - - def _print_AlgebraicNumber(self, expr): - if expr.is_aliased: - return self._print(expr.as_poly().as_expr()) - else: - return self._print(expr.as_expr()) - - def _print_RootOf(self, expr): - args = [self._print_Add(expr.expr, order='lex'), expr.index] - pform = prettyForm(*self._print_seq(args).parens()) - pform = prettyForm(*pform.left('RootOf')) - return pform - - def _print_RootSum(self, expr): - args = [self._print_Add(expr.expr, order='lex')] - - if expr.fun is not S.IdentityFunction: - args.append(self._print(expr.fun)) - - pform = prettyForm(*self._print_seq(args).parens()) - pform = prettyForm(*pform.left('RootSum')) - - return pform - - def _print_FiniteField(self, expr): - if self._use_unicode: - form = '\u2124_%d' - else: - form = 'GF(%d)' - - return prettyForm(pretty_symbol(form % expr.mod)) - - def _print_IntegerRing(self, expr): - if self._use_unicode: - return prettyForm('\u2124') - else: - return prettyForm('ZZ') - - def _print_RationalField(self, expr): - if self._use_unicode: - return prettyForm('\u211A') - else: - return prettyForm('QQ') - - def _print_RealDomain(self, expr): - if self._use_unicode: - return prettyForm('\u211D') - else: - return prettyForm('RR') - - def _print_ComplexDomain(self, expr): - if self._use_unicode: - return prettyForm('\u2102') - else: - return prettyForm('CC') - - def _print_PolynomialRingBase(self, expr): - g = expr.gens - if str(expr.order) != str(expr.default_order): - g = g + ("order=" + str(expr.order),) - pform = self._print_seq(g, '[', ']') - pform = prettyForm(*pform.left(self._print(expr.dom))) - - return pform - - def _print_FractionField(self, expr): - pform = self._print_seq(expr.gens, '(', ')') - pform = prettyForm(*pform.left(self._print(expr.dom))) - - return pform - - def _print_GroebnerBasis(self, basis): - exprs = [ self._print_Add(arg, order=basis.order) - for arg in basis.exprs ] - exprs = prettyForm(*self.join(", ", exprs).parens(left="[", right="]")) - - gens = [ self._print(gen) for gen in basis.gens ] - - domain = prettyForm( - *prettyForm("domain=").right(self._print(basis.domain))) - order = prettyForm( - *prettyForm("order=").right(self._print(basis.order))) - - pform = self.join(", ", [exprs] + gens + [domain, order]) - - pform = prettyForm(*pform.parens()) - pform = prettyForm(*pform.left(basis.__class__.__name__)) - - return pform - - def _print_Subs(self, e): - pform = self._print(e.expr) - pform = prettyForm(*pform.parens()) - - h = pform.height() if pform.height() > 1 else 2 - rvert = stringPict(vobj('|', h), baseline=pform.baseline) - pform = prettyForm(*pform.right(rvert)) - - b = pform.baseline - pform.baseline = pform.height() - 1 - pform = prettyForm(*pform.right(self._print_seq([ - self._print_seq((self._print(v[0]), xsym('=='), self._print(v[1])), - delimiter='') for v in zip(e.variables, e.point) ]))) - - pform.baseline = b - return pform - - def _print_euler(self, e): - pform = prettyForm("E") - arg = self._print(e.args[0]) - pform_arg = prettyForm(" "*arg.width()) - pform_arg = prettyForm(*pform_arg.below(arg)) - pform = prettyForm(*pform.right(pform_arg)) - return pform - - def _print_catalan(self, e): - pform = prettyForm("C") - arg = self._print(e.args[0]) - pform_arg = prettyForm(" "*arg.width()) - pform_arg = prettyForm(*pform_arg.below(arg)) - pform = prettyForm(*pform.right(pform_arg)) - return pform - - def _print_KroneckerDelta(self, e): - pform = self._print(e.args[0]) - pform = prettyForm(*pform.right((prettyForm(',')))) - pform = prettyForm(*pform.right((self._print(e.args[1])))) - if self._use_unicode: - a = stringPict(pretty_symbol('delta')) - else: - a = stringPict('d') - b = pform - top = stringPict(*b.left(' '*a.width())) - bot = stringPict(*a.right(' '*b.width())) - return prettyForm(binding=prettyForm.POW, *bot.below(top)) - - def _print_RandomDomain(self, d): - try: - pform = self._print('Domain: ') - pform = prettyForm(*pform.right(self._print(d.as_boolean()))) - return pform - - except: - try: - pform = self._print('Domain: ') - pform = prettyForm(*pform.right(self._print(d.symbols))) - pform = prettyForm(*pform.right(self._print(' in '))) - pform = prettyForm(*pform.right(self._print(d.set))) - return pform - except: - return self._print(None) - - def _print_DMP(self, p): - try: - if p.ring is not None: - # TODO incorporate order - return self._print(p.ring.to_sympy(p)) - except SympifyError: - pass - return self._print(repr(p)) - - def _print_DMF(self, p): - return self._print_DMP(p) - - def _print_Object(self, object): - return self._print(pretty_symbol(object.name)) - - def _print_Morphism(self, morphism): - arrow = xsym("-->") - - domain = self._print(morphism.domain) - codomain = self._print(morphism.codomain) - tail = domain.right(arrow, codomain)[0] - - return prettyForm(tail) - - def _print_NamedMorphism(self, morphism): - pretty_name = self._print(pretty_symbol(morphism.name)) - pretty_morphism = self._print_Morphism(morphism) - return prettyForm(pretty_name.right(":", pretty_morphism)[0]) - - def _print_IdentityMorphism(self, morphism): - from sympy.categories import NamedMorphism - return self._print_NamedMorphism( - NamedMorphism(morphism.domain, morphism.codomain, "id")) - - def _print_CompositeMorphism(self, morphism): - - circle = xsym(".") - - # All components of the morphism have names and it is thus - # possible to build the name of the composite. - component_names_list = [pretty_symbol(component.name) for - component in morphism.components] - component_names_list.reverse() - component_names = circle.join(component_names_list) + ":" - - pretty_name = self._print(component_names) - pretty_morphism = self._print_Morphism(morphism) - return prettyForm(pretty_name.right(pretty_morphism)[0]) - - def _print_Category(self, category): - return self._print(pretty_symbol(category.name)) - - def _print_Diagram(self, diagram): - if not diagram.premises: - # This is an empty diagram. - return self._print(S.EmptySet) - - pretty_result = self._print(diagram.premises) - if diagram.conclusions: - results_arrow = " %s " % xsym("==>") - - pretty_conclusions = self._print(diagram.conclusions)[0] - pretty_result = pretty_result.right( - results_arrow, pretty_conclusions) - - return prettyForm(pretty_result[0]) - - def _print_DiagramGrid(self, grid): - from sympy.matrices import Matrix - from sympy import Symbol - matrix = Matrix([[grid[i, j] if grid[i, j] else Symbol(" ") - for j in range(grid.width)] - for i in range(grid.height)]) - return self._print_matrix_contents(matrix) - - def _print_FreeModuleElement(self, m): - # Print as row vector for convenience, for now. - return self._print_seq(m, '[', ']') - - def _print_SubModule(self, M): - return self._print_seq(M.gens, '<', '>') - - def _print_FreeModule(self, M): - return self._print(M.ring)**self._print(M.rank) - - def _print_ModuleImplementedIdeal(self, M): - return self._print_seq([x for [x] in M._module.gens], '<', '>') - - def _print_QuotientRing(self, R): - return self._print(R.ring) / self._print(R.base_ideal) - - def _print_QuotientRingElement(self, R): - return self._print(R.data) + self._print(R.ring.base_ideal) - - def _print_QuotientModuleElement(self, m): - return self._print(m.data) + self._print(m.module.killed_module) - - def _print_QuotientModule(self, M): - return self._print(M.base) / self._print(M.killed_module) - - def _print_MatrixHomomorphism(self, h): - matrix = self._print(h._sympy_matrix()) - matrix.baseline = matrix.height() // 2 - pform = prettyForm(*matrix.right(' : ', self._print(h.domain), - ' %s> ' % hobj('-', 2), self._print(h.codomain))) - return pform - - def _print_BaseScalarField(self, field): - string = field._coord_sys._names[field._index] - return self._print(pretty_symbol(string)) - - def _print_BaseVectorField(self, field): - s = U('PARTIAL DIFFERENTIAL') + '_' + field._coord_sys._names[field._index] - return self._print(pretty_symbol(s)) - - def _print_Differential(self, diff): - field = diff._form_field - if hasattr(field, '_coord_sys'): - string = field._coord_sys._names[field._index] - return self._print('\u2146 ' + pretty_symbol(string)) - else: - pform = self._print(field) - pform = prettyForm(*pform.parens()) - return prettyForm(*pform.left("\u2146")) - - def _print_Tr(self, p): - #TODO: Handle indices - pform = self._print(p.args[0]) - pform = prettyForm(*pform.left('%s(' % (p.__class__.__name__))) - pform = prettyForm(*pform.right(')')) - return pform - -
    -
    [docs]def pretty(expr, **settings): - """Returns a string containing the prettified form of expr. - - For information on keyword arguments see pretty_print function. - - """ - pp = PrettyPrinter(settings) - - # XXX: this is an ugly hack, but at least it works - use_unicode = pp._settings['use_unicode'] - uflag = pretty_use_unicode(use_unicode) - - try: - return pp.doprint(expr) - finally: - pretty_use_unicode(uflag) - -
    -
    [docs]def pretty_print(expr, **settings): - """Prints expr in pretty form. - - pprint is just a shortcut for this function. - - - Parameters - ========== - - expr : expression - the expression to print - wrap_line : bool, optional - line wrapping enabled/disabled, defaults to True - num_columns : bool, optional - number of columns before line breaking (default to None which reads - the terminal width), useful when using SymPy without terminal. - use_unicode : bool or None, optional - use unicode characters, such as the Greek letter pi instead of - the string pi. - full_prec : bool or string, optional - use full precision. Default to "auto" - order : bool or string, optional - set to 'none' for long expressions if slow; default is None - - """ - print(pretty(expr, **settings)) -
    -pprint = pretty_print - - -def pager_print(expr, **settings): - """Prints expr using the pager, in pretty form. - - This invokes a pager command using pydoc. Lines are not wrapped - automatically. This routine is meant to be used with a pager that allows - sideways scrolling, like ``less -S``. - - Parameters are the same as for ``pretty_print``. If you wish to wrap lines, - pass ``num_columns=None`` to auto-detect the width of the terminal. - - """ - from pydoc import pager - from locale import getpreferredencoding - if 'num_columns' not in settings: - settings['num_columns'] = 500000 # disable line wrap - pager(pretty(expr, **settings).encode(getpreferredencoding())) -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/printing/pretty/pretty_symbology.html b/dev-py3k/_modules/sympy/printing/pretty/pretty_symbology.html deleted file mode 100644 index 6db9306c595..00000000000 --- a/dev-py3k/_modules/sympy/printing/pretty/pretty_symbology.html +++ /dev/null @@ -1,628 +0,0 @@ - - - - - - - - - - sympy.printing.pretty.pretty_symbology — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.printing.pretty.pretty_symbology

    -"""Symbolic primitives + unicode/ASCII abstraction for pretty.py"""
    -
    -import sys
    -warnings = ''
    -
    -# first, setup unicodedate environment
    -try:
    -    import unicodedata
    -
    -
    [docs] def U(name): - """unicode character by name or None if not found""" - try: - u = unicodedata.lookup(name) - except KeyError: - u = None - - global warnings - warnings += 'W: no \'%s\' in unocodedata\n' % name - - return u -
    -except ImportError: - warnings += 'W: no unicodedata available\n' - U = lambda name: None - -from sympy.printing.conventions import split_super_sub - - -# prefix conventions when constructing tables -# L - LATIN i -# G - GREEK beta -# D - DIGIT 0 -# S - SYMBOL + - - -__all__ = ['greek', 'sub', 'sup', 'xsym', 'vobj', 'hobj', 'pretty_symbol', - 'annotated'] - - -_use_unicode = False - - -
    [docs]def pretty_use_unicode(flag=None): - """Set whether pretty-printer should use unicode by default""" - global _use_unicode - global warnings - if flag is None: - return _use_unicode - - if flag and warnings: - # print warnings (if any) on first unicode usage - print("I: pprint -- we are going to use unicode, but there are following problems:") - print(warnings) - warnings = '' - - use_unicode_prev = _use_unicode - _use_unicode = flag - return use_unicode_prev - -
    -
    [docs]def pretty_try_use_unicode(): - """See if unicode output is available and leverage it if possible""" - - try: - symbols = [] - - # see, if we can represent greek alphabet - for g, G in greek.values(): - symbols.append(g) - symbols.append(G) - - # and atoms - symbols += list(atoms_table.values()) - - for s in symbols: - if s is None: - return # common symbols not present! - - encoding = getattr(sys.stdout, 'encoding', None) - - # this happens when e.g. stdout is redirected through a pipe, or is - # e.g. a cStringIO.StringO - if encoding is None: - return # sys.stdout has no encoding - - # try to encode - s.encode(encoding) - - except UnicodeEncodeError: - pass - else: - pretty_use_unicode(True) - -
    -
    [docs]def xstr(*args): - """call str or unicode depending on current mode""" - if _use_unicode: - return str(*args) - else: - return str(*args) - -# GREEK
    -g = lambda l: U('GREEK SMALL LETTER %s' % l.upper()) -G = lambda l: U('GREEK CAPITAL LETTER %s' % l.upper()) - -greek_letters = [ - 'alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta', 'eta', 'theta', - 'iota', 'kappa', 'lamda', 'mu', 'nu', 'xi', 'omicron', 'pi', 'rho', - 'sigma', 'tau', 'upsilon', 'phi', 'chi', 'psi', 'omega' ] - -# {} greek letter -> (g,G) -greek = dict([(l, (g(l), G(l))) for l in greek_letters]) -# aliases -greek['lambda'] = greek['lamda'] - -digit_2txt = { - '0': 'ZERO', - '1': 'ONE', - '2': 'TWO', - '3': 'THREE', - '4': 'FOUR', - '5': 'FIVE', - '6': 'SIX', - '7': 'SEVEN', - '8': 'EIGHT', - '9': 'NINE', -} - -symb_2txt = { - '+': 'PLUS SIGN', - '-': 'MINUS', - '=': 'EQUALS SIGN', - '(': 'LEFT PARENTHESIS', - ')': 'RIGHT PARENTHESIS', - '[': 'LEFT SQUARE BRACKET', - ']': 'RIGHT SQUARE BRACKET', - '{': 'LEFT CURLY BRACKET', - '}': 'RIGHT CURLY BRACKET', - - # non-std - '{}': 'CURLY BRACKET', - 'sum': 'SUMMATION', - 'int': 'INTEGRAL', -} - -# SUBSCRIPT & SUPERSCRIPT -LSUB = lambda letter: U('LATIN SUBSCRIPT SMALL LETTER %s' % letter.upper()) -GSUB = lambda letter: U('GREEK SUBSCRIPT SMALL LETTER %s' % letter.upper()) -DSUB = lambda digit: U('SUBSCRIPT %s' % digit_2txt[digit]) -SSUB = lambda symb: U('SUBSCRIPT %s' % symb_2txt[symb]) - -LSUP = lambda letter: U('SUPERSCRIPT LATIN SMALL LETTER %s' % letter.upper()) -DSUP = lambda digit: U('SUPERSCRIPT %s' % digit_2txt[digit]) -SSUP = lambda symb: U('SUPERSCRIPT %s' % symb_2txt[symb]) - -sub = {} # symb -> subscript symbol -sup = {} # symb -> superscript symbol - -# latin subscripts -for l in 'aeioruvx': - sub[l] = LSUB(l) - -for l in 'in': - sup[l] = LSUP(l) - -for gl in ['beta', 'gamma', 'rho', 'phi', 'chi']: - sub[gl] = GSUB(gl) - -for d in [str(i) for i in range(10)]: - sub[d] = DSUB(d) - sup[d] = DSUP(d) - -for s in '+-=()': - sub[s] = SSUB(s) - sup[s] = SSUP(s) - - -# VERTICAL OBJECTS -HUP = lambda symb: U('%s UPPER HOOK' % symb_2txt[symb]) -CUP = lambda symb: U('%s UPPER CORNER' % symb_2txt[symb]) -MID = lambda symb: U('%s MIDDLE PIECE' % symb_2txt[symb]) -EXT = lambda symb: U('%s EXTENSION' % symb_2txt[symb]) -HLO = lambda symb: U('%s LOWER HOOK' % symb_2txt[symb]) -CLO = lambda symb: U('%s LOWER CORNER' % symb_2txt[symb]) -TOP = lambda symb: U('%s TOP' % symb_2txt[symb]) -BOT = lambda symb: U('%s BOTTOM' % symb_2txt[symb]) - -# {} '(' -> (extension, start, end, middle) 1-character -_xobj_unicode = { - - # vertical symbols - # (( ext, top, bot, mid ), c1) - '(': (( EXT('('), HUP('('), HLO('(') ), '('), - ')': (( EXT(')'), HUP(')'), HLO(')') ), ')'), - '[': (( EXT('['), CUP('['), CLO('[') ), '['), - ']': (( EXT(']'), CUP(']'), CLO(']') ), ']'), - '{': (( EXT('{}'), HUP('{'), HLO('{'), MID('{') ), '{'), - '}': (( EXT('{}'), HUP('}'), HLO('}'), MID('}') ), '}'), - '|': U('BOX DRAWINGS LIGHT VERTICAL'), - - '<': ((U('BOX DRAWINGS LIGHT VERTICAL'), - U('BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT'), - U('BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT')), '<'), - - '>': ((U('BOX DRAWINGS LIGHT VERTICAL'), - U('BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT'), - U('BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT')), '>'), - - 'lfloor': (( EXT('['), EXT('['), CLO('[') ), U('LEFT FLOOR')), - 'rfloor': (( EXT(']'), EXT(']'), CLO(']') ), U('RIGHT FLOOR')), - 'lceil': (( EXT('['), CUP('['), EXT('[') ), U('LEFT CEILING')), - 'rceil': (( EXT(']'), CUP(']'), EXT(']') ), U('RIGHT CEILING')), - - 'int': (( EXT('int'), U('TOP HALF INTEGRAL'), U('BOTTOM HALF INTEGRAL') ), U('INTEGRAL')), - 'sum': (( U('BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT'), '_', U('OVERLINE'), U('BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT')), U('N-ARY SUMMATION')), - - # horizontal objects - #'-': '-', - '-': U('BOX DRAWINGS LIGHT HORIZONTAL'), - '_': U('LOW LINE'), - # We used to use this, but LOW LINE looks better for roots, as it's a - # little lower (i.e., it lines up with the / perfectly. But perhaps this - # one would still be wanted for some cases? - # '_': U('HORIZONTAL SCAN LINE-9'), - - # diagonal objects '\' & '/' ? - '/': U('BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT'), - '\\': U('BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT'), -} - -_xobj_ascii = { - # vertical symbols - # (( ext, top, bot, mid ), c1) - '(': (( '|', '/', '\\' ), '('), - ')': (( '|', '\\', '/' ), ')'), - -# XXX this looks ugly -# '[': (( '|', '-', '-' ), '['), -# ']': (( '|', '-', '-' ), ']'), -# XXX not so ugly :( - '[': (( '[', '[', '[' ), '['), - ']': (( ']', ']', ']' ), ']'), - - '{': (( '|', '/', '\\', '<' ), '{'), - '}': (( '|', '\\', '/', '>' ), '}'), - '|': '|', - - '<': (( '|', '/', '\\' ), '<'), - '>': (( '|', '\\', '/' ), '>'), - - 'int': ( ' | ', ' /', '/ ' ), - - # horizontal objects - '-': '-', - '_': '_', - - # diagonal objects '\' & '/' ? - '/': '/', - '\\': '\\', -} - - -
    [docs]def xobj(symb, length): - """Construct spatial object of given length. - - return: [] of equal-length strings - """ - - assert length > 0 - - # TODO robustify when no unicodedat available - if _use_unicode: - _xobj = _xobj_unicode - else: - _xobj = _xobj_ascii - - vinfo = _xobj[symb] - - c1 = top = bot = mid = None - - if not isinstance(vinfo, tuple): # 1 entry - ext = vinfo - else: - if isinstance(vinfo[0], tuple): # (vlong), c1 - vlong = vinfo[0] - c1 = vinfo[1] - else: # (vlong), c1 - vlong = vinfo - - ext = vlong[0] - - try: - top = vlong[1] - bot = vlong[2] - mid = vlong[3] - except IndexError: - pass - - if c1 is None: - c1 = ext - if top is None: - top = ext - if bot is None: - bot = ext - if mid is not None: - if (length % 2) == 0: - # even height, but we have to print it somehow anyway... - # XXX is it ok? - length += 1 - - else: - mid = ext - - if length == 1: - return c1 - - res = [] - next = (length - 2)//2 - nmid = (length - 2) - next*2 - - res += [top] - res += [ext]*next - res += [mid]*nmid - res += [ext]*next - res += [bot] - - return res - -
    -
    [docs]def vobj(symb, height): - """Construct vertical object of a given height - - see: xobj - """ - return '\n'.join( xobj(symb, height) ) - -
    -
    [docs]def hobj(symb, width): - """Construct horizontal object of a given width - - see: xobj - """ - return ''.join( xobj(symb, width) ) - -# RADICAL -# n -> symbol
    -root = { - 2: U('SQUARE ROOT'), # U('RADICAL SYMBOL BOTTOM') - 3: U('CUBE ROOT'), - 4: U('FOURTH ROOT'), -} - - -# RATIONAL -VF = lambda txt: U('VULGAR FRACTION %s' % txt) - -# (p,q) -> symbol -frac = { - (1, 2): VF('ONE HALF'), - (1, 3): VF('ONE THIRD'), - (2, 3): VF('TWO THIRDS'), - (1, 4): VF('ONE QUARTER'), - (3, 4): VF('THREE QUARTERS'), - (1, 5): VF('ONE FIFTH'), - (2, 5): VF('TWO FIFTHS'), - (3, 5): VF('THREE FIFTHS'), - (4, 5): VF('FOUR FIFTHS'), - (1, 6): VF('ONE SIXTH'), - (5, 6): VF('FIVE SIXTHS'), - (1, 8): VF('ONE EIGHTH'), - (3, 8): VF('THREE EIGHTHS'), - (5, 8): VF('FIVE EIGHTHS'), - (7, 8): VF('SEVEN EIGHTHS'), -} - - -# atom symbols -_xsym = { - '==': ('=', '='), - '<': ('<', '<'), - '>': ('>', '>'), - '<=': ('<=', U('LESS-THAN OR EQUAL TO')), - '>=': ('>=', U('GREATER-THAN OR EQUAL TO')), - '!=': ('!=', U('NOT EQUAL TO')), - '*': ('*', U('DOT OPERATOR')), - '-->': ('-->', U('EM DASH') + U('EM DASH') + - U('BLACK RIGHT-POINTING TRIANGLE')), - '==>': ('==>', U('BOX DRAWINGS DOUBLE HORIZONTAL') + - U('BOX DRAWINGS DOUBLE HORIZONTAL') + - U('BLACK RIGHT-POINTING TRIANGLE')), - '.': ('*', U('RING OPERATOR')), -} - - -
    [docs]def xsym(sym): - """get symbology for a 'character'""" - op = _xsym[sym] - - if _use_unicode: - return op[1] - else: - return op[0] - - -# SYMBOLS -
    -atoms_table = { - # class how-to-display - 'Exp1': U('SCRIPT SMALL E'), - 'Pi': U('GREEK SMALL LETTER PI'), - 'Infinity': U('INFINITY'), - 'NegativeInfinity': U('INFINITY') and ('-' + U('INFINITY')), # XXX what to do here - #'ImaginaryUnit': U('GREEK SMALL LETTER IOTA'), - #'ImaginaryUnit': U('MATHEMATICAL ITALIC SMALL I'), - 'ImaginaryUnit': U('DOUBLE-STRUCK ITALIC SMALL I'), - 'EmptySet': U('EMPTY SET'), - 'Naturals': U('DOUBLE-STRUCK CAPITAL N'), - 'Integers': U('DOUBLE-STRUCK CAPITAL Z'), - 'Reals': U('DOUBLE-STRUCK CAPITAL R'), - 'Union': U('UNION'), - 'Intersection': U('INTERSECTION'), - 'Ring': U('RING OPERATOR') -} - - -
    [docs]def pretty_atom(atom_name, default=None): - """return pretty representation of an atom""" - if _use_unicode: - return atoms_table[atom_name] - else: - if default is not None: - return default - - raise KeyError('only unicode') # send it default printer - -
    -
    [docs]def pretty_symbol(symb_name): - """return pretty representation of a symbol""" - # let's split symb_name into symbol + index - # UC: beta1 - # UC: f_beta - - if not _use_unicode: - return symb_name - - name, sups, subs = split_super_sub(symb_name) - - # let's prettify name - gG = greek.get(name.lower()) - if gG is not None: - if name.islower(): - greek_name = greek.get(name.lower())[0] - else: - greek_name = greek.get(name.lower())[1] - # some letters may not be available - if greek_name is not None: - name = greek_name - - # Let's prettify sups/subs. If it fails at one of them, pretty sups/subs are - # not used at all. - def pretty_list(l, mapping): - result = [] - for s in l: - pretty = mapping.get(s) - if pretty is None: - try: # match by separate characters - pretty = ''.join([mapping[c] for c in s]) - except KeyError: - return None - result.append(pretty) - return result - - pretty_sups = pretty_list(sups, sup) - if pretty_sups is not None: - pretty_subs = pretty_list(subs, sub) - else: - pretty_subs = None - - # glue the results into one string - if pretty_subs is None: # nice formatting of sups/subs did not work - return symb_name - else: - sups_result = ' '.join(pretty_sups) - subs_result = ' '.join(pretty_subs) - - return ''.join([name, sups_result, subs_result]) - -
    -
    [docs]def annotated(letter): - """ - Return a stylised drawing of the letter ``letter``, together with - information on how to put annotations (super- and subscripts to the - left and to the right) on it. - - See pretty.py functions _print_meijerg, _print_hyper on how to use this - information. - """ - ucode_pics = { - 'F': (2, 0, 2, 0, '\u250c\u2500\n\u251c\u2500\n\u2575'), - 'G': (3, 0, 3, 1, - '\u256d\u2500\u256e\n\u2502\u2576\u2510\n\u2570\u2500\u256f') - } - ascii_pics = { - 'F': (3, 0, 3, 0, ' _\n|_\n|\n'), - 'G': (3, 0, 3, 1, ' __\n/__\n\_|') - } - - if _use_unicode: - return ucode_pics[letter] - else: - return ascii_pics[letter]
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/printing/pretty/stringpict.html b/dev-py3k/_modules/sympy/printing/pretty/stringpict.html deleted file mode 100644 index 1bf2bf1eb0d..00000000000 --- a/dev-py3k/_modules/sympy/printing/pretty/stringpict.html +++ /dev/null @@ -1,612 +0,0 @@ - - - - - - - - - - sympy.printing.pretty.stringpict — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.printing.pretty.stringpict

    -"""Prettyprinter by Jurjen Bos.
    -(I hate spammers: mail me at pietjepuk314 at the reverse of ku.oc.oohay).
    -All objects have a method that create a "stringPict",
    -that can be used in the str method for pretty printing.
    -
    -Updates by Jason Gedge (email <my last name> at cs mun ca)
    -    - terminal_string() method
    -    - minor fixes and changes (mostly to prettyForm)
    -
    -TODO:
    -    - Allow left/center/right alignment options for above/below and
    -      top/center/bottom alignment options for left/right
    -"""
    -
    -from .pretty_symbology import hobj, vobj, xsym, xobj, pretty_use_unicode
    -
    -
    -
    [docs]class stringPict(object): - """An ASCII picture. - The pictures are represented as a list of equal length strings. - """ - #special value for stringPict.below - LINE = 'line' - - def __init__(self, s, baseline=0): - """Initialize from string. - Multiline strings are centered. - """ - #picture is a string that just can be printed - self.picture = stringPict.equalLengths(s.splitlines()) - #baseline is the line number of the "base line" - self.baseline = baseline - self.binding = None - - @staticmethod - def equalLengths(lines): - # empty lines - if not lines: - return [''] - - width = max(len(line) for line in lines) - return [line.center(width) for line in lines] - -
    [docs] def height(self): - """The height of the picture in characters.""" - return len(self.picture) -
    -
    [docs] def width(self): - """The width of the picture in characters.""" - return len(self.picture[0]) -
    - @staticmethod -
    [docs] def next(*args): - """Put a string of stringPicts next to each other. - Returns string, baseline arguments for stringPict. - """ - #convert everything to stringPicts - objects = [] - for arg in args: - if isinstance(arg, str): - arg = stringPict(arg) - objects.append(arg) - - #make a list of pictures, with equal height and baseline - newBaseline = max(obj.baseline for obj in objects) - newHeightBelowBaseline = max( - obj.height() - obj.baseline - for obj in objects) - newHeight = newBaseline + newHeightBelowBaseline - - pictures = [] - for obj in objects: - oneEmptyLine = [' '*obj.width()] - basePadding = newBaseline - obj.baseline - totalPadding = newHeight - obj.height() - pictures.append( - oneEmptyLine * basePadding + - obj.picture + - oneEmptyLine * (totalPadding - basePadding)) - - result = [''.join(lines) for lines in zip(*pictures)] - return '\n'.join(result), newBaseline -
    -
    [docs] def right(self, *args): - r"""Put pictures next to this one. - Returns string, baseline arguments for stringPict. - (Multiline) strings are allowed, and are given a baseline of 0. - - Examples - ======== - - >>> from sympy.printing.pretty.stringpict import stringPict - >>> print(stringPict("10").right(" + ",stringPict("1\r-\r2",1))[0]) - 1 - 10 + - - 2 - - """ - return stringPict.next(self, *args) -
    -
    [docs] def left(self, *args): - """Put pictures (left to right) at left. - Returns string, baseline arguments for stringPict. - """ - return stringPict.next(*(args + (self,))) -
    - @staticmethod -
    [docs] def stack(*args): - """Put pictures on top of each other, - from top to bottom. - Returns string, baseline arguments for stringPict. - The baseline is the baseline of the second picture. - Everything is centered. - Baseline is the baseline of the second picture. - Strings are allowed. - The special value stringPict.LINE is a row of '-' extended to the width. - """ - #convert everything to stringPicts; keep LINE - objects = [] - for arg in args: - if arg is not stringPict.LINE and isinstance(arg, str): - arg = stringPict(arg) - objects.append(arg) - - #compute new width - newWidth = max( - obj.width() - for obj in objects - if obj is not stringPict.LINE) - - lineObj = stringPict(hobj('-', newWidth)) - - #replace LINE with proper lines - for i, obj in enumerate(objects): - if obj is stringPict.LINE: - objects[i] = lineObj - - #stack the pictures, and center the result - newPicture = [] - for obj in objects: - newPicture.extend(obj.picture) - newPicture = [line.center(newWidth) for line in newPicture] - newBaseline = objects[0].height() + objects[1].baseline - return '\n'.join(newPicture), newBaseline -
    -
    [docs] def below(self, *args): - """Put pictures under this picture. - Returns string, baseline arguments for stringPict. - Baseline is baseline of top picture - - Examples - ======== - - >>> from sympy.printing.pretty.stringpict import stringPict - >>> print(stringPict("x+3").below( - ... stringPict.LINE, '3')[0]) #doctest: +NORMALIZE_WHITESPACE - x+3 - --- - 3 - - """ - s, baseline = stringPict.stack(self, *args) - return s, self.baseline -
    -
    [docs] def above(self, *args): - """Put pictures above this picture. - Returns string, baseline arguments for stringPict. - Baseline is baseline of bottom picture. - """ - string, baseline = stringPict.stack(*(args + (self,))) - baseline = len(string.splitlines()) - self.height() + self.baseline - return string, baseline -
    -
    [docs] def parens(self, left='(', right=')', ifascii_nougly=False): - """Put parentheses around self. - Returns string, baseline arguments for stringPict. - - left or right can be None or empty string which means 'no paren from - that side' - """ - h = self.height() - b = self.baseline - - # XXX this is a hack -- ascii parens are ugly! - if ifascii_nougly and not pretty_use_unicode(): - h = 1 - b = 0 - - res = self - - if left: - lparen = stringPict(vobj(left, h), baseline=b) - res = stringPict(*lparen.right(self)) - if right: - rparen = stringPict(vobj(right, h), baseline=b) - res = stringPict(*res.right(rparen)) - - return ('\n'.join(res.picture), res.baseline) -
    -
    [docs] def leftslash(self): - """Precede object by a slash of the proper size. - """ - # XXX not used anywhere ? - height = max( - self.baseline, - self.height() - 1 - self.baseline)*2 + 1 - slash = '\n'.join( - ' '*(height - i - 1) + xobj('/', 1) + ' '*i - for i in range(height) - ) - return self.left(stringPict(slash, height//2)) -
    -
    [docs] def root(self, n=None): - """Produce a nice root symbol. - Produces ugly results for big n inserts. - """ - # XXX not used anywhere - # XXX duplicate of root drawing in pretty.py - #put line over expression - result = self.above('_'*self.width()) - #construct right half of root symbol - height = self.height() - slash = '\n'.join( - ' ' * (height - i - 1) + '/' + ' ' * i - for i in range(height) - ) - slash = stringPict(slash, height - 1) - #left half of root symbol - if height > 2: - downline = stringPict('\\ \n \\', 1) - else: - downline = stringPict('\\') - #put n on top, as low as possible - if n is not None and n.width() > downline.width(): - downline = downline.left(' '*(n.width() - downline.width())) - downline = downline.above(n) - #build root symbol - root = downline.right(slash) - #glue it on at the proper height - #normally, the root symbel is as high as self - #which is one less than result - #this moves the root symbol one down - #if the root became higher, the baseline has to grow too - root.baseline = result.baseline - result.height() + root.height() - return result.left(root) -
    -
    [docs] def render(self, * args, **kwargs): - """Return the string form of self. - - Unless the argument line_break is set to False, it will - break the expression in a form that can be printed - on the terminal without being broken up. - """ - if kwargs["wrap_line"] is False: - return "\n".join(self.picture) - - if kwargs["num_columns"] is not None: - # Read the argument num_columns if it is not None - ncols = kwargs["num_columns"] - else: - # Attempt to get a terminal width - ncols = self.terminal_width() - - ncols -= 2 - if ncols <= 0: - ncols = 78 - - # If smaller than the terminal width, no need to correct - if self.width() <= ncols: - return type(self.picture[0])(self) - - # for one-line pictures we don't need v-spacers. on the other hand, for - # multiline-pictures, we need v-spacers between blocks, compare: - # - # 2 2 3 | a*c*e + a*c*f + a*d | a*c*e + a*c*f + a*d | 3.14159265358979323 - # 6*x *y + 4*x*y + | | *e + a*d*f + b*c*e | 84626433832795 - # | *e + a*d*f + b*c*e | + b*c*f + b*d*e + b | - # 3 4 4 | | *d*f | - # 4*y*x + x + y | + b*c*f + b*d*e + b | | - # | | | - # | *d*f - - i = 0 - svals = [] - do_vspacers = (self.height() > 1) - while i < self.width(): - svals.extend([ sval[i:i + ncols] for sval in self.picture ]) - if do_vspacers: - svals.append("") # a vertical spacer - i += ncols - - if svals[-1] == '': - del svals[-1] # Get rid of the last spacer - - return "\n".join(svals) -
    -
    [docs] def terminal_width(self): - """Return the terminal width if possible, otherwise return 0. - """ - ncols = 0 - try: - import curses - import io - try: - curses.setupterm() - ncols = curses.tigetnum('cols') - except AttributeError: - # windows curses doesn't implement setupterm or tigetnum - # code below from - # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440694 - from ctypes import windll, create_string_buffer - # stdin handle is -10 - # stdout handle is -11 - # stderr handle is -12 - h = windll.kernel32.GetStdHandle(-12) - csbi = create_string_buffer(22) - res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi) - if res: - import struct - (bufx, bufy, curx, cury, wattr, - left, top, right, bottom, maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw) - ncols = right - left + 1 - except curses.error: - pass - except io.UnsupportedOperation: - pass - except (ImportError, TypeError): - pass - return ncols -
    - def __eq__(self, o): - if isinstance(o, str): - return '\n'.join(self.picture) == o - elif isinstance(o, stringPict): - return o.picture == self.picture - return False - - def __hash__(self): - return super(stringPict, self).__hash__() - - def __str__(self): - return str.join('\n', self.picture) - - def __unicode__(self): - return str.join('\n', self.picture) - - def __repr__(self): - return "stringPict(%r,%d)" % ('\n'.join(self.picture), self.baseline) - - def __getitem__(self, index): - return self.picture[index] - - def __len__(self): - return len(self.s) - -
    -
    [docs]class prettyForm(stringPict): - """Extension of the stringPict class that knows about - basic math applications, optimizing double minus signs. - - "Binding" is interpreted as follows:: - - ATOM this is an atom: never needs to be parenthesized - FUNC this is a function application: parenthesize if added (?) - DIV this is a division: make wider division if divided - POW this is a power: only parenthesize if exponent - MUL this is a multiplication: parenthesize if powered - ADD this is an addition: parenthesize if multiplied or powered - NEG this is a negative number: optimize if added, parenthesize if multiplied or powered - - """ - ATOM, FUNC, DIV, POW, MUL, ADD, NEG = list(range(7)) - - def __init__(self, s, baseline=0, binding=0, str=None): - """Initialize from stringPict and binding power.""" - stringPict.__init__(self, s, baseline) - self.binding = binding - self.str = str or s - - def __add__(self, *others): - """Make a pretty addition. - Addition of negative numbers is simplified. - """ - arg = self - if arg.binding > prettyForm.NEG: - arg = stringPict(*arg.parens()) - result = [arg] - for arg in others: - #add parentheses for weak binders - if arg.binding > prettyForm.NEG: - arg = stringPict(*arg.parens()) - #use existing minus sign if available - if arg.binding != prettyForm.NEG: - result.append(' + ') - result.append(arg) - return prettyForm(binding=prettyForm.ADD, *stringPict.next(*result)) - - def __div__(self, den, slashed=False): - """Make a pretty division; stacked or slashed. - """ - if slashed: - raise NotImplementedError("Can't do slashed fraction yet") - num = self - if num.binding == prettyForm.DIV: - num = stringPict(*num.parens()) - if den.binding == prettyForm.DIV: - den = stringPict(*den.parens()) - - return prettyForm(binding=prettyForm.DIV, *stringPict.stack( - num, - stringPict.LINE, - den)) - - def __truediv__(self, o): - return self.__div__(o) - - def __mul__(self, *others): - """Make a pretty multiplication. - Parentheses are needed around +, - and neg. - """ - args = self - if args.binding > prettyForm.MUL: - arg = stringPict(*args.parens()) - result = [args] - for arg in others: - result.append(xsym('*')) - #add parentheses for weak binders - if arg.binding > prettyForm.MUL: - arg = stringPict(*arg.parens()) - result.append(arg) - len_res = len(result) - for i in range(len_res): - if i < len_res - 1 and result[i] == '-1' and result[i + 1] == xsym('*'): - # substitute -1 by -, like in -1*x -> -x - result.pop(i) - result.pop(i) - result.insert(i, '-') - if result[0][0] == '-': - # if there is a - sign in front of all - bin = prettyForm.NEG - else: - bin = prettyForm.MUL - return prettyForm(binding=bin, *stringPict.next(*result)) - - def __repr__(self): - return "prettyForm(%r,%d,%d)" % ( - '\n'.join(self.picture), - self.baseline, - self.binding) - - def __pow__(self, b): - """Make a pretty power. - """ - a = self - if a.binding > prettyForm.FUNC: - a = stringPict(*a.parens()) - if b.binding == prettyForm.POW: - b = stringPict(*b.parens()) - - if a.binding == prettyForm.FUNC: - # 2 <-- top - # sin (x) <-- bot - top = stringPict(*b.left(' '*a.prettyFunc.width())) - top = stringPict(*top.right(' '*a.prettyArgs.width())) - bot = stringPict(*a.prettyFunc.right(' '*b.width())) - bot = stringPict(*bot.right(a.prettyArgs)) - else: - # 2 <-- top - # (x+y) <-- bot - top = stringPict(*b.left(' '*a.width())) - bot = stringPict(*a.right(' '*b.width())) - - return prettyForm(binding=prettyForm.POW, *bot.above(top)) - - simpleFunctions = ["sin", "cos", "tan"] - - @staticmethod -
    [docs] def apply(function, *args): - """Functions of one or more variables. - """ - if function in prettyForm.simpleFunctions: - #simple function: use only space if possible - assert len( - args) == 1, "Simple function %s must have 1 argument" % function - arg = args[0].__pretty__() - if arg.binding <= prettyForm.DIV: - #optimization: no parentheses necessary - return prettyForm(binding=prettyForm.FUNC, *arg.left(function + ' ')) - argumentList = [] - for arg in args: - argumentList.append(',') - argumentList.append(arg.__pretty__()) - argumentList = stringPict(*stringPict.next(*argumentList[1:])) - argumentList = stringPict(*argumentList.parens()) - return prettyForm(binding=prettyForm.ATOM, *argumentList.left(function))
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/printing/preview.html b/dev-py3k/_modules/sympy/printing/preview.html deleted file mode 100644 index 8aec5b81b33..00000000000 --- a/dev-py3k/_modules/sympy/printing/preview.html +++ /dev/null @@ -1,331 +0,0 @@ - - - - - - - - - - sympy.printing.preview — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.printing.preview

    -import os
    -import time
    -import tempfile
    -
    -from .latex import latex
    -
    -
    -def preview(expr, output='png', viewer=None, euler=True, packages=(), **latex_settings):
    -    r"""
    -
    [docs] View expression or LaTeX markup in PNG, DVI, PostScript or PDF form. - - If the expr argument is an expression, it will be exported to LaTeX and - then compiled using available the TeX distribution. The first argument, - 'expr', may also be a LaTeX string. The function will then run the - appropriate viewer for the given output format or use the user defined - one. By default png output is generated. - - By default pretty Euler fonts are used for typesetting (they were used to - typeset the well known "Concrete Mathematics" book). For that to work, you - need the 'eulervm.sty' LaTeX style (in Debian/Ubuntu, install the - texlive-fonts-extra package). If you prefer default AMS fonts or your - system lacks 'eulervm' LaTeX package then unset the 'euler' keyword - argument. - - To use viewer auto-detection, lets say for 'png' output, issue - - >>> from sympy import symbols, preview, Symbol - >>> x, y = symbols("x,y") - - >>> preview(x + y, output='png') # doctest: +SKIP - - This will choose 'pyglet' by default. To select a different one, do - - >>> preview(x + y, output='png', viewer='gimp') # doctest: +SKIP - - The 'png' format is considered special. For all other formats the rules - are slightly different. As an example we will take 'dvi' output format. If - you would run - - >>> preview(x + y, output='dvi') # doctest: +SKIP - - then 'view' will look for available 'dvi' viewers on your system - (predefined in the function, so it will try evince, first, then kdvi and - xdvi). If nothing is found you will need to set the viewer explicitly. - - >>> preview(x + y, output='dvi', viewer='superior-dvi-viewer') # doctest: +SKIP - - This will skip auto-detection and will run user specified - 'superior-dvi-viewer'. If 'view' fails to find it on your system it will - gracefully raise an exception. You may also enter 'file' for the viewer - argument. Doing so will cause this function to return a file object in - read-only mode. - - Currently this depends on pexpect, which is not available for windows. - - Additional keyword args will be passed to the latex call, e.g., the - symbol_names flag. - - >>> phidd = Symbol('phidd') - >>> preview(phidd, symbol_names={phidd:r'\ddot{\varphi}'}) # doctest: +SKIP - - """ - - # we don't want to depend on anything not in the - # standard library with SymPy by default - import pexpect - - special = [ 'pyglet' ] - - if viewer is None: - if output == "png": - viewer = "pyglet" - else: - # sorted in order from most pretty to most ugly - # very discussable, but indeed 'gv' looks awful :) - candidates = { - "dvi": [ "evince", "okular", "kdvi", "xdvi" ], - "ps": [ "evince", "okular", "gsview", "gv" ], - "pdf": [ "evince", "okular", "kpdf", "acroread", "xpdf", "gv" ], - } - - try: - for candidate in candidates[output]: - if pexpect.which(candidate): - viewer = candidate - break - else: - raise SystemError( - "No viewers found for '%s' output format." % output) - except KeyError: - raise SystemError("Invalid output format: %s" % output) - else: - if viewer not in special and not pexpect.which(viewer): - raise SystemError("Unrecognized viewer: %s" % viewer) - - actual_packages = packages + ("amsmath", "amsfonts") - if euler: - actual_packages += ("euler",) - package_includes = "\n".join(["\\usepackage{%s}" % p - for p in actual_packages]) - - format = r"""\documentclass[12pt]{article} - %s - \begin{document} - \pagestyle{empty} - %s - \vfill - \end{document} - """ % (package_includes, "%s") - - if isinstance(expr, str): - latex_string = expr - else: - latex_string = latex(expr, mode='inline', **latex_settings) - - tmp = tempfile.mktemp() - - with open(tmp + ".tex", "w") as tex: - tex.write(format % latex_string) - - cwd = os.getcwd() - os.chdir(tempfile.gettempdir()) - - if os.system("latex -halt-on-error %s.tex" % tmp) != 0: - raise SystemError("Failed to generate DVI output.") - - os.remove(tmp + ".tex") - os.remove(tmp + ".aux") - os.remove(tmp + ".log") - - if output != "dvi": - command = { - "ps": "dvips -o %s.ps %s.dvi", - "pdf": "dvipdf %s.dvi %s.pdf", - "png": "dvipng -T tight -z 9 " + - "--truecolor -o %s.png %s.dvi", - } - - try: - if os.system(command[output] % (tmp, tmp)) != 0: - raise SystemError("Failed to generate '%s' output." % output) - else: - os.remove(tmp + ".dvi") - except KeyError: - raise SystemError("Invalid output format: %s" % output) - - src = "%s.%s" % (tmp, output) - src_file = None - - if viewer == "file": - src_file = open(src, 'rb') - elif viewer == "pyglet": - try: - from pyglet import window, image, gl - from pyglet.window import key - except ImportError: - raise ImportError("pyglet is required for plotting.\n visit http://www.pyglet.org/") - - if output == "png": - from pyglet.image.codecs.png import PNGImageDecoder - img = image.load(src, decoder=PNGImageDecoder()) - else: - raise SystemError("pyglet preview works only for 'png' files.") - - offset = 25 - - win = window.Window( - width=img.width + 2*offset, - height=img.height + 2*offset, - caption="sympy", - resizable=False - ) - - win.set_vsync(False) - - try: - def on_close(): - win.has_exit = True - - win.on_close = on_close - - def on_key_press(symbol, modifiers): - if symbol in [key.Q, key.ESCAPE]: - on_close() - - win.on_key_press = on_key_press - - def on_expose(): - gl.glClearColor(1.0, 1.0, 1.0, 1.0) - gl.glClear(gl.GL_COLOR_BUFFER_BIT) - - img.blit( - (win.width - img.width) / 2, - (win.height - img.height) / 2 - ) - - win.on_expose = on_expose - - while not win.has_exit: - win.dispatch_events() - win.flip() - except KeyboardInterrupt: - pass - - win.close() - else: - os.system("%s %s &> /dev/null &" % (viewer, src)) - time.sleep(2) # wait for the viewer to read data - - os.remove(src) - os.chdir(cwd) - - if src_file is not None: - return src_file -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    - - - - - diff --git a/dev-py3k/_modules/sympy/printing/printer.html b/dev-py3k/_modules/sympy/printing/printer.html deleted file mode 100644 index 709b58f8816..00000000000 --- a/dev-py3k/_modules/sympy/printing/printer.html +++ /dev/null @@ -1,386 +0,0 @@ - - - - - - - - - - sympy.printing.printer — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.printing.printer

    -"""Printing subsystem driver
    -
    -SymPy's printing system works the following way: Any expression can be
    -passed to a designated Printer who then is responsible to return an
    -adequate representation of that expression.
    -
    -The basic concept is the following:
    -  1. Let the object print itself if it knows how.
    -  2. Take the best fitting method defined in the printer.
    -  3. As fall-back use the emptyPrinter method for the printer.
    -
    -Some more information how the single concepts work and who should use which:
    -
    -1. The object prints itself
    -
    -    This was the original way of doing printing in sympy. Every class had
    -    its own latex, mathml, str and repr methods, but it turned out that it
    -    is hard to produce a high quality printer, if all the methods are spread
    -    out that far. Therefor all printing code was combined into the different
    -    printers, which works great for built-in sympy objects, but not that
    -    good for user defined classes where it is inconvenient to patch the
    -    printers.
    -
    -    Nevertheless, to get a fitting representation, the printers look for a
    -    specific method in every object, that will be called if it's available
    -    and is then responsible for the representation. The name of that method
    -    depends on the specific printer and is defined under
    -    Printer.printmethod.
    -
    -2. Take the best fitting method defined in the printer.
    -
    -    The printer loops through expr classes (class + its bases), and tries
    -    to dispatch the work to _print_<EXPR_CLASS>
    -
    -    e.g., suppose we have the following class hierarchy::
    -
    -            Basic
    -            |
    -            Atom
    -            |
    -            Number
    -            |
    -        Rational
    -
    -    then, for expr=Rational(...), in order to dispatch, we will try
    -    calling printer methods as shown in the figure below::
    -
    -        p._print(expr)
    -        |
    -        |-- p._print_Rational(expr)
    -        |
    -        |-- p._print_Number(expr)
    -        |
    -        |-- p._print_Atom(expr)
    -        |
    -        `-- p._print_Basic(expr)
    -
    -    if ._print_Rational method exists in the printer, then it is called,
    -    and the result is returned back.
    -
    -    otherwise, we proceed with trying Rational bases in the inheritance
    -    order.
    -
    -3. As fall-back use the emptyPrinter method for the printer.
    -
    -    As fall-back self.emptyPrinter will be called with the expression. If
    -    not defined in the Printer subclass this will be the same as str(expr).
    -"""
    -
    -from sympy import Basic, Add
    -
    -from sympy.core.core import BasicMeta
    -
    -from sympy.core.compatibility import cmp_to_key
    -
    -
    -
    [docs]class Printer(object): - """Generic printer - - Its job is to provide infrastructure for implementing new printers easily. - - Basically, if you want to implement a printer, all you have to do is: - - 1. Subclass Printer. - - 2. Define Printer.printmethod in your subclass. - If a object has a method with that name, this method will be used - for printing. - - 3. In your subclass, define ``_print_<CLASS>`` methods - - For each class you want to provide printing to, define an appropriate - method how to do it. For example if you want a class FOO to be printed in - its own way, define _print_FOO:: - - def _print_FOO(self, e): - ... - - this should return how FOO instance e is printed - - Also, if ``BAR`` is a subclass of ``FOO``, ``_print_FOO(bar)`` will - be called for instance of ``BAR``, if no ``_print_BAR`` is provided. - Thus, usually, we don't need to provide printing routines for every - class we want to support -- only generic routine has to be provided - for a set of classes. - - A good example for this are functions - for example ``PrettyPrinter`` - only defines ``_print_Function``, and there is no ``_print_sin``, - ``_print_tan``, etc... - - On the other hand, a good printer will probably have to define - separate routines for ``Symbol``, ``Atom``, ``Number``, ``Integral``, - ``Limit``, etc... - - 4. If convenient, override ``self.emptyPrinter`` - - This callable will be called to obtain printing result as a last resort, - that is when no appropriate print method was found for an expression. - - Examples of overloading StrPrinter:: - - from sympy import Basic, Function, Symbol - from sympy.printing.str import StrPrinter - - class CustomStrPrinter(StrPrinter): - \"\"\" - Examples of how to customize the StrPrinter for both a SymPy class and a - user defined class subclassed from the SymPy Basic class. - \"\"\" - - def _print_Derivative(self, expr): - \"\"\" - Custom printing of the SymPy Derivative class. - - Instead of: - - D(x(t), t) or D(x(t), t, t) - - We will print: - - x' or x'' - - In this example, expr.args == (x(t), t), and expr.args[0] == x(t), and - expr.args[0].func == x - \"\"\" - return str(expr.args[0].func) + "'"*len(expr.args[1:]) - - def _print_MyClass(self, expr): - \"\"\" - Print the characters of MyClass.s alternatively lower case and upper - case - \"\"\" - s = "" - i = 0 - for char in expr.s: - if i % 2 == 0: - s += char.lower() - else: - s += char.upper() - i += 1 - return s - - # Override the __str__ method of to use CustromStrPrinter - Basic.__str__ = lambda self: CustomStrPrinter().doprint(self) - # Demonstration of CustomStrPrinter: - t = Symbol('t') - x = Function('x')(t) - dxdt = x.diff(t) # dxdt is a Derivative instance - d2xdt2 = dxdt.diff(t) # dxdt2 is a Derivative instance - ex = MyClass('I like both lowercase and upper case') - - print dxdt - print d2xdt2 - print ex - - The output of the above code is:: - - x' - x'' - i lIkE BoTh lOwErCaSe aNd uPpEr cAsE - - By overriding Basic.__str__, we can customize the printing of anything that - is subclassed from Basic. - - """ - - _global_settings = {} - - _default_settings = {} - - emptyPrinter = str - printmethod = None - - def __init__(self, settings=None): - self._str = str - - self._settings = self._default_settings.copy() - - for key, val in self._global_settings.items(): - if key in self._default_settings: - self._settings[key] = val - - if settings is not None: - self._settings.update(settings) - - if len(self._settings) > len(self._default_settings): - for key in self._settings: - if key not in self._default_settings: - raise TypeError("Unknown setting '%s'." % key) - - # _print_level is the number of times self._print() was recursively - # called. See StrPrinter._print_Float() for an example of usage - self._print_level = 0 - - @classmethod -
    [docs] def set_global_settings(cls, **settings): - """Set system-wide printing settings. """ - for key, val in settings.items(): - if val is not None: - cls._global_settings[key] = val -
    - @property - def order(self): - if 'order' in self._settings: - return self._settings['order'] - else: - raise AttributeError("No order defined.") - -
    [docs] def doprint(self, expr): - """Returns printer's representation for expr (as a string)""" - return self._str(self._print(expr)) -
    -
    [docs] def _print(self, expr, *args): - """Internal dispatcher - - Tries the following concepts to print an expression: - 1. Let the object print itself if it knows how. - 2. Take the best fitting method defined in the printer. - 3. As fall-back use the emptyPrinter method for the printer. - """ - self._print_level += 1 - try: - # If the printer defines a name for a printing method - # (Printer.printmethod) and the object knows for itself how it - # should be printed, use that method. - if (self.printmethod and hasattr(expr, self.printmethod) - and not isinstance(expr, BasicMeta)): - return getattr(expr, self.printmethod)(self, *args) - - # See if the class of expr is known, or if one of its super - # classes is known, and use that print function - for cls in type(expr).__mro__: - printmethod = '_print_' + cls.__name__ - if hasattr(self, printmethod): - return getattr(self, printmethod)(expr, *args) - - # Unknown object, fall back to the emptyPrinter. - return self.emptyPrinter(expr) - finally: - self._print_level -= 1 -
    - def _as_ordered_terms(self, expr, order=None): - """A compatibility function for ordering terms in Add. """ - order = order or self.order - - if order == 'old': - return sorted(Add.make_args(expr), key=cmp_to_key(Basic._compare_pretty)) - else: - return expr.as_ordered_terms(order=order)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/printing/repr.html b/dev-py3k/_modules/sympy/printing/repr.html deleted file mode 100644 index 1b153cf8342..00000000000 --- a/dev-py3k/_modules/sympy/printing/repr.html +++ /dev/null @@ -1,266 +0,0 @@ - - - - - - - - - - sympy.printing.repr — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.printing.repr

    -"""
    -A Printer for generating executable code.
    -
    -The most important function here is srepr that returns a string so that the
    -relation eval(srepr(expr))=expr holds in an appropriate environment.
    -"""
    -
    -from .printer import Printer
    -import sympy.mpmath.libmp as mlib
    -from sympy.mpmath.libmp import prec_to_dps, repr_dps
    -
    -
    -
    [docs]class ReprPrinter(Printer): - printmethod = "_sympyrepr" - - _default_settings = { - "order": None - } - -
    [docs] def reprify(self, args, sep): - """ - Prints each item in `args` and joins them with `sep`. - """ - return sep.join([self.doprint(item) for item in args]) -
    -
    [docs] def emptyPrinter(self, expr): - """ - The fallback printer. - """ - if isinstance(expr, str): - return expr - elif hasattr(expr, "__srepr__"): - return expr.__srepr__() - elif hasattr(expr, "args") and hasattr(expr.args, "__iter__"): - l = [] - for o in expr.args: - l.append(self._print(o)) - return expr.__class__.__name__ + '(%s)' % ', '.join(l) - elif hasattr(expr, "__module__") and hasattr(expr, "__name__"): - return "<'%s.%s'>" % (expr.__module__, expr.__name__) - else: - return str(expr) -
    - def _print_Add(self, expr, order=None): - args = self._as_ordered_terms(expr, order=order) - args = list(map(self._print, args)) - return "Add(%s)" % ", ".join(args) - - def _print_Function(self, expr): - r = self._print(expr.func) - r += '(%s)' % ', '.join([self._print(a) for a in expr.args]) - return r - - def _print_FunctionClass(self, expr): - return 'Function(%r)' % (expr.__name__) - - def _print_Half(self, expr): - return 'Rational(1, 2)' - - def _print_RationalConstant(self, expr): - return str(expr) - - def _print_AtomicExpr(self, expr): - return str(expr) - - def _print_NumberSymbol(self, expr): - return str(expr) - - def _print_Integer(self, expr): - return 'Integer(%i)' % expr.p - - def _print_list(self, expr): - return "[%s]" % self.reprify(expr, ", ") - - def _print_MatrixBase(self, expr): - l = [] - for i in range(expr.rows): - l.append([]) - for j in range(expr.cols): - l[-1].append(expr[i, j]) - return '%s(%s)' % (expr.__class__.__name__, self._print(l)) - - _print_SparseMatrix = \ - _print_MutableSparseMatrix = \ - _print_ImmutableSparseMatrix = \ - _print_Matrix = \ - _print_DenseMatrix = \ - _print_MutableDenseMatrix = \ - _print_ImmutableMatrix = \ - _print_ImmutableDenseMatrix = \ - _print_MatrixBase - - def _print_NaN(self, expr): - return "nan" - - def _print_Rational(self, expr): - return 'Rational(%s, %s)' % (self._print(expr.p), self._print(expr.q)) - - def _print_Mul(self, expr, order=None): - terms = expr.args - if self.order != 'old': - args = expr._new_rawargs(*terms).as_ordered_factors() - else: - args = terms - - args = list(map(self._print, args)) - return "Mul(%s)" % ", ".join(args) - - def _print_Fraction(self, expr): - return 'Fraction(%s, %s)' % (self._print(expr.numerator), self._print(expr.denominator)) - - def _print_Float(self, expr): - dps = prec_to_dps(expr._prec) - r = mlib.to_str(expr._mpf_, repr_dps(expr._prec)) - return "%s('%s', prec=%i)" % (expr.__class__.__name__, r, dps) - - def _print_Sum2(self, expr): - return "Sum2(%s, (%s, %s, %s))" % (self._print(expr.f), self._print(expr.i), - self._print(expr.a), self._print(expr.b)) - - def _print_Symbol(self, expr): - return "%s(%s)" % (expr.__class__.__name__, self._print(expr.name)) - - def _print_Predicate(self, expr): - return "%s(%s)" % (expr.__class__.__name__, self._print(expr.name)) - - def _print_AppliedPredicate(self, expr): - return "%s(%s, %s)" % (expr.__class__.__name__, expr.func, expr.arg) - - def _print_str(self, expr): - return repr(expr) - - def _print_tuple(self, expr): - if len(expr) == 1: - return "(%s,)" % self._print(expr[0]) - else: - return "(%s)" % self.reprify(expr, ", ") - - def _print_WildFunction(self, expr): - return "%s('%s')" % (expr.__class__.__name__, expr.name) - - def _print_AlgebraicNumber(self, expr): - return "%s(%s, %s)" % (self.__class__.__name__, - self._print(self.coeffs()), self._print(expr.root)) - -
    -
    [docs]def srepr(expr, **settings): - """return expr in repr form""" - return ReprPrinter(settings).doprint(expr)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/printing/str.html b/dev-py3k/_modules/sympy/printing/str.html deleted file mode 100644 index 8eb786cc1e8..00000000000 --- a/dev-py3k/_modules/sympy/printing/str.html +++ /dev/null @@ -1,795 +0,0 @@ - - - - - - - - - - sympy.printing.str — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.printing.str

    -"""
    -A Printer for generating readable representation of most sympy classes.
    -"""
    -
    -from sympy.core import S, Rational, Pow, Basic, Mul
    -from sympy.core.mul import _keep_coeff
    -from sympy.core.numbers import Integer
    -from .printer import Printer
    -from sympy.printing.precedence import precedence, PRECEDENCE
    -
    -import sympy.mpmath.libmp as mlib
    -from sympy.mpmath.libmp import prec_to_dps
    -
    -from sympy.polys.polyerrors import PolynomialError
    -
    -from sympy.utilities import default_sort_key
    -
    -
    -
    [docs]class StrPrinter(Printer): - printmethod = "_sympystr" - _default_settings = { - "order": None, - "full_prec": "auto", - } - - def parenthesize(self, item, level): - if precedence(item) <= level: - return "(%s)" % self._print(item) - else: - return self._print(item) - - def stringify(self, args, sep, level=0): - return sep.join([self.parenthesize(item, level) for item in args]) - - def emptyPrinter(self, expr): - if isinstance(expr, str): - return expr - elif isinstance(expr, Basic): - if hasattr(expr, "args"): - return repr(expr) - else: - raise - else: - return str(expr) - - def _print_Add(self, expr, order=None): - if self.order == 'none': - terms = list(expr.args) - else: - terms = self._as_ordered_terms(expr, order=order) - - PREC = precedence(expr) - l = [] - for term in terms: - t = self._print(term) - if t.startswith('-'): - sign = "-" - t = t[1:] - else: - sign = "+" - if precedence(term) < PREC: - l.extend([sign, "(%s)" % t]) - else: - l.extend([sign, t]) - sign = l.pop(0) - if sign == '+': - sign = "" - return sign + ' '.join(l) - - def _print_And(self, expr): - return '%s(%s)' % (expr.func, ', '.join(sorted(self._print(a) for a in - expr.args))) - - def _print_Or(self, expr): - return '%s(%s)' % (expr.func, ', '.join(sorted(self._print(a) for a in - expr.args))) - - def _print_AppliedPredicate(self, expr): - return '%s(%s)' % (expr.func, expr.arg) - - def _print_Basic(self, expr): - l = [self._print(o) for o in expr.args] - return expr.__class__.__name__ + "(%s)" % ", ".join(l) - - def _print_BlockMatrix(self, B): - if B.blocks.shape == (1, 1): - self._print(B.blocks[0, 0]) - return self._print(B.blocks) - - def _print_Catalan(self, expr): - return 'Catalan' - - def _print_ComplexInfinity(self, expr): - return 'zoo' - - def _print_Derivative(self, expr): - return 'Derivative(%s)' % ", ".join(map(self._print, expr.args)) - - def _print_dict(self, d): - keys = sorted(list(d.keys()), key=default_sort_key) - items = [] - - for key in keys: - item = "%s: %s" % (self._print(key), self._print(d[key])) - items.append(item) - - return "{%s}" % ", ".join(items) - - def _print_Dict(self, expr): - return self._print_dict(expr) - - def _print_RandomDomain(self, d): - try: - return 'Domain: ' + self._print(d.as_boolean()) - except: - try: - return ('Domain: ' + self._print(d.symbols) + ' in ' + - self._print(d.set)) - except: - return 'Domain on ' + self._print(d.symbols) - - def _print_Dummy(self, expr): - return '_' + expr.name - - def _print_EulerGamma(self, expr): - return 'EulerGamma' - - def _print_Exp1(self, expr): - return 'E' - - def _print_ExprCondPair(self, expr): - return '(%s, %s)' % (expr.expr, expr.cond) - - def _print_subfactorial(self, expr): - return "!%s" % self.parenthesize(expr.args[0], PRECEDENCE["Pow"]) - - def _print_factorial(self, expr): - return "%s!" % self.parenthesize(expr.args[0], PRECEDENCE["Pow"]) - - def _print_FiniteSet(self, s): - s = sorted(s, key=default_sort_key) - if len(s) > 10: - printset = s[:3] + ['...'] + s[-3:] - else: - printset = s - return '{' + ', '.join(self._print(el) for el in printset) + '}' - - def _print_Function(self, expr): - return expr.func.__name__ + "(%s)" % self.stringify(expr.args, ", ") - - def _print_GeometryEntity(self, expr): - # GeometryEntity is special -- it's base is tuple - return str(expr) - - def _print_GoldenRatio(self, expr): - return 'GoldenRatio' - - def _print_ImaginaryUnit(self, expr): - return 'I' - - def _print_Infinity(self, expr): - return 'oo' - - def _print_Integral(self, expr): - def _xab_tostr(xab): - if len(xab) == 1: - return self._print(xab[0]) - else: - return self._print((xab[0],) + tuple(xab[1:])) - L = ', '.join([_xab_tostr(l) for l in expr.limits]) - return 'Integral(%s, %s)' % (self._print(expr.function), L) - - def _print_Interval(self, i): - if i.left_open: - left = '(' - else: - left = '[' - - if i.right_open: - right = ')' - else: - right = ']' - - return "%s%s, %s%s" % \ - (left, self._print(i.start), self._print(i.end), right) - - def _print_Inverse(self, I): - return "%s^-1" % self.parenthesize(I.arg, PRECEDENCE["Pow"]) - - def _print_Lambda(self, obj): - args, expr = obj.args - if len(args) == 1: - return "Lambda(%s, %s)" % (args.args[0], expr) - else: - arg_string = ", ".join(self._print(arg) for arg in args) - return "Lambda((%s), %s" % (arg_string, expr) - - def _print_LatticeOp(self, expr): - args = sorted(expr.args, key=default_sort_key) - return expr.func.__name__ + "(%s)" % ", ".join(self._print(arg) for arg in args) - - def _print_Limit(self, expr): - e, z, z0, dir = expr.args - if str(dir) == "+": - return "Limit(%s, %s, %s)" % (e, z, z0) - else: - return "Limit(%s, %s, %s, dir='%s')" % (e, z, z0, dir) - - def _print_list(self, expr): - return "[%s]" % self.stringify(expr, ", ") - - def _print_MatrixBase(self, expr): - return expr._format_str(lambda elem: self._print(elem)) - _print_SparseMatrix = \ - _print_MutableSparseMatrix = \ - _print_ImmutableSparseMatrix = \ - _print_Matrix = \ - _print_DenseMatrix = \ - _print_MutableDenseMatrix = \ - _print_ImmutableMatrix = \ - _print_ImmutableDenseMatrix = \ - _print_MatrixBase - - def _print_DeferredVector(self, expr): - return expr.name - - def _print_Mul(self, expr): - - prec = precedence(expr) - - c, e = expr.as_coeff_Mul() - if c < 0: - expr = _keep_coeff(-c, e) - sign = "-" - else: - sign = "" - - a = [] # items in the numerator - b = [] # items that are in the denominator (if any) - - if self.order not in ('old', 'none'): - args = expr.as_ordered_factors() - else: - # use make_args in case expr was something like -x -> x - args = Mul.make_args(expr) - - # Gather args for numerator/denominator - for item in args: - if item.is_commutative and item.is_Pow and item.exp.is_Rational and item.exp.is_negative: - if item.exp != -1: - b.append(Pow(item.base, -item.exp, evaluate=False)) - else: - b.append(Pow(item.base, -item.exp)) - elif item.is_Rational and item is not S.Infinity: - if item.p != 1: - a.append(Rational(item.p)) - if item.q != 1: - b.append(Rational(item.q)) - else: - a.append(item) - - a = a or [S.One] - - a_str = [self.parenthesize(x, prec) for x in a] - b_str = [self.parenthesize(x, prec) for x in b] - - if len(b) == 0: - return sign + '*'.join(a_str) - elif len(b) == 1: - if len(a) == 1 and not (a[0].is_Atom or a[0].is_Add): - return sign + "%s/" % a_str[0] + '*'.join(b_str) - else: - return sign + '*'.join(a_str) + "/%s" % b_str[0] - else: - return sign + '*'.join(a_str) + "/(%s)" % '*'.join(b_str) - - def _print_MatMul(self, expr): - return '*'.join([self.parenthesize(arg, precedence(expr)) - for arg in expr.args]) - - def _print_HadamardProduct(self, expr): - return '.*'.join([self.parenthesize(arg, precedence(expr)) - for arg in expr.args]) - - def _print_MatAdd(self, expr): - return ' + '.join([self.parenthesize(arg, precedence(expr)) - for arg in expr.args]) - - def _print_NaN(self, expr): - return 'nan' - - def _print_NegativeInfinity(self, expr): - return '-oo' - - def _print_Normal(self, expr): - return "Normal(%s, %s)" % (expr.mu, expr.sigma) - - def _print_Order(self, expr): - if len(expr.variables) <= 1: - return 'O(%s)' % self._print(expr.expr) - else: - return 'O(%s)' % self.stringify(expr.args, ', ', 0) - - def _print_Cycle(self, expr): - """We want it to print as Cycle in doctests for which a repr is required. - - With __repr__ defined in Cycle, interactive output gives Cycle form but - during doctests, the dict's __repr__ form is used. Defining this _print - function solves that problem. - - >>> from sympy.combinatorics import Cycle - >>> Cycle(1, 2) # will print as a dict without this method - Cycle(1, 2) - """ - return expr.__repr__() - - def _print_Permutation(self, expr): - from sympy.combinatorics.permutations import Permutation, Cycle - if Permutation.print_cyclic: - if not expr.size: - return 'Permutation()' - # before taking Cycle notation, see if the last element is - # a singleton and move it to the head of the string - s = Cycle(expr)(expr.size - 1).__repr__()[len('Cycle'):] - last = s.rfind('(') - if not last == 0 and ',' not in s[last:]: - s = s[last:] + s[:last] - return 'Permutation%s' % s - else: - s = expr.support() - if not s: - if expr.size < 5: - return 'Permutation(%s)' % str(expr.array_form) - return 'Permutation([], size=%s)' % expr.size - trim = str(expr.array_form[:s[-1] + 1]) + ', size=%s' % expr.size - use = full = str(expr.array_form) - if len(trim) < len(full): - use = trim - return 'Permutation(%s)' % use - - def _print_PermutationGroup(self, expr): - p = [' %s' % str(a) for a in expr.args] - return 'PermutationGroup([\n%s])' % ',\n'.join(p) - - def _print_PDF(self, expr): - return 'PDF(%s, (%s, %s, %s))' % \ - (self._print(expr.pdf.args[1]), self._print(expr.pdf.args[0]), - self._print(expr.domain[0]), self._print(expr.domain[1])) - - def _print_Pi(self, expr): - return 'pi' - - def _print_Poly(self, expr): - terms, gens = [], [ self._print(s) for s in expr.gens ] - - for monom, coeff in expr.terms(): - s_monom = [] - - for i, exp in enumerate(monom): - if exp > 0: - if exp == 1: - s_monom.append(gens[i]) - else: - s_monom.append(gens[i] + "**%d" % exp) - - s_monom = "*".join(s_monom) - - if coeff.is_Add: - if s_monom: - s_coeff = "(" + self._print(coeff) + ")" - else: - s_coeff = self._print(coeff) - else: - if s_monom: - if coeff is S.One: - terms.extend(['+', s_monom]) - continue - - if coeff is S.NegativeOne: - terms.extend(['-', s_monom]) - continue - - s_coeff = self._print(coeff) - - if not s_monom: - s_term = s_coeff - else: - s_term = s_coeff + "*" + s_monom - - if s_term.startswith('-'): - terms.extend(['-', s_term[1:]]) - else: - terms.extend(['+', s_term]) - - if terms[0] in ['-', '+']: - modifier = terms.pop(0) - - if modifier == '-': - terms[0] = '-' + terms[0] - - format = expr.__class__.__name__ + "(%s, %s" - - try: - format += ", modulus=%s" % expr.get_modulus() - except PolynomialError: - format += ", domain='%s'" % expr.get_domain() - - format += ")" - - return format % (' '.join(terms), ', '.join(gens)) - - def _print_ProductSet(self, p): - return ' x '.join(self._print(set) for set in p.sets) - - def _print_AlgebraicNumber(self, expr): - if expr.is_aliased: - return self._print(expr.as_poly().as_expr()) - else: - return self._print(expr.as_expr()) - - def _print_Pow(self, expr, rational=False): - PREC = precedence(expr) - - if expr.exp is S.Half and not rational: - return "sqrt(%s)" % self._print(expr.base) - - if expr.is_commutative: - if -expr.exp is S.Half and not rational: - # Note: Don't test "expr.exp == -S.Half" here, because that will - # match -0.5, which we don't want. - return "1/sqrt(%s)" % self._print(expr.base) - if expr.exp == -1: - return '1/%s' % self.parenthesize(expr.base, PREC) - - e = self.parenthesize(expr.exp, PREC) - if self.printmethod == '_sympyrepr' and expr.exp.is_Rational and expr.exp.q != 1: - # the parenthesized exp should be '(Rational(a, b))' so strip parens, - # but just check to be sure. - if e.startswith('(Rational'): - return '%s**%s' % (self.parenthesize(expr.base, PREC), e[1:-1]) - return '%s**%s' % (self.parenthesize(expr.base, PREC), e) - - def _print_MatPow(self, expr): - PREC = precedence(expr) - return '%s**%s' % (self.parenthesize(expr.base, PREC), - self.parenthesize(expr.exp, PREC)) - - def _print_Integer(self, expr): - return str(expr.p) - - def _print_int(self, expr): - return str(expr) - - def _print_mpz(self, expr): - return str(expr) - - def _print_Rational(self, expr): - return '%s/%s' % (expr.p, expr.q) - - def _print_Fraction(self, expr): - return '%s/%s' % (expr.numerator, expr.denominator) - - def _print_mpq(self, expr): - return '%s/%s' % (expr.numer(), expr.denom()) - - def _print_Float(self, expr): - prec = expr._prec - if prec < 5: - dps = 0 - else: - dps = prec_to_dps(expr._prec) - if self._settings["full_prec"] is True: - strip = False - elif self._settings["full_prec"] is False: - strip = True - elif self._settings["full_prec"] == "auto": - strip = self._print_level > 1 - rv = mlib.to_str(expr._mpf_, dps, strip_zeros=strip) - if rv.startswith('-.0'): - rv = '-0.' + rv[3:] - elif rv.startswith('.0'): - rv = '0.' + rv[2:] - return rv - - def _print_Relational(self, expr): - return '%s %s %s' % (self.parenthesize(expr.lhs, precedence(expr)), - expr.rel_op, - self.parenthesize(expr.rhs, precedence(expr))) - - def _print_RootOf(self, expr): - return "RootOf(%s, %d)" % (self._print_Add(expr.expr, order='lex'), expr.index) - - def _print_RootSum(self, expr): - args = [self._print_Add(expr.expr, order='lex')] - - if expr.fun is not S.IdentityFunction: - args.append(self._print(expr.fun)) - - return "RootSum(%s)" % ", ".join(args) - - def _print_GroebnerBasis(self, basis): - cls = basis.__class__.__name__ - - exprs = [ self._print_Add(arg, order=basis.order) - for arg in basis.exprs ] - exprs = "[%s]" % ", ".join(exprs) - - gens = [ self._print(gen) for gen in basis.gens ] - domain = "domain='%s'" % self._print(basis.domain) - order = "order='%s'" % self._print(basis.order) - - args = [exprs] + gens + [domain, order] - - return "%s(%s)" % (cls, ", ".join(args)) - - def _print_Sample(self, expr): - return "Sample([%s])" % self.stringify(expr, ", ", 0) - - def _print_set(self, s): - items = sorted(s, key=default_sort_key) - - args = ', '.join(self._print(item) for item in items) - if args: - args = '[%s]' % args - return '%s(%s)' % (type(s).__name__, args) - - _print_frozenset = _print_set - - def _print_SparseMatrix(self, expr): - from sympy.matrices import Matrix - return self._print(Matrix(expr)) - - def _print_Sum(self, expr): - def _xab_tostr(xab): - if len(xab) == 1: - return self._print(xab[0]) - else: - return self._print((xab[0],) + tuple(xab[1:])) - L = ', '.join([_xab_tostr(l) for l in expr.limits]) - return 'Sum(%s, %s)' % (self._print(expr.function), L) - - def _print_Symbol(self, expr): - return expr.name - _print_MatrixSymbol = _print_Symbol - - def _print_Identity(self, expr): - return "I" - - def _print_ZeroMatrix(self, expr): - return "0" - - def _print_Predicate(self, expr): - return "Q.%s" % expr.name - - def _print_str(self, expr): - return expr - - def _print_tuple(self, expr): - if len(expr) == 1: - return "(%s,)" % self._print(expr[0]) - else: - return "(%s)" % self.stringify(expr, ", ") - - def _print_Tuple(self, expr): - return self._print_tuple(expr) - - def _print_Transpose(self, T): - return "%s'" % self.parenthesize(T.arg, PRECEDENCE["Pow"]) - - def _print_Uniform(self, expr): - return "Uniform(%s, %s)" % (expr.a, expr.b) - - def _print_Union(self, expr): - return ' U '.join(self._print(set) for set in expr.args) - - def _print_Unit(self, expr): - return expr.abbrev - - def _print_Wild(self, expr): - return expr.name + '_' - - def _print_WildFunction(self, expr): - return expr.name + '_' - - def _print_Zero(self, expr): - return "0" - - def _print_DMP(self, p): - from sympy.core.sympify import SympifyError - try: - if p.ring is not None: - # TODO incorporate order - return self._print(p.ring.to_sympy(p)) - except SympifyError: - pass - - cls = p.__class__.__name__ - rep = self._print(p.rep) - dom = self._print(p.dom) - ring = self._print(p.ring) - - return "%s(%s, %s, %s)" % (cls, rep, dom, ring) - - def _print_DMF(self, expr): - return self._print_DMP(expr) - - def _print_Object(self, object): - return 'Object("%s")' % object.name - - def _print_IdentityMorphism(self, morphism): - return 'IdentityMorphism(%s)' % morphism.domain - - def _print_NamedMorphism(self, morphism): - return 'NamedMorphism(%s, %s, "%s")' % \ - (morphism.domain, morphism.codomain, morphism.name) - - def _print_Category(self, category): - return 'Category("%s")' % category.name - - def _print_BaseScalarField(self, field): - return field._coord_sys._names[field._index] - - def _print_BaseVectorField(self, field): - return 'e_%s' % field._coord_sys._names[field._index] - - def _print_Differential(self, diff): - field = diff._form_field - if hasattr(field, '_coord_sys'): - return 'd%s' % field._coord_sys._names[field._index] - else: - return 'd(%s)' % self._print(field) - - def _print_Tr(self, expr): - #TODO : Handle indices - return "%s(%s)" % ("Tr", self._print(expr.args[0])) - -
    -def sstr(expr, **settings): - """Returns the expression as a string. - - For large expressions where speed is a concern, use the setting - order='none'. - - Examples - ======== - - >>> from sympy import symbols, Eq, sstr - >>> a, b = symbols('a b') - >>> sstr(Eq(a + b, 0)) - 'a + b == 0' - """ - - p = StrPrinter(settings) - s = p.doprint(expr) - - return s - - -class StrReprPrinter(StrPrinter): - """(internal) -- see sstrrepr""" - - def _print_str(self, s): - return repr(s) - - -
    [docs]def sstrrepr(expr, **settings): - """return expr in mixed str/repr form - - i.e. strings are returned in repr form with quotes, and everything else - is returned in str form. - - This function could be useful for hooking into sys.displayhook - """ - - p = StrReprPrinter(settings) - s = p.doprint(expr) - - return s
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/printing/tree.html b/dev-py3k/_modules/sympy/printing/tree.html deleted file mode 100644 index 0c2ed0c1075..00000000000 --- a/dev-py3k/_modules/sympy/printing/tree.html +++ /dev/null @@ -1,214 +0,0 @@ - - - - - - - - - - sympy.printing.tree — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.printing.tree

    -def pprint_nodes(subtrees):
    -    """
    -
    [docs] Prettyprints systems of nodes. - - Examples - ======== - - >>> from sympy.printing.tree import pprint_nodes - >>> print(pprint_nodes(["a", "b1\\nb2", "c"])) - +-a - +-b1 - | b2 - +-c - - """ - def indent(s, type=1): - x = s.split("\n") - r = "+-%s\n" % x[0] - for a in x[1:]: - if a == "": - continue - if type == 1: - r += "| %s\n" % a - else: - r += " %s\n" % a - return r - if len(subtrees) == 0: - return "" - f = "" - for a in subtrees[:-1]: - f += indent(a) - f += indent(subtrees[-1], 2) - return f - - -def print_node(node): - """
    - -
    [docs] Returns a tree representation of "node" as a string. - - It uses print_node() together with pprint_nodes() on node.args recursively. - - See also: print_tree() - """ - subtrees = [] - for arg in node.args: - subtrees.append(tree(arg)) - s = print_node(node) + pprint_nodes(subtrees) - return s - - -def print_tree(node): - """
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    - - - - - diff --git a/dev-py3k/_modules/sympy/series/acceleration.html b/dev-py3k/_modules/sympy/series/acceleration.html deleted file mode 100644 index 86df3dc7e63..00000000000 --- a/dev-py3k/_modules/sympy/series/acceleration.html +++ /dev/null @@ -1,213 +0,0 @@ - - - - - - - - - - sympy.series.acceleration — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.series.acceleration

    -"""
    -Convergence acceleration / extrapolation methods for series and
    -sequences.
    -
    -References:
    -Carl M. Bender & Steven A. Orszag, "Advanced Mathematical Methods for
    -Scientists and Engineers: Asymptotic Methods and Perturbation Theory",
    -Springer 1999. (Shanks transformation: pp. 368-375, Richardson
    -extrapolation: pp. 375-377.)
    -"""
    -
    -from sympy import factorial, Integer, S
    -
    -
    -
    [docs]def richardson(A, k, n, N): - """ - Calculate an approximation for lim k->oo A(k) using Richardson - extrapolation with the terms A(n), A(n+1), ..., A(n+N+1). - Choosing N ~= 2*n often gives good results. - - A simple example is to calculate exp(1) using the limit definition. - This limit converges slowly; n = 100 only produces two accurate - digits: - - >>> from sympy.abc import n - >>> e = (1 + 1/n)**n - >>> print(round(e.subs(n, 100).evalf(), 10)) - 2.7048138294 - - Richardson extrapolation with 11 appropriately chosen terms gives - a value that is accurate to the indicated precision: - - >>> from sympy import E - >>> from sympy.series.acceleration import richardson - >>> print(round(richardson(e, n, 10, 20).evalf(), 10)) - 2.7182818285 - >>> print(round(E.evalf(), 10)) - 2.7182818285 - - Another useful application is to speed up convergence of series. - Computing 100 terms of the zeta(2) series 1/k**2 yields only - two accurate digits: - - >>> from sympy.abc import k, n - >>> from sympy import Sum - >>> A = Sum(k**-2, (k, 1, n)) - >>> print(round(A.subs(n, 100).evalf(), 10)) - 1.6349839002 - - Richardson extrapolation performs much better: - - >>> from sympy import pi - >>> print(round(richardson(A, n, 10, 20).evalf(), 10)) - 1.6449340668 - >>> print(round(((pi**2)/6).evalf(), 10)) # Exact value - 1.6449340668 - - """ - s = S.Zero - for j in range(0, N + 1): - s += A.subs(k, Integer(n + j)).doit() * (n + j)**N * (-1)**(j + N) / \ - (factorial(j) * factorial(N - j)) - return s - -
    -
    [docs]def shanks(A, k, n, m=1): - """ - Calculate an approximation for lim k->oo A(k) using the n-term Shanks - transformation S(A)(n). With m > 1, calculate the m-fold recursive - Shanks transformation S(S(...S(A)...))(n). - - The Shanks transformation is useful for summing Taylor series that - converge slowly near a pole or singularity, e.g. for log(2): - - >>> from sympy.abc import k, n - >>> from sympy import Sum, Integer - >>> from sympy.series.acceleration import shanks - >>> A = Sum(Integer(-1)**(k+1) / k, (k, 1, n)) - >>> print(round(A.subs(n, 100).doit().evalf(), 10)) - 0.6881721793 - >>> print(round(shanks(A, n, 25).evalf(), 10)) - 0.6931396564 - >>> print(round(shanks(A, n, 25, 5).evalf(), 10)) - 0.6931471806 - - The correct value is 0.6931471805599453094172321215. - """ - table = [A.subs(k, Integer(j)).doit() for j in range(n + m + 2)] - table2 = table[:] - - for i in range(1, m + 1): - for j in range(i, n + m + 1): - x, y, z = table[j - 1], table[j], table[j + 1] - table2[j] = (z*x - y**2) / (z + x - 2*y) - table = table2[:] - return table[n]
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/series/gruntz.html b/dev-py3k/_modules/sympy/series/gruntz.html deleted file mode 100644 index d852919b962..00000000000 --- a/dev-py3k/_modules/sympy/series/gruntz.html +++ /dev/null @@ -1,832 +0,0 @@ - - - - - - - - - - sympy.series.gruntz — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.series.gruntz

    -"""
    -Limits
    -======
    -
    -Implemented according to the PhD thesis
    -http://www.cybertester.com/data/gruntz.pdf, which contains very thorough
    -descriptions of the algorithm including many examples.  We summarize here
    -the gist of it.
    -
    -All functions are sorted according to how rapidly varying they are at
    -infinity using the following rules. Any two functions f and g can be
    -compared using the properties of L:
    -
    -L=lim  log|f(x)| / log|g(x)|           (for x -> oo)
    -
    -We define >, < ~ according to::
    -
    -    1. f > g .... L=+-oo
    -
    -        we say that:
    -        - f is greater than any power of g
    -        - f is more rapidly varying than g
    -        - f goes to infinity/zero faster than g
    -
    -    2. f < g .... L=0
    -
    -        we say that:
    -        - f is lower than any power of g
    -
    -    3. f ~ g .... L!=0, +-oo
    -
    -        we say that:
    -        - both f and g are bounded from above and below by suitable integral
    -          powers of the other
    -
    -Examples
    -========
    -::
    -    2 < x < exp(x) < exp(x**2) < exp(exp(x))
    -    2 ~ 3 ~ -5
    -    x ~ x**2 ~ x**3 ~ 1/x ~ x**m ~ -x
    -    exp(x) ~ exp(-x) ~ exp(2x) ~ exp(x)**2 ~ exp(x+exp(-x))
    -    f ~ 1/f
    -
    -So we can divide all the functions into comparability classes (x and x^2
    -belong to one class, exp(x) and exp(-x) belong to some other class). In
    -principle, we could compare any two functions, but in our algorithm, we
    -don't compare anything below the class 2~3~-5 (for example log(x) is
    -below this), so we set 2~3~-5 as the lowest comparability class.
    -
    -Given the function f, we find the list of most rapidly varying (mrv set)
    -subexpressions of it. This list belongs to the same comparability class.
    -Let's say it is {exp(x), exp(2x)}. Using the rule f ~ 1/f we find an
    -element "w" (either from the list or a new one) from the same
    -comparability class which goes to zero at infinity. In our example we
    -set w=exp(-x) (but we could also set w=exp(-2x) or w=exp(-3x) ...). We
    -rewrite the mrv set using w, in our case {1/w, 1/w^2}, and substitute it
    -into f. Then we expand f into a series in w::
    -
    -    f = c0*w^e0 + c1*w^e1 + ... + O(w^en),       where e0<e1<...<en, c0!=0
    -
    -but for x->oo, lim f = lim c0*w^e0, because all the other terms go to zero,
    -because w goes to zero faster than the ci and ei. So::
    -
    -    for e0>0, lim f = 0
    -    for e0<0, lim f = +-oo   (the sign depends on the sign of c0)
    -    for e0=0, lim f = lim c0
    -
    -We need to recursively compute limits at several places of the algorithm, but
    -as is shown in the PhD thesis, it always finishes.
    -
    -Important functions from the implementation:
    -
    -compare(a, b, x) compares "a" and "b" by computing the limit L.
    -mrv(e, x) returns list of most rapidly varying (mrv) subexpressions of "e"
    -rewrite(e, Omega, x, wsym) rewrites "e" in terms of w
    -leadterm(f, x) returns the lowest power term in the series of f
    -mrv_leadterm(e, x) returns the lead term (c0, e0) for e
    -limitinf(e, x) computes lim e  (for x->oo)
    -limit(e, z, z0) computes any limit by converting it to the case x->oo
    -
    -All the functions are really simple and straightforward except
    -rewrite(), which is the most difficult/complex part of the algorithm.
    -When the algorithm fails, the bugs are usually in the series expansion
    -(i.e. in SymPy) or in rewrite.
    -
    -This code is almost exact rewrite of the Maple code inside the Gruntz
    -thesis.
    -
    -Debugging
    ----------
    -
    -Because the gruntz algorithm is highly recursive, it's difficult to
    -figure out what went wrong inside a debugger. Instead, turn on nice
    -debug prints by defining the environment variable SYMPY_DEBUG. For
    -example:
    -
    -[user@localhost]: SYMPY_DEBUG=True ./bin/isympy
    -
    -In [1]: limit(sin(x)/x, x, 0)
    -limitinf(_x*sin(1/_x), _x) = 1
    -+-mrv_leadterm(_x*sin(1/_x), _x) = (1, 0)
    -| +-mrv(_x*sin(1/_x), _x) = set([_x])
    -| | +-mrv(_x, _x) = set([_x])
    -| | +-mrv(sin(1/_x), _x) = set([_x])
    -| |   +-mrv(1/_x, _x) = set([_x])
    -| |     +-mrv(_x, _x) = set([_x])
    -| +-mrv_leadterm(exp(_x)*sin(exp(-_x)), _x, set([exp(_x)])) = (1, 0)
    -|   +-rewrite(exp(_x)*sin(exp(-_x)), set([exp(_x)]), _x, _w) = (1/_w*sin(_w), -_x)
    -|     +-sign(_x, _x) = 1
    -|     +-mrv_leadterm(1, _x) = (1, 0)
    -+-sign(0, _x) = 0
    -+-limitinf(1, _x) = 1
    -
    -And check manually which line is wrong. Then go to the source code and
    -debug this function to figure out the exact problem.
    -
    -"""
    -from sympy import SYMPY_DEBUG
    -from sympy.core import Basic, S, oo, Symbol, I, Dummy, Wild
    -from sympy.functions import log, exp
    -from sympy.series.order import Order
    -from sympy.simplify import powsimp
    -from sympy import cacheit
    -
    -from sympy.core.compatibility import reduce
    -from functools import reduce
    -
    -O = Order
    -
    -
    -def debug(func):
    -    """Only for debugging purposes: prints a tree
    -
    -    It will print a nice execution tree with arguments and results
    -    of all decorated functions.
    -    """
    -    if not SYMPY_DEBUG:
    -        #normal mode - do nothing
    -        return func
    -
    -    #debug mode
    -    def decorated(*args, **kwargs):
    -        #r = func(*args, **kwargs)
    -        r = maketree(func, *args, **kwargs)
    -        #print "%s = %s(%s, %s)" % (r, func.__name__, args, kwargs)
    -        return r
    -
    -    return decorated
    -
    -from sympy.utilities.timeutils import timethis
    -timeit = timethis('gruntz')
    -
    -
    -def tree(subtrees):
    -    """Only debugging purposes: prints a tree"""
    -    def indent(s, type=1):
    -        x = s.split("\n")
    -        r = "+-%s\n" % x[0]
    -        for a in x[1:]:
    -            if a == "":
    -                continue
    -            if type == 1:
    -                r += "| %s\n" % a
    -            else:
    -                r += "  %s\n" % a
    -        return r
    -    if len(subtrees) == 0:
    -        return ""
    -    f = []
    -    for a in subtrees[:-1]:
    -        f.append(indent(a))
    -    f.append(indent(subtrees[-1], 2))
    -    return ''.join(f)
    -
    -tmp = []
    -iter = 0
    -
    -
    -def maketree(f, *args, **kw):
    -    """Only debugging purposes: prints a tree"""
    -    global tmp
    -    global iter
    -    oldtmp = tmp
    -    tmp = []
    -    iter += 1
    -
    -    # If there is a bug and the algorithm enters an infinite loop, enable the
    -    # following line. It will print the names and parameters of all major functions
    -    # that are called, *before* they are called
    -    #print "%s%s %s%s" % (iter, reduce(lambda x, y: x + y,map(lambda x: '-',range(1,2+iter))), f.func_name, args)
    -
    -    r = f(*args, **kw)
    -
    -    iter -= 1
    -    s = "%s%s = %s\n" % (f.__name__, args, r)
    -    if tmp != []:
    -        s += tree(tmp)
    -    tmp = oldtmp
    -    tmp.append(s)
    -    if iter == 0:
    -        print(tmp[0])
    -        tmp = []
    -    return r
    -
    -
    -
    [docs]def compare(a, b, x): - """Returns "<" if a<b, "=" for a == b, ">" for a>b""" - # log(exp(...)) must always be simplified here for termination - la, lb = log(a), log(b) - if isinstance(a, Basic) and a.func is exp: - la = a.args[0] - if isinstance(b, Basic) and b.func is exp: - lb = b.args[0] - - c = limitinf(la/lb, x) - if c == 0: - return "<" - elif c.is_unbounded: - return ">" - else: - return "=" - -
    -
    [docs]class SubsSet(dict): - """ - Stores (expr, dummy) pairs, and how to rewrite expr-s. - - The gruntz algorithm needs to rewrite certain expressions in term of a new - variable w. We cannot use subs, because it is just too smart for us. For - example:: - - > Omega=[exp(exp(_p - exp(-_p))/(1 - 1/_p)), exp(exp(_p))] - > O2=[exp(-exp(_p) + exp(-exp(-_p))*exp(_p)/(1 - 1/_p))/_w, 1/_w] - > e = exp(exp(_p - exp(-_p))/(1 - 1/_p)) - exp(exp(_p)) - > e.subs(Omega[0],O2[0]).subs(Omega[1],O2[1]) - -1/w + exp(exp(p)*exp(-exp(-p))/(1 - 1/p)) - - is really not what we want! - - So we do it the hard way and keep track of all the things we potentially - want to substitute by dummy variables. Consider the expression:: - - exp(x - exp(-x)) + exp(x) + x. - - The mrv set is {exp(x), exp(-x), exp(x - exp(-x))}. - We introduce corresponding dummy variables d1, d2, d3 and rewrite:: - - d3 + d1 + x. - - This class first of all keeps track of the mapping expr->variable, i.e. - will at this stage be a dictionary:: - - {exp(x): d1, exp(-x): d2, exp(x - exp(-x)): d3}. - - [It turns out to be more convenient this way round.] - But sometimes expressions in the mrv set have other expressions from the - mrv set as subexpressions, and we need to keep track of that as well. In - this case, d3 is really exp(x - d2), so rewrites at this stage is:: - - {d3: exp(x-d2)}. - - The function rewrite uses all this information to correctly rewrite our - expression in terms of w. In this case w can be choosen to be exp(-x), - i.e. d2. The correct rewriting then is:: - - exp(-w)/w + 1/w + x. - """ - def __init__(self): - self.rewrites = {} - - def __repr__(self): - return super(SubsSet, self).__repr__() + ', ' + self.rewrites.__repr__() - - def __getitem__(self, key): - if not key in self: - self[key] = Dummy() - return dict.__getitem__(self, key) - - def do_subs(self, e): - for expr, var in self.items(): - e = e.subs(var, expr) - return e - -
    [docs] def meets(self, s2): - """Tell whether or not self and s2 have non-empty intersection""" - return set(self.keys()).intersection(list(s2.keys())) != set() -
    -
    [docs] def union(self, s2, exps=None): - """Compute the union of self and s2, adjusting exps""" - res = self.copy() - tr = {} - for expr, var in s2.items(): - if expr in self: - if exps: - exps = exps.subs(var, res[expr]) - tr[var] = res[expr] - else: - res[expr] = var - for var, rewr in s2.rewrites.items(): - res.rewrites[var] = rewr.subs(tr) - return res, exps -
    - def copy(self): - r = SubsSet() - r.rewrites = self.rewrites.copy() - for expr, var in self.items(): - r[expr] = var - return r - -
    -@debug -
    [docs]def mrv(e, x): - """Returns a SubsSet of most rapidly varying (mrv) subexpressions of 'e', - and e rewritten in terms of these""" - e = powsimp(e, deep=True, combine='exp') - assert isinstance(e, Basic) - if not e.has(x): - return SubsSet(), e - elif e == x: - s = SubsSet() - return s, s[x] - elif e.is_Mul or e.is_Add: - i, d = e.as_independent(x) # throw away x-independent terms - if d.func != e.func: - s, expr = mrv(d, x) - return s, e.func(i, expr) - a, b = d.as_two_terms() - s1, e1 = mrv(a, x) - s2, e2 = mrv(b, x) - return mrv_max1(s1, s2, e.func(i, e1, e2), x) - elif e.is_Pow: - b, e = e.as_base_exp() - if e.has(x): - return mrv(exp(e * log(b)), x) - else: - s, expr = mrv(b, x) - return s, expr**e - elif e.func is log: - s, expr = mrv(e.args[0], x) - return s, log(expr) - elif e.func is exp: - # We know from the theory of this algorithm that exp(log(...)) may always - # be simplified here, and doing so is vital for termination. - if e.args[0].func is log: - return mrv(e.args[0].args[0], x) - if limitinf(e.args[0], x).is_unbounded: - s1 = SubsSet() - e1 = s1[e] - s2, e2 = mrv(e.args[0], x) - su = s1.union(s2)[0] - su.rewrites[e1] = exp(e2) - return mrv_max3(s1, e1, s2, exp(e2), su, e1, x) - else: - s, expr = mrv(e.args[0], x) - return s, exp(expr) - elif e.is_Function: - l = [mrv(a, x) for a in e.args] - l2 = [s for (s, _) in l if s != SubsSet()] - if len(l2) != 1: - # e.g. something like BesselJ(x, x) - raise NotImplementedError("MRV set computation for functions in" - " several variables not implemented.") - s, ss = l2[0], SubsSet() - args = [ss.do_subs(x[1]) for x in l] - return s, e.func(*args) - elif e.is_Derivative: - raise NotImplementedError("MRV set computation for derviatives" - " not implemented yet.") - return mrv(e.args[0], x) - raise NotImplementedError( - "Don't know how to calculate the mrv of '%s'" % e) - -
    -
    [docs]def mrv_max3(f, expsf, g, expsg, union, expsboth, x): - """Computes the maximum of two sets of expressions f and g, which - are in the same comparability class, i.e. max() compares (two elements of) - f and g and returns either (f, expsf) [if f is larger], (g, expsg) - [if g is larger] or (union, expsboth) [if f, g are of the same class]. - """ - assert isinstance(f, SubsSet) - assert isinstance(g, SubsSet) - - if f == SubsSet(): - return g, expsg - elif g == SubsSet(): - return f, expsf - elif f.meets(g): - return union, expsboth - - c = compare(list(f.keys())[0], list(g.keys())[0], x) - if c == ">": - return f, expsf - elif c == "<": - return g, expsg - else: - assert c == "=" - return union, expsboth - -
    -
    [docs]def mrv_max1(f, g, exps, x): - """Computes the maximum of two sets of expressions f and g, which - are in the same comparability class, i.e. mrv_max1() compares (two elements of) - f and g and returns the set, which is in the higher comparability class - of the union of both, if they have the same order of variation. - Also returns exps, with the appropriate substitutions made. - """ - u, b = f.union(g, exps) - return mrv_max3(f, g.do_subs(exps), g, f.do_subs(exps), - u, b, x) - -
    -@debug -@cacheit -@timeit -
    [docs]def sign(e, x): - """ - Returns a sign of an expression e(x) for x->oo. - - :: - - e > 0 for x sufficiently large ... 1 - e == 0 for x sufficiently large ... 0 - e < 0 for x sufficiently large ... -1 - - The result of this function is currently undefined if e changes sign - arbitarily often for arbitrarily large x (e.g. sin(x)). - - Note that this returns zero only if e is *constantly* zero - for x sufficiently large. [If e is constant, of course, this is just - the same thing as the sign of e.] - """ - from sympy import sign as _sign - assert isinstance(e, Basic) - - if e.is_positive: - return 1 - elif e.is_negative: - return -1 - elif e.is_zero: - return 0 - - elif not e.has(x): - return _sign(e) - elif e == x: - return 1 - elif e.is_Mul: - a, b = e.as_two_terms() - sa = sign(a, x) - if not sa: - return 0 - return sa * sign(b, x) - elif e.func is exp: - return 1 - elif e.is_Pow: - s = sign(e.base, x) - if s == 1: - return 1 - if e.exp.is_Integer: - return s**e.exp - elif e.func is log: - return sign(e.args[0] - 1, x) - - # if all else fails, do it the hard way - c0, e0 = mrv_leadterm(e, x) - return sign(c0, x) - -
    -@debug -@timeit -@cacheit -
    [docs]def limitinf(e, x): - """Limit e(x) for x-> oo""" - #rewrite e in terms of tractable functions only - e = e.rewrite('tractable', deep=True) - - if not e.has(x): - return e # e is a constant - if not x.is_positive: - # We make sure that x.is_positive is True so we - # get all the correct mathematical bechavior from the expression. - # We need a fresh variable. - p = Dummy('p', positive=True, bounded=True) - e = e.subs(x, p) - x = p - c0, e0 = mrv_leadterm(e, x) - sig = sign(e0, x) - if sig == 1: - return S.Zero # e0>0: lim f = 0 - elif sig == -1: # e0<0: lim f = +-oo (the sign depends on the sign of c0) - if c0.match(I*Wild("a", exclude=[I])): - return c0*oo - s = sign(c0, x) - #the leading term shouldn't be 0: - assert s != 0 - return s*oo - elif sig == 0: - return limitinf(c0, x) # e0=0: lim f = lim c0 - -
    -def moveup2(s, x): - r = SubsSet() - for expr, var in s.items(): - r[expr.subs(x, exp(x))] = var - for var, expr in s.rewrites.items(): - r.rewrites[var] = s.rewrites[var].subs(x, exp(x)) - return r - - -def moveup(l, x): - return [e.subs(x, exp(x)) for e in l] - - -@debug -@timeit -
    [docs]def calculate_series(e, x, skip_abs=False, logx=None): - """ Calculates at least one term of the series of "e" in "x". - - This is a place that fails most often, so it is in its own function. - """ - n = 1 - while 1: - series = e.nseries(x, n=n, logx=logx) - if not series.has(O): - # The series expansion is locally exact. - return series - - series = series.removeO() - if series and ((not skip_abs) or series.has(x)): - return series - n *= 2 - -
    -@debug -@timeit -@cacheit -
    [docs]def mrv_leadterm(e, x): - """Returns (c0, e0) for e.""" - Omega = SubsSet() - if not e.has(x): - return (e, S.Zero) - if Omega == SubsSet(): - Omega, exps = mrv(e, x) - if not Omega: - # e really does not depend on x after simplification - series = calculate_series(e, x) - c0, e0 = series.leadterm(x) - assert e0 == 0 - return c0, e0 - if x in Omega: - #move the whole omega up (exponentiate each term): - Omega_up = moveup2(Omega, x) - e_up = moveup([e], x)[0] - exps_up = moveup([exps], x)[0] - # NOTE: there is no need to move this down! - e = e_up - Omega = Omega_up - exps = exps_up - # - # The positive dummy, w, is used here so log(w*2) etc. will expand; - # a unique dummy is needed in this algorithm - # - # For limits of complex functions, the algorithm would have to be - # improved, or just find limits of Re and Im components separately. - # - w = Dummy("w", real=True, positive=True, bounded=True) - f, logw = rewrite(exps, Omega, x, w) - series = calculate_series(f, w, logx=logw) - series = series.subs(log(w), logw) # this should not be necessary - return series.leadterm(w) - -
    -
    [docs]def build_expression_tree(Omega, rewrites): - r""" Helper function for rewrite. - - We need to sort Omega (mrv set) so that we replace an expression before - we replace any expression in terms of which it has to be rewritten:: - - e1 ---> e2 ---> e3 - \ - -> e4 - - Here we can do e1, e2, e3, e4 or e1, e2, e4, e3. - To do this we assemble the nodes into a tree, and sort them by height. - - This function builds the tree, rewrites then sorts the nodes. - """ - class Node: - def ht(self): - return reduce(lambda x, y: x + y, - [x.ht() for x in self.before], 1) - nodes = {} - for expr, v in Omega: - n = Node() - n.before = [] - n.var = v - n.expr = expr - nodes[v] = n - for _, v in Omega: - if v in rewrites: - n = nodes[v] - r = rewrites[v] - for _, v2 in Omega: - if r.has(v2): - n.before.append(nodes[v2]) - - return nodes - -
    -@debug -@timeit -
    [docs]def rewrite(e, Omega, x, wsym): - """e(x) ... the function - Omega ... the mrv set - wsym ... the symbol which is going to be used for w - - Returns the rewritten e in terms of w and log(w). See test_rewrite1() - for examples and correct results. - """ - from sympy import ilcm - assert isinstance(Omega, SubsSet) - assert len(Omega) != 0 - #all items in Omega must be exponentials - for t in list(Omega.keys()): - assert t.func is exp - rewrites = Omega.rewrites - Omega = list(Omega.items()) - - nodes = build_expression_tree(Omega, rewrites) - Omega.sort(key=lambda x: nodes[x[1]].ht(), reverse=True) - - g, _ = Omega[-1] - # g is going to be the "w" - the simplest one in the mrv set - sig = sign(g.args[0], x) - if sig == 1: - wsym = 1/wsym # if g goes to oo, substitute 1/w - elif sig != -1: - raise NotImplementedError('Result depends on the sign of %s' % sig) - #O2 is a list, which results by rewriting each item in Omega using "w" - O2 = [] - denominators = [] - for f, var in Omega: - c = limitinf(f.args[0]/g.args[0], x) - if c.is_Rational: - denominators.append(c.q) - arg = f.args[0] - if var in rewrites: - assert rewrites[var].func is exp - arg = rewrites[var].args[0] - O2.append((var, exp((arg - c*g.args[0]).expand())*wsym**c)) - - #Remember that Omega contains subexpressions of "e". So now we find - #them in "e" and substitute them for our rewriting, stored in O2 - - # the following powsimp is necessary to automatically combine exponentials, - # so that the .subs() below succeeds: - # TODO this should not be necessary - f = powsimp(e, deep=True, combine='exp') - for a, b in O2: - f = f.subs(a, b) - - for _, var in Omega: - assert not f.has(var) - - #finally compute the logarithm of w (logw). - logw = g.args[0] - if sig == 1: - logw = -logw # log(w)->log(1/w)=-log(w) - - # Some parts of sympy have difficulty computing series expansions with - # non-integral exponents. The following heuristic improves the situation: - exponent = reduce(ilcm, denominators, 1) - f = f.subs(wsym, wsym**exponent) - logw /= exponent - - return f, logw - -
    -
    [docs]def gruntz(e, z, z0, dir="+"): - """ - Compute the limit of e(z) at the point z0 using the Gruntz algorithm. - - z0 can be any expression, including oo and -oo. - - For dir="+" (default) it calculates the limit from the right - (z->z0+) and for dir="-" the limit from the left (z->z0-). For infinite z0 - (oo or -oo), the dir argument doesn't matter. - - This algorithm is fully described in the module docstring in the gruntz.py - file. It relies heavily on the series expansion. Most frequently, gruntz() - is only used if the faster limit() function (which uses heuristics) fails. - """ - if not isinstance(z, Symbol): - raise NotImplementedError("Second argument must be a Symbol") - - #convert all limits to the limit z->oo; sign of z is handled in limitinf - r = None - if z0 == oo: - r = limitinf(e, z) - elif z0 == -oo: - r = limitinf(e.subs(z, -z), z) - else: - if dir == "-": - e0 = e.subs(z, z0 - 1/z) - elif dir == "+": - e0 = e.subs(z, z0 + 1/z) - else: - raise NotImplementedError("dir must be '+' or '-'") - r = limitinf(e0, z) - - # This is a bit of a heuristic for nice results... we always rewrite - # tractable functions in terms of familiar intractable ones. - # It might be nicer to rewrite the exactly to what they were initially, - # but that would take some work to implement. - return r.rewrite('intractable', deep=True)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/series/limits.html b/dev-py3k/_modules/sympy/series/limits.html deleted file mode 100644 index f073e6050c9..00000000000 --- a/dev-py3k/_modules/sympy/series/limits.html +++ /dev/null @@ -1,450 +0,0 @@ - - - - - - - - - - sympy.series.limits — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.series.limits

    -from sympy.core import S, Symbol, Add, sympify, Expr, PoleError, Mul, oo, C
    -from sympy.functions import tan, cot
    -from .gruntz import gruntz
    -
    -
    -
    [docs]def limit(e, z, z0, dir="+"): - """ - Compute the limit of e(z) at the point z0. - - z0 can be any expression, including oo and -oo. - - For dir="+" (default) it calculates the limit from the right - (z->z0+) and for dir="-" the limit from the left (z->z0-). For infinite z0 - (oo or -oo), the dir argument doesn't matter. - - Examples - ======== - - >>> from sympy import limit, sin, Symbol, oo - >>> from sympy.abc import x - >>> limit(sin(x)/x, x, 0) - 1 - >>> limit(1/x, x, 0, dir="+") - oo - >>> limit(1/x, x, 0, dir="-") - -oo - >>> limit(1/x, x, oo) - 0 - - Notes - ===== - - First we try some heuristics for easy and frequent cases like "x", "1/x", - "x**2" and similar, so that it's fast. For all other cases, we use the - Gruntz algorithm (see the gruntz() function). - """ - from sympy import Wild, log - - e = sympify(e) - z = sympify(z) - z0 = sympify(z0) - - if e == z: - return z0 - - if e.is_Rational: - return e - - if not e.has(z): - return e - - if e.func is tan: - # discontinuity at odd multiples of pi/2; 0 at even - disc = S.Pi/2 - sign = 1 - if dir == '-': - sign *= -1 - i = limit(sign*e.args[0], z, z0)/disc - if i.is_integer: - if i.is_even: - return S.Zero - elif i.is_odd: - if dir == '+': - return S.NegativeInfinity - else: - return S.Infinity - - if e.func is cot: - # discontinuity at multiples of pi; 0 at odd pi/2 multiples - disc = S.Pi - sign = 1 - if dir == '-': - sign *= -1 - i = limit(sign*e.args[0], z, z0)/disc - if i.is_integer: - if dir == '-': - return S.NegativeInfinity - else: - return S.Infinity - elif (2*i).is_integer: - return S.Zero - - if e.is_Pow: - b, ex = e.args - c = None # records sign of b if b is +/-z or has a bounded value - if b.is_Mul: - c, b = b.as_two_terms() - if c is S.NegativeOne and b == z: - c = '-' - elif b == z: - c = '+' - - if ex.is_number: - if c is None: - base = b.subs(z, z0) - if base.is_finite and (ex.is_bounded or base is not S.One): - return base**ex - else: - if z0 == 0 and ex < 0: - if dir != c: - # integer - if ex.is_even: - return S.Infinity - elif ex.is_odd: - return S.NegativeInfinity - # rational - elif ex.is_Rational: - return (S.NegativeOne**ex)*S.Infinity - else: - return S.ComplexInfinity - return S.Infinity - return z0**ex - - if e.is_Mul or not z0 and e.is_Pow and b.func is log: - if e.is_Mul: - if abs(z0) is S.Infinity: - n, d = e.as_numer_denom() - # XXX todo: this should probably be stated in the - # negative -- i.e. to exclude expressions that should - # not be handled this way but I'm not sure what that - # condition is; when ok is True it means that the leading - # term approach is going to succeed (hopefully) - ok = lambda w: (z in w.free_symbols and - any(a.is_polynomial(z) or - any(z in m.free_symbols and m.is_polynomial(z) - for m in Mul.make_args(a)) - for a in Add.make_args(w))) - if all(ok(w) for w in (n, d)): - u = C.Dummy(positive=(z0 is S.Infinity)) - inve = (n/d).subs(z, 1/u) - return limit(inve.as_leading_term(u), u, - S.Zero, "+" if z0 is S.Infinity else "-") - - # weed out the z-independent terms - i, d = e.as_independent(z) - if i is not S.One and i.is_bounded: - return i*limit(d, z, z0, dir) - else: - i, d = S.One, e - if not z0: - # look for log(z)**q or z**p*log(z)**q - p, q = Wild("p"), Wild("q") - r = d.match(z**p * log(z)**q) - if r: - p, q = [r.get(w, w) for w in [p, q]] - if q and q.is_number and p.is_number: - if q > 0: - if p > 0: - return S.Zero - else: - return -oo*i - else: - if p >= 0: - return S.Zero - else: - return -oo*i - - if e.is_Add: - if e.is_polynomial() and not z0.is_unbounded: - return Add(*[limit(term, z, z0, dir) for term in e.args]) - - # this is a case like limit(x*y+x*z, z, 2) == x*y+2*x - # but we need to make sure, that the general gruntz() algorithm is - # executed for a case like "limit(sqrt(x+1)-sqrt(x),x,oo)==0" - - unbounded = [] - unbounded_result = [] - unbounded_const = [] - unknown = [] - unknown_result = [] - finite = [] - zero = [] - - def _sift(term): - if z not in term.free_symbols: - if term.is_unbounded: - unbounded_const.append(term) - else: - finite.append(term) - else: - result = term.subs(z, z0) - bounded = result.is_bounded - if bounded is False or result is S.NaN: - unbounded.append(term) - if result != S.NaN: - # take result from direction given - result = limit(term, z, z0, dir) - unbounded_result.append(result) - elif bounded: - if result: - finite.append(result) - else: - zero.append(term) - else: - unknown.append(term) - unknown_result.append(result) - - for term in e.args: - _sift(term) - - bad = bool(unknown and unbounded) - if bad or len(unknown) > 1 or len(unbounded) > 1 and not zero: - uu = unknown + unbounded - # we won't be able to resolve this with unbounded - # terms, e.g. Sum(1/k, (k, 1, n)) - log(n) as n -> oo: - # since the Sum is unevaluated it's boundedness is - # unknown and the log(n) is oo so you get Sum - oo - # which is unsatisfactory. BUT...if there are both - # unknown and unbounded terms (condition 'bad') or - # there are multiple terms that are unknown, or - # there are multiple symbolic unbounded terms they may - # respond better if they are made into a rational - # function, so give them a chance to do so before - # reporting failure. - u = Add(*uu) - f = u.normal() - if f != u: - unknown = [] - unbounded = [] - unbounded_result = [] - unknown_result = [] - _sift(limit(f, z, z0, dir)) - - # We came in with a) unknown and unbounded terms or b) had multiple - # unknown terms - - # At this point we've done one of 3 things. - # (1) We did nothing with f so we now report the error - # showing the troublesome terms which are now in uu. OR - - # (2) We did something with f but the result came back as unknown. - # Normally this wouldn't be a problem, - # but we had either multiple terms that were troublesome (unk and - # unbounded or multiple unknown terms) so if we - # weren't able to resolve the boundedness by now, that indicates a - # problem so we report the error showing the troublesome terms which are - # now in uu. - if unknown: - if bad: - msg = 'unknown and unbounded terms present in %s' - elif unknown: - msg = 'multiple terms with unknown boundedness in %s' - raise NotImplementedError(msg % uu) - # OR - # (3) the troublesome terms have been identified as finite or unbounded - # and we proceed with the non-error code since the lists have been updated. - - u = Add(*unknown_result) - if unbounded_result or unbounded_const: - unbounded.extend(zero) - inf_limit = Add(*(unbounded_result + unbounded_const)) - if inf_limit is not S.NaN: - return inf_limit + u - if finite: - return Add(*finite) + limit(Add(*unbounded), z, z0, dir) + u - else: - return Add(*finite) + u - - if e.is_Order: - args = e.args - return C.Order(limit(args[0], z, z0), *args[1:]) - - try: - r = gruntz(e, z, z0, dir) - if r is S.NaN: - raise PoleError() - except (PoleError, ValueError): - r = heuristics(e, z, z0, dir) - return r - -
    -def heuristics(e, z, z0, dir): - if abs(z0) is S.Infinity: - return limit(e.subs(z, 1/z), z, S.Zero, "+" if z0 is S.Infinity else "-") - - rv = None - bad = (S.Infinity, S.NegativeInfinity, S.NaN, None) - if e.is_Mul: - r = [] - for a in e.args: - if not a.is_bounded: - r.append(a.limit(z, z0, dir)) - if r[-1] in bad: - break - else: - if r: - rv = Mul(*r) - if rv is None and (e.is_Add or e.is_Pow or e.is_Function): - rv = e.func(*[limit(a, z, z0, dir) for a in e.args]) - if rv in bad: - msg = "Don't know how to calculate the limit(%s, %s, %s, dir=%s), sorry." - raise PoleError(msg % (e, z, z0, dir)) - return rv - - -
    [docs]class Limit(Expr): - """Represents an unevaluated limit. - - Examples - ======== - - >>> from sympy import Limit, sin, Symbol - >>> from sympy.abc import x - >>> Limit(sin(x)/x, x, 0) - Limit(sin(x)/x, x, 0) - >>> Limit(1/x, x, 0, dir="-") - Limit(1/x, x, 0, dir='-') - - """ - - def __new__(cls, e, z, z0, dir="+"): - e = sympify(e) - z = sympify(z) - z0 = sympify(z0) - if isinstance(dir, str): - dir = Symbol(dir) - elif not isinstance(dir, Symbol): - raise TypeError("direction must be of type basestring or Symbol, not %s" % type(dir)) - if str(dir) not in ('+', '-'): - raise ValueError( - "direction must be either '+' or '-', not %s" % dir) - obj = Expr.__new__(cls) - obj._args = (e, z, z0, dir) - return obj - -
    [docs] def doit(self, **hints): - """Evaluates limit""" - e, z, z0, dir = self.args - if hints.get('deep', True): - e = e.doit(**hints) - z = z.doit(**hints) - z0 = z0.doit(**hints) - return limit(e, z, z0, str(dir))
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/series/order.html b/dev-py3k/_modules/sympy/series/order.html deleted file mode 100644 index 06729a4c6d4..00000000000 --- a/dev-py3k/_modules/sympy/series/order.html +++ /dev/null @@ -1,383 +0,0 @@ - - - - - - - - - - sympy.series.order — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.series.order

    -from sympy.core import Basic, S, sympify, Expr, Rational, Symbol
    -from sympy.core import Add, Mul
    -from sympy.core.cache import cacheit
    -from sympy.core.compatibility import cmp_to_key
    -
    -
    -
    [docs]class Order(Expr): - """ Represents the limiting behavior of some function - - The order of a function characterizes the function based on the limiting - behavior of the function as it goes to some limit. Only taking the limit - point to be 0 is currently supported. This is expressed in big O notation - [1]_. - - The formal definition for the order of a function `g(x)` about a point `a` - is such that `g(x) = O(f(x))` as `x \\rightarrow a` if and only if for any - `\delta > 0` there exists a `M > 0` such that `|g(x)| \leq M|f(x)|` for - `|x-a| < \delta`. This is equivalent to `\lim_{x \\rightarrow a} - |g(x)/f(x)| < \infty`. - - Let's illustrate it on the following example by taking the expansion of - `\sin(x)` about 0: - - .. math :: - \sin(x) = x - x^3/3! + O(x^5) - - where in this case `O(x^5) = x^5/5! - x^7/7! + \cdots`. By the definition - of `O`, for any `\delta > 0` there is an `M` such that: - - .. math :: - |x^5/5! - x^7/7! + ....| <= M|x^5| \\text{ for } |x| < \delta - - or by the alternate definition: - - .. math :: - \lim_{x \\rightarrow 0} | (x^5/5! - x^7/7! + ....) / x^5| < \infty - - which surely is true, because - - .. math :: - \lim_{x \\rightarrow 0} | (x^5/5! - x^7/7! + ....) / x^5| = 1/5! - - - As it is usually used, the order of a function can be intuitively thought - of representing all terms of powers greater than the one specified. For - example, `O(x^3)` corresponds to any terms proportional to `x^3, - x^4,\ldots` and any higher power. For a polynomial, this leaves terms - proportional to `x^2`, `x` and constants. - - Examples - ======== - - >>> from sympy import O - >>> from sympy.abc import x - >>> O(x) - O(x) - >>> O(x)*x - O(x**2) - >>> O(x)-O(x) - O(x) - - References - ========== - - .. [1] `Big O notation <http://en.wikipedia.org/wiki/Big_O_notation>`_ - - Notes - ===== - - In ``O(f(x), x)`` the expression ``f(x)`` is assumed to have a leading - term. ``O(f(x), x)`` is automatically transformed to - ``O(f(x).as_leading_term(x),x)``. - - ``O(expr*f(x), x)`` is ``O(f(x), x)`` - - ``O(expr, x)`` is ``O(1)`` - - ``O(0, x)`` is 0. - - Multivariate O is also supported: - - ``O(f(x, y), x, y)`` is transformed to - ``O(f(x, y).as_leading_term(x,y).as_leading_term(y), x, y)`` - - In the multivariate case, it is assumed the limits w.r.t. the various - symbols commute. - - If no symbols are passed then all symbols in the expression are used: - - """ - - is_Order = True - - __slots__ = [] - - @cacheit - def __new__(cls, expr, *symbols, **assumptions): - - expr = sympify(expr).expand() - if expr is S.NaN: - return S.NaN - - if symbols: - symbols = list(map(sympify, symbols)) - if not all(isinstance(s, Symbol) for s in symbols): - raise NotImplementedError( - 'Order at points other than 0 not supported.') - else: - symbols = list(expr.free_symbols) - - if expr.is_Order: - - new_symbols = list(expr.variables) - for s in symbols: - if s not in new_symbols: - new_symbols.append(s) - if len(new_symbols) == len(expr.variables): - return expr - symbols = new_symbols - - elif symbols: - - if expr.is_Add: - lst = expr.extract_leading_order(*symbols) - expr = Add(*[f.expr for (e, f) in lst]) - elif expr: - if len(symbols) > 1 or expr.is_commutative is False: - # TODO - # We cannot use compute_leading_term because that only - # works in one symbol. - expr = expr.as_leading_term(*symbols) - else: - expr = expr.compute_leading_term(symbols[0]) - terms = expr.as_coeff_mul(*symbols)[1] - s = set(symbols) - expr = Mul(*[t for t in terms if s & t.free_symbols]) - - if expr is S.Zero: - return expr - elif not expr.has(*symbols): - expr = S.One - - # create Order instance: - symbols.sort(key=cmp_to_key(Basic.compare)) - obj = Expr.__new__(cls, expr, *symbols, **assumptions) - - return obj - - def _hashable_content(self): - return self.args - - def oseries(self, order): - return self - - def _eval_nseries(self, x, n, logx): - return self - - @property - def expr(self): - return self._args[0] - - @property - def variables(self): - return self._args[1:] - - @property - def free_symbols(self): - return self.expr.free_symbols - - def _eval_power(b, e): - if e.is_Number: - return Order(b.expr ** e, *b.variables) - return - - def as_expr_variables(self, order_symbols): - if order_symbols is None: - order_symbols = self.variables - else: - for s in self.variables: - if s not in order_symbols: - order_symbols = order_symbols + (s,) - return self.expr, order_symbols - - def removeO(self): - return S.Zero - - def getO(self): - return self - - @cacheit -
    [docs] def contains(self, expr): - """ - Return True if expr belongs to Order(self.expr, \*self.variables). - Return False if self belongs to expr. - Return None if the inclusion relation cannot be determined - (e.g. when self and expr have different symbols). - """ - # NOTE: when multiplying out series a lot of queries like - # O(...).contains(a*x**b) with many a and few b are made. - # Separating out the independent part allows for better caching. - c, m = expr.as_coeff_mul(*self.variables) - if m != (): - return self._contains(Mul(*m)) - else: - # Mul(*m) == 1, and O(1) treatment is somewhat peculiar ... - # some day this else should not be necessary - return self._contains(expr) -
    - @cacheit - def _contains(self, expr): - from sympy import powsimp, limit - if expr is S.Zero: - return True - if expr is S.NaN: - return False - if expr.is_Order: - if self.variables and expr.variables: - common_symbols = tuple( - [s for s in self.variables if s in expr.variables]) - elif self.variables: - common_symbols = self.variables - else: - common_symbols = expr.variables - if not common_symbols: - if not (self.variables or expr.variables): # O(1),O(1) - return True - return None - r = None - for s in common_symbols: - l = limit(powsimp(self.expr/expr.expr, deep=True, - combine='exp'), s, 0) != 0 - if r is None: - r = l - else: - if r != l: - return - return r - obj = Order(expr, *self.variables) - return self.contains(obj) - - def _eval_subs(self, old, new): - if old.is_Symbol and old in self.variables: - i = list(self.variables).index(old) - if isinstance(new, Symbol): - return Order(self.expr._subs(old, new), *(self.variables[:i] + (new,) + self.variables[i + 1:])) - return Order(self.expr._subs(old, new), *(self.variables[:i] + self.variables[i + 1:])) - return Order(self.expr._subs(old, new), *self.variables) - - def _eval_conjugate(self): - expr = self.expr._eval_conjugate() - if expr is not None: - return self.func(expr, *self.variables) - - def _eval_derivative(self, x): - return self.func(self.expr.diff(x), *self.variables) or self - - def _eval_transpose(self): - expr = self.expr._eval_transpose() - if expr is not None: - return self.func(expr, *self.variables) - - def _sage_(self): - #XXX: SAGE doesn't have Order yet. Let's return 0 instead. - return Rational(0)._sage_() -
    -O = Order -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/series/residues.html b/dev-py3k/_modules/sympy/series/residues.html deleted file mode 100644 index 189598ba97d..00000000000 --- a/dev-py3k/_modules/sympy/series/residues.html +++ /dev/null @@ -1,195 +0,0 @@ - - - - - - - - - - sympy.series.residues — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.series.residues

    -"""
    -This module implements the Residue function and related tools for working
    -with residues.
    -"""
    -
    -from sympy import Wild, sympify, Integer, Add
    -from sympy.utilities.timeutils import timethis
    -
    -
    -@timethis('residue')
    -
    [docs]def residue(expr, x, x0): - """ - Finds the residue of ``expr`` at the point x=x0. - - The residue is defined as the coefficient of 1/(x-x0) in the power series - expansion about x=x0. - - Examples - ======== - - >>> from sympy import Symbol, residue, sin - >>> x = Symbol("x") - >>> residue(1/x, x, 0) - 1 - >>> residue(1/x**2, x, 0) - 0 - >>> residue(2/sin(x), x, 0) - 2 - - This function is essential for the Residue Theorem [1]. - - References - ========== - - 1. http://en.wikipedia.org/wiki/Residue_theorem - """ - # The current implementation uses series expansion to - # calculate it. A more general implementation is explained in - # the section 5.6 of the Bronstein's book {M. Bronstein: - # Symbolic Integration I, Springer Verlag (2005)}. For purely - # rational functions, the algorithm is much easier. See - # sections 2.4, 2.5, and 2.7 (this section actually gives an - # algorithm for computing any Laurent series coefficient for - # a rational function). The theory in section 2.4 will help to - # understand why the resultant works in the general algorithm. - # For the definition of a resultant, see section 1.4 (and any - # previous sections for more review). - - from sympy import collect, Mul, Order, S - expr = sympify(expr) - if x0 != 0: - expr = expr.subs(x, x + x0) - for n in [0, 1, 2, 4, 8, 16, 32]: - if n == 0: - s = expr.series(x, n=0) - else: - s = expr.nseries(x, n=n) - if s.has(Order) and s.removeO() == 0: - # bug in nseries - continue - if not s.has(Order) or s.getn() >= 0: - break - if s.has(Order) and s.getn() < 0: - raise NotImplementedError('Bug in nseries?') - s = collect(s.removeO(), x) - if s.is_Add: - args = s.args - else: - args = [s] - res = S(0) - for arg in args: - c, m = arg.as_coeff_mul(x) - m = Mul(*m) - if not (m == 1 or m == x or (m.is_Pow and m.exp.is_Integer)): - raise NotImplementedError('term of unexpected form: %s' % m) - if m == 1/x: - res += c - return res
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/series/series.html b/dev-py3k/_modules/sympy/series/series.html deleted file mode 100644 index bd7cf66fb6f..00000000000 --- a/dev-py3k/_modules/sympy/series/series.html +++ /dev/null @@ -1,127 +0,0 @@ - - - - - - - - - - sympy.series.series — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.series.series

    -from sympy.core.sympify import sympify
    -
    -
    -
    [docs]def series(expr, x=None, x0=0, n=6, dir="+"): - """Series expansion of expr around point `x = x0`. - - See the doctring of Expr.series() for complete details of this wrapper. - """ - expr = sympify(expr) - return expr.series(x, x0, n, dir)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/sets/fancysets.html b/dev-py3k/_modules/sympy/sets/fancysets.html deleted file mode 100644 index b611a94b869..00000000000 --- a/dev-py3k/_modules/sympy/sets/fancysets.html +++ /dev/null @@ -1,405 +0,0 @@ - - - - - - - - - - sympy.sets.fancysets — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.sets.fancysets

    -from sympy import (Dummy, S, symbols, Lambda, pi, Basic, sympify, ask, Q, Min,
    -        Max)
    -from sympy.functions.elementary.integers import floor, ceiling
    -from sympy.functions.elementary.complexes import sign
    -from sympy.core.compatibility import iterable, as_int
    -from sympy.core.sets import Set, Interval, FiniteSet, Intersection
    -from sympy.core.singleton import Singleton, S
    -from sympy.solvers import solve
    -
    -oo = S.Infinity
    -
    -
    -
    [docs]class Naturals(Set, metaclass=Singleton): - """ - Represents the Natural Numbers. The Naturals are available as a singleton - as S.Naturals - - Examples - ======== - - >>> from sympy import S, Interval, pprint - >>> 5 in S.Naturals - True - >>> iterable = iter(S.Naturals) - >>> print(next(iterable)) - 1 - >>> print(next(iterable)) - 2 - >>> print(next(iterable)) - 3 - >>> pprint(S.Naturals.intersect(Interval(0, 10))) - {1, 2, ..., 10} - """ - is_iterable = True - - def _intersect(self, other): - if other.is_Interval: - return Intersection(S.Integers, other, Interval(1, oo)) - return None - - def _contains(self, other): - if ask(Q.positive(other)) and ask(Q.integer(other)): - return True - return False - - def __iter__(self): - i = S(1) - while True: - yield i - i = i + 1 - - @property - def _inf(self): - return S.One - - @property - def _sup(self): - return oo - -
    -
    [docs]class Integers(Set, metaclass=Singleton): - """ - Represents the Integers. The Integers are available as a singleton - as S.Integers - - Examples - ======== - - >>> from sympy import S, Interval, pprint - >>> 5 in S.Naturals - True - >>> iterable = iter(S.Integers) - >>> print(next(iterable)) - 0 - >>> print(next(iterable)) - 1 - >>> print(next(iterable)) - -1 - >>> print(next(iterable)) - 2 - - >>> pprint(S.Integers.intersect(Interval(-4, 4))) - {-4, -3, ..., 4} - """ - is_iterable = True - - def _intersect(self, other): - if other.is_Interval and other.measure < oo: - s = Range(ceiling(other.left), floor(other.right) + 1) - return s.intersect(other) # take out endpoints if open interval - return None - - def _contains(self, other): - if ask(Q.integer(other)): - return True - return False - - def __iter__(self): - yield S.Zero - i = S(1) - while True: - yield i - yield -i - i = i + 1 - - @property - def _inf(self): - return -oo - - @property - def _sup(self): - return oo - -
    -class Reals(Interval, metaclass=Singleton): - def __new__(cls): - return Interval.__new__(cls, -oo, oo) - - -
    [docs]class TransformationSet(Set): - """ - A set that is a transformation of another through some algebraic expression - - Examples - -------- - >>> from sympy import Symbol, S, TransformationSet, FiniteSet, Lambda - - >>> x = Symbol('x') - >>> N = S.Naturals - >>> squares = TransformationSet(Lambda(x, x**2), N) # {x**2 for x in N} - >>> 4 in squares - True - >>> 5 in squares - False - - >>> FiniteSet(0, 1, 2, 3, 4, 5, 6, 7, 9, 10).intersect(squares) - {1, 4, 9} - - >>> square_iterable = iter(squares) - >>> for i in range(4): - ... next(square_iterable) - 1 - 4 - 9 - 16 - """ - def __new__(cls, lamda, base_set): - return Basic.__new__(cls, lamda, base_set) - - lamda = property(lambda self: self.args[0]) - base_set = property(lambda self: self.args[1]) - - def __iter__(self): - already_seen = set() - for i in self.base_set: - val = self.lamda(i) - if val in already_seen: - continue - else: - already_seen.add(val) - yield val - - def _is_multivariate(self): - return len(self.lamda.variables) > 1 - - def _contains(self, other): - L = self.lamda - if self._is_multivariate(): - solns = solve([expr - val for val, expr in zip(other, L.expr)], - L.variables) - else: - solns = solve(L.expr - other, L.variables[0]) - - for soln in solns: - try: - if soln in self.base_set: - return True - except TypeError: - if soln.evalf() in self.base_set: - return True - return False - - @property - def is_iterable(self): - return self.base_set.is_iterable - -
    -class Range(Set): - """ - Represents a range of integers. - - Examples - ======== - - >>> from sympy import Range - >>> list(Range(5)) # 0 to 5 - [0, 1, 2, 3, 4] - >>> list(Range(10, 15)) # 10 to 15 - [10, 11, 12, 13, 14] - >>> list(Range(10, 20, 2)) # 10 to 20 in steps of 2 - [10, 12, 14, 16, 18] - >>> list(Range(20, 10, -2)) # 20 to 10 backward in steps of 2 - [12, 14, 16, 18, 20] - - """ - - is_iterable = True - - def __new__(cls, *args): - # expand range - slc = slice(*args) - start, stop, step = slc.start or 0, slc.stop, slc.step or 1 - try: - start, stop, step = [S(as_int(w)) for w in (start, stop, step)] - except ValueError: - raise ValueError("Inputs to Range must be Integer Valued\n" + - "Use TransformationSets of Ranges for other cases") - n = ceiling((stop - start)/step) - if n <= 0: - return S.EmptySet - - # normalize args: regardless of how they are entered they will show - # canonically as Range(inf, sup, step) with step > 0 - start, stop = sorted((start, start + (n - 1)*step)) - step = abs(step) - - return Basic.__new__(cls, start, stop + step, step) - - start = property(lambda self: self.args[0]) - stop = property(lambda self: self.args[1]) - step = property(lambda self: self.args[2]) - - def _intersect(self, other): - if other.is_Interval: - osup = other.sup - oinf = other.inf - # if other is [0, 10) we can only go up to 9 - if osup.is_integer and other.right_open: - osup -= 1 - if oinf.is_integer and other.left_open: - oinf += 1 - - # Take the most restrictive of the bounds set by the two sets - # round inwards - inf = ceiling(Max(self.inf, oinf)) - sup = floor(Min(self.sup, osup)) - # if we are off the sequence, get back on - off = (inf - self.inf) % self.step - if off: - inf += self.step - off - - return Range(inf, sup + 1, self.step) - - if other == S.Naturals: - return self._intersect(Interval(1, oo)) - - if other == S.Integers: - return self - - return None - - def _contains(self, other): - return (other >= self.inf and other <= self.sup and - ask(Q.integer((self.start - other)/self.step))) - - def __iter__(self): - i = self.start - while(i < self.stop): - yield i - i = i + self.step - - def __len__(self): - return ((self.stop - self.start)//self.step) - - def _ith_element(self, i): - return self.start + i*self.step - - @property - def _last_element(self): - return self._ith_element(len(self) - 1) - - @property - def _inf(self): - return self.start - - @property - def _sup(self): - return self.stop - self.step -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/simplify/cse_main.html b/dev-py3k/_modules/sympy/simplify/cse_main.html deleted file mode 100644 index 9be9b4d6088..00000000000 --- a/dev-py3k/_modules/sympy/simplify/cse_main.html +++ /dev/null @@ -1,525 +0,0 @@ - - - - - - - - - - sympy.simplify.cse_main — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.simplify.cse_main

    -""" Tools for doing common subexpression elimination.
    -"""
    -import difflib
    -
    -from sympy.core import Basic, Mul, Add, sympify
    -from sympy.core.basic import preorder_traversal
    -from sympy.core.function import _coeff_isneg
    -from sympy.core.compatibility import iterable
    -from sympy.utilities.iterables import numbered_symbols, \
    -    sift, topological_sort, ordered
    -
    -from . import cse_opts
    -
    -# (preprocessor, postprocessor) pairs which are commonly useful. They should
    -# each take a sympy expression and return a possibly transformed expression.
    -# When used in the function ``cse()``, the target expressions will be transformed
    -# by each of the preprocessor functions in order. After the common
    -# subexpressions are eliminated, each resulting expression will have the
    -# postprocessor functions transform them in *reverse* order in order to undo the
    -# transformation if necessary. This allows the algorithm to operate on
    -# a representation of the expressions that allows for more optimization
    -# opportunities.
    -# ``None`` can be used to specify no transformation for either the preprocessor or
    -# postprocessor.
    -
    -cse_optimizations = list(cse_opts.default_optimizations)
    -
    -# sometimes we want the output in a different format; non-trivial
    -# transformations can be put here for users
    -# ===============================================================
    -
    -
    -def reps_toposort(r):
    -    """Sort replacements `r` so (k1, v1) appears before (k2, v2)
    -    if k2 is in v1's free symbols. This orders items in the
    -    way that cse returns its results (hence, in order to use the
    -    replacements in a substitution option it would make sense
    -    to reverse the order).
    -
    -    Examples
    -    ========
    -    >>> from sympy.simplify.cse_main import reps_toposort
    -    >>> from sympy.abc import x, y
    -    >>> from sympy import Eq
    -    >>> for l, r in reps_toposort([(x, y + 1), (y, 2)]):
    -    ...     print(Eq(l, r))
    -    ...
    -    y == 2
    -    x == y + 1
    -
    -    """
    -    r = sympify(r)
    -    E = []
    -    for c1, (k1, v1) in enumerate(r):
    -        for c2, (k2, v2) in enumerate(r):
    -            if k1 in v2.free_symbols:
    -                E.append((c1, c2))
    -    return [r[i] for i in topological_sort((list(range(len(r))), E))]
    -
    -
    -def cse_separate(r, e):
    -    """Move expressions that are in the form (symbol, expr) out of the
    -    expressions and sort them into the replacements using the reps_toposort.
    -
    -    Examples
    -    ========
    -    >>> from sympy.simplify.cse_main import cse_separate
    -    >>> from sympy.abc import x, y, z
    -    >>> from sympy import cos, exp, cse, Eq, symbols
    -    >>> x0, x1 = symbols('x:2')
    -    >>> eq = (x + 1 + exp((x + 1)/(y + 1)) + cos(y + 1))
    -    >>> cse([eq, Eq(x, z + 1), z - 2], postprocess=cse_separate) in [
    -    ... [[(x0, y + 1), (x, z + 1), (x1, x + 1)],
    -    ...  [x1 + exp(x1/x0) + cos(x0), z - 2]],
    -    ... [[(x1, y + 1), (x, z + 1), (x0, x + 1)],
    -    ...  [x0 + exp(x0/x1) + cos(x1), z - 2]]]
    -    ...
    -    True
    -    """
    -    d = sift(e, lambda w: w.is_Equality and w.lhs.is_Symbol)
    -    r = r + [w.args for w in d[True]]
    -    e = d[False]
    -    return [reps_toposort(r), e]
    -
    -# ====end of cse postprocess idioms===========================
    -
    -
    -def preprocess_for_cse(expr, optimizations):
    -    """ Preprocess an expression to optimize for common subexpression
    -    elimination.
    -
    -    Parameters
    -    ----------
    -    expr : sympy expression
    -        The target expression to optimize.
    -    optimizations : list of (callable, callable) pairs
    -        The (preprocessor, postprocessor) pairs.
    -
    -    Returns
    -    -------
    -    expr : sympy expression
    -        The transformed expression.
    -    """
    -    for pre, post in optimizations:
    -        if pre is not None:
    -            expr = pre(expr)
    -    return expr
    -
    -
    -def postprocess_for_cse(expr, optimizations):
    -    """ Postprocess an expression after common subexpression elimination to
    -    return the expression to canonical sympy form.
    -
    -    Parameters
    -    ----------
    -    expr : sympy expression
    -        The target expression to transform.
    -    optimizations : list of (callable, callable) pairs, optional
    -        The (preprocessor, postprocessor) pairs.  The postprocessors will be
    -        applied in reversed order to undo the effects of the preprocessors
    -        correctly.
    -
    -    Returns
    -    -------
    -    expr : sympy expression
    -        The transformed expression.
    -    """
    -    if optimizations is None:
    -        optimizations = cse_optimizations
    -    for pre, post in reversed(optimizations):
    -        if post is not None:
    -            expr = post(expr)
    -    return expr
    -
    -
    -def _remove_singletons(reps, exprs):
    -    """
    -    Helper function for cse that will remove expressions that weren't
    -    used more than once.
    -    """
    -    u_reps = []  # the useful reps that are used more than once
    -    for i, ui in enumerate(reps):
    -        used = []  # where it was used
    -        ri, ei = ui
    -
    -        # keep track of whether the substitution was used more
    -        # than once. If used is None, it was never used (yet);
    -        # if used is an int, that is the last place where it was
    -        # used (>=0 in the reps, <0 in the expressions) and if
    -        # it is True, it was used more than once.
    -
    -        used = None
    -
    -        tot = 0  # total times used so far
    -
    -        # search through the reps
    -        for j in range(i + 1, len(reps)):
    -            c = reps[j][1].count(ri)
    -            if c:
    -                tot += c
    -                if tot > 1:
    -                    u_reps.append(ui)
    -                    used = True
    -                    break
    -                else:
    -                    used = j
    -
    -        if used is not True:
    -
    -            # then search through the expressions
    -
    -            for j, rj in enumerate(exprs):
    -                c = rj.count(ri)
    -                if c:
    -                    # append a negative so we know that it was in the
    -                    # expression that used it
    -                    tot += c
    -                    if tot > 1:
    -                        u_reps.append(ui)
    -                        used = True
    -                        break
    -                    else:
    -                        used = j - len(exprs)
    -
    -            if type(used) is int:
    -
    -                # undo the change
    -
    -                rep = {ri: ei}
    -                j = used
    -                if j < 0:
    -                    exprs[j] = exprs[j].subs(rep)
    -                else:
    -                    reps[j] = reps[j][0], reps[j][1].subs(rep)
    -
    -    # reuse unused symbols so a contiguous range of symbols is returned
    -
    -    if len(u_reps) != len(reps):
    -        for i, ri in enumerate(u_reps):
    -            if u_reps[i][0] != reps[i][0]:
    -                rep = (u_reps[i][0], reps[i][0])
    -                u_reps[i] = rep[1], u_reps[i][1].subs(*rep)
    -                for j in range(i + 1, len(u_reps)):
    -                    u_reps[j] = u_reps[j][0], u_reps[j][1].subs(*rep)
    -                for j, rj in enumerate(exprs):
    -                    exprs[j] = exprs[j].subs(*rep)
    -
    -    reps[:] = u_reps  # change happens in-place
    -
    -
    -
    [docs]def cse(exprs, symbols=None, optimizations=None, postprocess=None): - """ Perform common subexpression elimination on an expression. - - Parameters - ========== - - exprs : list of sympy expressions, or a single sympy expression - The expressions to reduce. - symbols : infinite iterator yielding unique Symbols - The symbols used to label the common subexpressions which are pulled - out. The ``numbered_symbols`` generator is useful. The default is a - stream of symbols of the form "x0", "x1", etc. This must be an infinite - iterator. - optimizations : list of (callable, callable) pairs, optional - The (preprocessor, postprocessor) pairs. If not provided, - ``sympy.simplify.cse.cse_optimizations`` is used. - postprocess : a function which accepts the two return values of cse and - returns the desired form of output from cse, e.g. if you want the - replacements reversed the function might be the following lambda: - lambda r, e: return reversed(r), e - - Returns - ======= - - replacements : list of (Symbol, expression) pairs - All of the common subexpressions that were replaced. Subexpressions - earlier in this list might show up in subexpressions later in this list. - reduced_exprs : list of sympy expressions - The reduced expressions with all of the replacements above. - """ - from sympy.matrices import Matrix - - if symbols is None: - symbols = numbered_symbols() - else: - # In case we get passed an iterable with an __iter__ method instead of - # an actual iterator. - symbols = iter(symbols) - seen_subexp = set() - muls = set() - adds = set() - to_eliminate = set() - - if optimizations is None: - # Pull out the default here just in case there are some weird - # manipulations of the module-level list in some other thread. - optimizations = list(cse_optimizations) - - # Handle the case if just one expression was passed. - if isinstance(exprs, Basic): - exprs = [exprs] - - # Preprocess the expressions to give us better optimization opportunities. - reduced_exprs = [preprocess_for_cse(e, optimizations) for e in exprs] - - # Find all of the repeated subexpressions. - for expr in reduced_exprs: - if not isinstance(expr, Basic): - continue - pt = preorder_traversal(expr) - for subtree in pt: - - inv = 1/subtree if subtree.is_Pow else None - - if subtree.is_Atom or iterable(subtree) or inv and inv.is_Atom: - # Exclude atoms, since there is no point in renaming them. - continue - - if subtree in seen_subexp: - if inv and _coeff_isneg(subtree.exp): - # save the form with positive exponent - subtree = inv - to_eliminate.add(subtree) - pt.skip() - continue - - if inv and inv in seen_subexp: - if _coeff_isneg(subtree.exp): - # save the form with positive exponent - subtree = inv - to_eliminate.add(subtree) - pt.skip() - continue - elif subtree.is_Mul: - muls.add(subtree) - elif subtree.is_Add: - adds.add(subtree) - - seen_subexp.add(subtree) - - # process adds - any adds that weren't repeated might contain - # subpatterns that are repeated, e.g. x+y+z and x+y have x+y in common - adds = [set(a.args) for a in ordered(adds)] - for i in range(len(adds)): - for j in range(i + 1, len(adds)): - com = adds[i].intersection(adds[j]) - if len(com) > 1: - to_eliminate.add(Add(*com)) - - # remove this set of symbols so it doesn't appear again - adds[i] = adds[i].difference(com) - adds[j] = adds[j].difference(com) - for k in range(j + 1, len(adds)): - if not com.difference(adds[k]): - adds[k] = adds[k].difference(com) - - # process muls - any muls that weren't repeated might contain - # subpatterns that are repeated, e.g. x*y*z and x*y have x*y in common - - # use SequenceMatcher on the nc part to find the longest common expression - # in common between the two nc parts - sm = difflib.SequenceMatcher() - - muls = [a.args_cnc(cset=True) for a in ordered(muls)] - for i in range(len(muls)): - if muls[i][1]: - sm.set_seq1(muls[i][1]) - for j in range(i + 1, len(muls)): - # the commutative part in common - ccom = muls[i][0].intersection(muls[j][0]) - - # the non-commutative part in common - if muls[i][1] and muls[j][1]: - # see if there is any chance of an nc match - ncom = set(muls[i][1]).intersection(set(muls[j][1])) - if len(ccom) + len(ncom) < 2: - continue - - # now work harder to find the match - sm.set_seq2(muls[j][1]) - i1, _, n = sm.find_longest_match(0, len(muls[i][1]), - 0, len(muls[j][1])) - ncom = muls[i][1][i1:i1 + n] - else: - ncom = [] - - com = list(ccom) + ncom - if len(com) < 2: - continue - - to_eliminate.add(Mul(*com)) - - # remove ccom from all if there was no ncom; to update the nc part - # would require finding the subexpr and then replacing it with a - # dummy to keep bounding nc symbols from being identified as a - # subexpr, e.g. removing B*C from A*B*C*D might allow A*D to be - # identified as a subexpr which would not be right. - if not ncom: - muls[i][0] = muls[i][0].difference(ccom) - for k in range(j, len(muls)): - if not ccom.difference(muls[k][0]): - muls[k][0] = muls[k][0].difference(ccom) - - # make to_eliminate canonical; we will prefer non-Muls to Muls - # so select them first (non-Muls will have False for is_Mul and will - # be first in the ordering. - to_eliminate = list(ordered(to_eliminate, lambda _: _.is_Mul)) - - # Substitute symbols for all of the repeated subexpressions. - replacements = [] - reduced_exprs = list(reduced_exprs) - hit = True - for i, subtree in enumerate(to_eliminate): - if hit: - sym = next(symbols) - hit = False - if subtree.is_Pow and subtree.exp.is_Rational: - update = lambda x: x.xreplace({subtree: sym, 1/subtree: 1/sym}) - else: - update = lambda x: x.subs(subtree, sym) - # Make the substitution in all of the target expressions. - for j, expr in enumerate(reduced_exprs): - old = reduced_exprs[j] - reduced_exprs[j] = update(expr) - hit = hit or (old != reduced_exprs[j]) - # Make the substitution in all of the subsequent substitutions. - for j in range(i + 1, len(to_eliminate)): - old = to_eliminate[j] - to_eliminate[j] = update(to_eliminate[j]) - hit = hit or (old != to_eliminate[j]) - if hit: - replacements.append((sym, subtree)) - - # Postprocess the expressions to return the expressions to canonical form. - for i, (sym, subtree) in enumerate(replacements): - subtree = postprocess_for_cse(subtree, optimizations) - replacements[i] = (sym, subtree) - reduced_exprs = [postprocess_for_cse(e, optimizations) - for e in reduced_exprs] - - # remove replacements that weren't used more than once - _remove_singletons(replacements, reduced_exprs) - - if isinstance(exprs, Matrix): - reduced_exprs = [Matrix(exprs.rows, exprs.cols, reduced_exprs)] - if postprocess is None: - return replacements, reduced_exprs - return postprocess(replacements, reduced_exprs)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/simplify/epathtools.html b/dev-py3k/_modules/sympy/simplify/epathtools.html deleted file mode 100644 index fda326fb79a..00000000000 --- a/dev-py3k/_modules/sympy/simplify/epathtools.html +++ /dev/null @@ -1,470 +0,0 @@ - - - - - - - - - - sympy.simplify.epathtools — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.simplify.epathtools

    -"""Tools for manipulation of expressions using paths. """
    -
    -from sympy.core import Basic
    -
    -
    -
    [docs]class EPath(object): - r""" - Manipulate expressions using paths. - - EPath grammar in EBNF notation:: - - literal ::= /[A-Za-z_][A-Za-z_0-9]*/ - number ::= /-?\d+/ - type ::= literal - attribute ::= literal "?" - all ::= "*" - slice ::= "[" number? (":" number? (":" number?)?)? "]" - range ::= all | slice - query ::= (type | attribute) ("|" (type | attribute))* - selector ::= range | query range? - path ::= "/" selector ("/" selector)* - - See the docstring of the epath() function. - - """ - - __slots__ = ["_path", "_epath"] - - def __new__(cls, path): - """Construct new EPath. """ - if isinstance(path, EPath): - return path - - if not path: - raise ValueError("empty EPath") - - _path = path - - if path[0] == '/': - path = path[1:] - else: - raise NotImplementedError("non-root EPath") - - epath = [] - - for selector in path.split('/'): - selector = selector.strip() - - if not selector: - raise ValueError("empty selector") - - index = 0 - - for c in selector: - if c.isalnum() or c == '_' or c == '|' or c == '?': - index += 1 - else: - break - - attrs = [] - types = [] - - if index: - elements = selector[:index] - selector = selector[index:] - - for element in elements.split('|'): - element = element.strip() - - if not element: - raise ValueError("empty element") - - if element.endswith('?'): - attrs.append(element[:-1]) - else: - types.append(element) - - span = None - - if selector == '*': - pass - else: - if selector.startswith('['): - try: - i = selector.index(']') - except ValueError: - raise ValueError("expected ']', got EOL") - - _span, span = selector[1:i], [] - - if ':' not in _span: - span = int(_span) - else: - for elt in _span.split(':', 3): - if not elt: - span.append(None) - else: - span.append(int(elt)) - - span = slice(*span) - - selector = selector[i + 1:] - - if selector: - raise ValueError("trailing characters in selector") - - epath.append((attrs, types, span)) - - obj = object.__new__(cls) - - obj._path = _path - obj._epath = epath - - return obj - - def __repr__(self): - return "%s(%r)" % (self.__class__.__name__, self._path) - - def _get_ordered_args(self, expr): - """Sort ``expr.args`` using printing order. """ - if expr.is_Add: - return expr.as_ordered_terms() - elif expr.is_Mul: - return expr.as_ordered_factors() - else: - return expr.args - - def _hasattrs(self, expr, attrs): - """Check if ``expr`` has any of ``attrs``. """ - for attr in attrs: - if not hasattr(expr, attr): - return False - - return True - - def _hastypes(self, expr, types): - """Check if ``expr`` is any of ``types``. """ - _types = [ cls.__name__ for cls in expr.__class__.mro() ] - return bool(set(_types).intersection(types)) - - def _has(self, expr, attrs, types): - """Apply ``_hasattrs`` and ``_hastypes`` to ``expr``. """ - if not (attrs or types): - return True - - if attrs and self._hasattrs(expr, attrs): - return True - - if types and self._hastypes(expr, types): - return True - - return False - -
    [docs] def apply(self, expr, func, args=None, kwargs=None): - """ - Modify parts of an expression selected by a path. - - Examples - ======== - - >>> from sympy.simplify.epathtools import EPath - >>> from sympy import sin, cos, E - >>> from sympy.abc import x, y, z, t - - >>> path = EPath("/*/[0]/Symbol") - >>> expr = [((x, 1), 2), ((3, y), z)] - - >>> path.apply(expr, lambda expr: expr**2) - [((x**2, 1), 2), ((3, y**2), z)] - - >>> path = EPath("/*/*/Symbol") - >>> expr = t + sin(x + 1) + cos(x + y + E) - - >>> path.apply(expr, lambda expr: 2*expr) - t + sin(2*x + 1) + cos(2*x + 2*y + E) - - """ - def _apply(path, expr, func): - if not path: - return func(expr) - else: - selector, path = path[0], path[1:] - attrs, types, span = selector - - if isinstance(expr, Basic): - if not expr.is_Atom: - args, basic = self._get_ordered_args(expr), True - else: - return expr - elif hasattr(expr, '__iter__'): - args, basic = expr, False - else: - return expr - - args = list(args) - - if span is not None: - if type(span) == slice: - indices = list(range(*span.indices(len(args)))) - else: - indices = [span] - else: - indices = range(len(args)) - - for i in indices: - try: - arg = args[i] - except IndexError: - continue - - if self._has(arg, attrs, types): - args[i] = _apply(path, arg, func) - - if basic: - return expr.func(*args) - else: - return expr.__class__(args) - - _args, _kwargs = args or (), kwargs or {} - _func = lambda expr: func(expr, *_args, **_kwargs) - - return _apply(self._epath, expr, _func) -
    -
    [docs] def select(self, expr): - """ - Retrieve parts of an expression selected by a path. - - Examples - ======== - - >>> from sympy.simplify.epathtools import EPath - >>> from sympy import sin, cos, E - >>> from sympy.abc import x, y, z, t - - >>> path = EPath("/*/[0]/Symbol") - >>> expr = [((x, 1), 2), ((3, y), z)] - - >>> path.select(expr) - [x, y] - - >>> path = EPath("/*/*/Symbol") - >>> expr = t + sin(x + 1) + cos(x + y + E) - - >>> path.select(expr) - [x, x, y] - - """ - result = [] - - def _select(path, expr): - if not path: - result.append(expr) - else: - selector, path = path[0], path[1:] - attrs, types, span = selector - - if isinstance(expr, Basic): - args = self._get_ordered_args(expr) - elif hasattr(expr, '__iter__'): - args = expr - else: - return - - if span is not None: - if type(span) == slice: - args = args[span] - else: - try: - args = [args[span]] - except IndexError: - return - - for arg in args: - if self._has(arg, attrs, types): - _select(path, arg) - - _select(self._epath, expr) - return result - -
    -
    [docs]def epath(path, expr=None, func=None, args=None, kwargs=None): - r""" - Manipulate parts of an expression selected by a path. - - This function allows to manipulate large nested expressions in single - line of code, utilizing techniques to those applied in XML processing - standards (e.g. XPath). - - If ``func`` is ``None``, :func:`epath` retrieves elements selected by - the ``path``. Otherwise it applies ``func`` to each matching element. - - Note that it is more efficient to create an EPath object and use the select - and apply methods of that object, since this will compile the path string - only once. This function should only be used as a convenient shortcut for - interactive use. - - This is the supported syntax: - - * select all: ``/*`` - Equivalent of ``for arg in args:``. - * select slice: ``/[0]`` or ``/[1:5]`` or ``/[1:5:2]`` - Supports standard Python's slice syntax. - * select by type: ``/list`` or ``/list|tuple`` - Emulates :func:`isinstance`. - * select by attribute: ``/__iter__?`` - Emulates :func:`hasattr`. - - Parameters - ========== - - path : str | EPath - A path as a string or a compiled EPath. - expr : Basic | iterable - An expression or a container of expressions. - func : callable (optional) - A callable that will be applied to matching parts. - args : tuple (optional) - Additional positional arguments to ``func``. - kwargs : dict (optional) - Additional keyword arguments to ``func``. - - Examples - ======== - - >>> from sympy.simplify.epathtools import epath - >>> from sympy import sin, cos, E - >>> from sympy.abc import x, y, z, t - - >>> path = "/*/[0]/Symbol" - >>> expr = [((x, 1), 2), ((3, y), z)] - - >>> epath(path, expr) - [x, y] - >>> epath(path, expr, lambda expr: expr**2) - [((x**2, 1), 2), ((3, y**2), z)] - - >>> path = "/*/*/Symbol" - >>> expr = t + sin(x + 1) + cos(x + y + E) - - >>> epath(path, expr) - [x, x, y] - >>> epath(path, expr, lambda expr: 2*expr) - t + sin(2*x + 1) + cos(2*x + 2*y + E) - - """ - _epath = EPath(path) - - if expr is None: - return _epath - if func is None: - return _epath.select(expr) - else: - return _epath.apply(expr, func, args, kwargs)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/simplify/hyperexpand.html b/dev-py3k/_modules/sympy/simplify/hyperexpand.html deleted file mode 100644 index f2c3bbb6ae4..00000000000 --- a/dev-py3k/_modules/sympy/simplify/hyperexpand.html +++ /dev/null @@ -1,2663 +0,0 @@ - - - - - - - - - - sympy.simplify.hyperexpand — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.simplify.hyperexpand

    -"""
    -Expand Hypergeometric (and Meijer G) functions into named
    -special functions.
    -
    -The algorithm for doing this uses a collection of lookup tables of
    -hypergeometric functions, and various of their properties, to expand
    -many hypergeometric functions in terms of special functions.
    -
    -It is based on the following paper:
    -      Kelly B. Roach.  Meijer G Function Representations.
    -      In: Proceedings of the 1997 International Symposium on Symbolic and
    -      Algebraic Computation, pages 205-211, New York, 1997. ACM.
    -
    -It is described in great(er) detail in the Sphinx documentation.
    -"""
    -# SUMMARY OF EXTENSIONS FOR MEIJER G FUNCTIONS
    -#
    -# o z**rho G(ap, bq; z) = G(ap + rho, bq + rho; z)
    -#
    -# o denote z*d/dz by D
    -#
    -# o It is helpful to keep in mind that ap and bq play essentially symmetric
    -#   roles: G(1/z) has slightly altered parameters, with ap and bq interchanged.
    -#
    -# o There are four shift operators:
    -#   A_J = b_J - D,     J = 1, ..., n
    -#   B_J = 1 - a_j + D, J = 1, ..., m
    -#   C_J = -b_J + D,    J = m+1, ..., q
    -#   D_J = a_J - 1 - D, J = n+1, ..., p
    -#
    -#   A_J, C_J increment b_J
    -#   B_J, D_J decrement a_J
    -#
    -# o The corresponding four inverse-shift operators are defined if there
    -#   is no cancellation. Thus e.g. an index a_J (upper or lower) can be
    -#   incremented if a_J != b_i for i = 1, ..., q.
    -#
    -# o Order reduction: if b_j - a_i is a non-negative integer, where
    -#   j <= m and i > n, the corresponding quotient of gamma functions reduces
    -#   to a polynomial. Hence the G function can be expressed using a G-function
    -#   of lower order.
    -#   Similarly if j > m and i <= n.
    -#
    -#   Secondly, there are paired index theorems [Adamchik, The evaluation of
    -#   integrals of Bessel functions via G-function identities]. Suppose there
    -#   are three parameters a, b, c, where a is an a_i, i <= n, b is a b_j,
    -#   j <= m and c is a denominator parameter (i.e. a_i, i > n or b_j, j > m).
    -#   Suppose further all three differ by integers.
    -#   Then the order can be reduced.
    -#   TODO work this out in detail.
    -#
    -# o An index quadruple is called suitable if its order cannot be reduced.
    -#   If there exists a sequence of shift operators transforming one index
    -#   quadruple into another, we say one is reachable from the other.
    -#
    -# o Deciding if one index quadruple is reachable from another is tricky. For
    -#   this reason, we use hand-built routines to match and instantiate formulas.
    -#
    -from sympy.core import S, Dummy, symbols, sympify, Tuple, expand, I, Mul
    -from sympy.core.mod import Mod
    -from sympy.functions.special.hyper import hyper
    -from sympy.core.compatibility import default_sort_key
    -from sympy import SYMPY_DEBUG
    -
    -from sympy.utilities.timeutils import timethis
    -_timeit = timethis('meijerg')
    -
    -# leave add formulae at the top for easy reference
    -
    -
    -def add_formulae(formulae):
    -    """ Create our knowledge base. """
    -
    -    a, b, c, z = symbols('a b c, z', cls=Dummy)
    -
    -    def add(ap, bq, res):
    -        formulae.append(Formula(ap, bq, z, res, (a, b, c)))
    -
    -    def addb(ap, bq, B, C, M):
    -        formulae.append(Formula(ap, bq, z, None, (a, b, c), B, C, M))
    -
    -    # Luke, Y. L. (1969), The Special Functions and Their Approximations,
    -    # Volume 1, section 6.2
    -
    -    from sympy import (
    -        exp, sqrt, root, cosh, log, asin, atan, I, lowergamma, cos,
    -        atanh, besseli, gamma, erf, pi, sin, besselj, Ei,
    -        EulerGamma, Shi, sinh, cosh, Chi, diag, Matrix,
    -        fresnels, fresnelc)
    -    from sympy.functions.special.hyper import (HyperRep_atanh,
    -        HyperRep_power1, HyperRep_power2, HyperRep_log1, HyperRep_asin1,
    -        HyperRep_asin2, HyperRep_sqrts1, HyperRep_sqrts2, HyperRep_log2,
    -        HyperRep_cosasin, HyperRep_sinasin)
    -    from sympy import polar_lift, exp_polar
    -
    -    # 0F0
    -    add((), (), exp(z))
    -
    -    # 1F0
    -    add((-a, ), (), HyperRep_power1(a, z))
    -
    -    # 2F1
    -    addb((a, a - S.Half), (2*a, ),
    -         Matrix([HyperRep_power2(a, z),
    -                 HyperRep_power2(a + S(1)/2, z)/2]),
    -         Matrix([[1, 0]]),
    -         Matrix([[(a - S.Half)*z/(1 - z), (S.Half - a)*z/(1 - z)],
    -                 [a/(1 - z), a*(z - 2)/(1 - z)]]))
    -    addb((1, 1), (2, ),
    -         Matrix([HyperRep_log1(z), 1]), Matrix([[-1/z, 0]]),
    -         Matrix([[0, z/(z - 1)], [0, 0]]))
    -    addb((S.Half, 1), (S('3/2'), ),
    -         Matrix([HyperRep_atanh(z), 1]),
    -         Matrix([[1, 0]]),
    -         Matrix([[-S(1)/2, 1/(1 - z)/2], [0, 0]]))
    -    addb((S.Half, S.Half), (S('3/2'), ),
    -         Matrix([HyperRep_asin1(z), HyperRep_power1(-S(1)/2, z)]),
    -         Matrix([[1, 0]]),
    -         Matrix([[-S(1)/2, S(1)/2], [0, z/(1 - z)/2]]))
    -    addb((-a, S.Half - a), (S.Half, ),
    -         Matrix([HyperRep_sqrts1(a, z), -HyperRep_sqrts2(a - S(1)/2, z)]),
    -         Matrix([[1, 0]]),
    -         Matrix([[0, a],
    -                 [z*(2*a - 1)/2/(1 - z), S.Half - z*(2*a - 1)/(1 - z)]]))
    -
    -    # A. P. Prudnikov, Yu. A. Brychkov and O. I. Marichev (1990).
    -    # Integrals and Series: More Special Functions, Vol. 3,.
    -    # Gordon and Breach Science Publisher
    -    addb([a, -a], [S.Half],
    -         Matrix([HyperRep_cosasin(a, z), HyperRep_sinasin(a, z)]),
    -         Matrix([[1, 0]]),
    -         Matrix([[0, -a], [a*z/(1 - z), 1/(1 - z)/2]]))
    -    addb([1, 1], [3*S.Half],
    -         Matrix([HyperRep_asin2(z), 1]), Matrix([[1, 0]]),
    -         Matrix([[(z - S.Half)/(1 - z), 1/(1 - z)/2], [0, 0]]))
    -
    -    # 3F2
    -    addb([-S.Half, 1, 1], [S.Half, 2],
    -         Matrix([z*HyperRep_atanh(z), HyperRep_log1(z), 1]),
    -         Matrix([[-S(2)/3, -S(1)/(3*z), S(2)/3]]),
    -         Matrix([[S(1)/2, 0, z/(1 - z)/2],
    -                 [0, 0, z/(z - 1)],
    -                 [0, 0, 0]]))
    -    # actually the formula for 3/2 is much nicer ...
    -    addb([-S.Half, 1, 1], [2, 2],
    -         Matrix([HyperRep_power1(S(1)/2, z), HyperRep_log2(z), 1]),
    -         Matrix([[S(4)/9 - 16/(9*z), 4/(3*z), 16/(9*z)]]),
    -         Matrix([[z/2/(z - 1), 0, 0], [1/(2*(z - 1)), 0, S.Half], [0, 0, 0]]))
    -
    -    # 1F1
    -    addb([1], [b], Matrix([z**(1 - b) * exp(z) * lowergamma(b - 1, z), 1]),
    -         Matrix([[b - 1, 0]]), Matrix([[1 - b + z, 1], [0, 0]]))
    -    addb([a], [2*a],
    -         Matrix([z**(S.Half - a)*exp(z/2)*besseli(a - S.Half, z/2)
    -                 * gamma(a + S.Half)/4**(S.Half - a),
    -                 z**(S.Half - a)*exp(z/2)*besseli(a + S.Half, z/2)
    -                 * gamma(a + S.Half)/4**(S.Half - a)]),
    -         Matrix([[1, 0]]),
    -         Matrix([[z/2, z/2], [z/2, (z/2 - 2*a)]]))
    -    mz = polar_lift(-1)*z
    -    addb([a], [a + 1],
    -         Matrix([mz**(-a)*a*lowergamma(a, mz), a*exp(z)]),
    -         Matrix([[1, 0]]),
    -         Matrix([[-a, 1], [0, z]]))
    -    # This one is redundant.
    -    add([-S.Half], [S.Half], exp(z) - sqrt(pi*z)*(-I)*erf(I*sqrt(z)))
    -
    -    # Added to get nice results for Laplace transform of Fresnel functions
    -    # http://functions.wolfram.com/07.22.03.6437.01
    -    # Basic rule
    -    #add([1], [S(3)/4, S(5)/4],
    -    #    sqrt(pi) * (cos(2*sqrt(polar_lift(-1)*z))*fresnelc(2*root(polar_lift(-1)*z,4)/sqrt(pi)) +
    -    #                sin(2*sqrt(polar_lift(-1)*z))*fresnels(2*root(polar_lift(-1)*z,4)/sqrt(pi)))
    -    #    / (2*root(polar_lift(-1)*z,4)))
    -    # Manually tuned rule
    -    addb([1], [S(3)/4, S(5)/4],
    -         Matrix([ sqrt(pi)*(I*sinh(2*sqrt(z))*fresnels(2*root(z, 4)*exp(I*pi/4)/sqrt(pi))
    -                            + cosh(2*sqrt(z))*fresnelc(2*root(z, 4)*exp(I*pi/4)/sqrt(pi)))
    -                  * exp(-I*pi/4)/(2*root(z, 4)),
    -                  sqrt(pi)*root(z, 4)*(sinh(2*sqrt(z))*fresnelc(2*root(z, 4)*exp(I*pi/4)/sqrt(pi))
    -                                      + I*cosh(2*sqrt(z))*fresnels(2*root(z, 4)*exp(I*pi/4)/sqrt(pi)))
    -                  *exp(-I*pi/4)/2,
    -                  1 ]),
    -         Matrix([[1, 0, 0]]),
    -         Matrix([[-S(1)/4, 1,      S(1)/4],
    -                 [ z,      S(1)/4, 0     ],
    -                 [ 0,      0,      0     ]]))
    -
    -    # 2F2
    -    addb([S.Half, a], [S(3)/2, a + 1],
    -         Matrix([a/(2*a - 1)*(-I)*sqrt(pi/z)*erf(I*sqrt(z)),
    -                 a/(2*a - 1)*(polar_lift(-1)*z)**(-a)*
    -                 lowergamma(a, polar_lift(-1)*z),
    -                 a/(2*a - 1)*exp(z)]),
    -         Matrix([[1, -1, 0]]),
    -         Matrix([[-S.Half, 0, 1], [0, -a, 1], [0, 0, z]]))
    -    # We make a "basis" of four functions instead of three, and give EulerGamma
    -    # an extra slot (it could just be a coefficient to 1). The advantage is
    -    # that this way Polys will not see multivariate polynomials (it treats
    -    # EulerGamma as an indeterminate), which is *way* faster.
    -    addb([1, 1], [2, 2],
    -         Matrix([Ei(z) - log(z), exp(z), 1, EulerGamma]),
    -         Matrix([[1/z, 0, 0, -1/z]]),
    -         Matrix([[0, 1, -1, 0], [0, z, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]))
    -
    -    # 0F1
    -    add((), (S.Half, ), cosh(2*sqrt(z)))
    -    addb([], [b],
    -         Matrix([gamma(b)*z**((1 - b)/2)*besseli(b - 1, 2*sqrt(z)),
    -                 gamma(b)*z**(1 - b/2)*besseli(b, 2*sqrt(z))]),
    -         Matrix([[1, 0]]), Matrix([[0, 1], [z, (1 - b)]]))
    -
    -    # 0F3
    -    x = 4*z**(S(1)/4)
    -
    -    def fp(a, z):
    -        return besseli(a, x) + besselj(a, x)
    -
    -    def fm(a, z):
    -        return besseli(a, x) - besselj(a, x)
    -
    -    # TODO branching
    -    addb([], [S.Half, a, a + S.Half],
    -         Matrix([fp(2*a - 1, z), fm(2*a, z)*z**(S(1)/4),
    -                 fm(2*a - 1, z)*sqrt(z), fp(2*a, z)*z**(S(3)/4)])
    -         * 2**(-2*a)*gamma(2*a)*z**((1 - 2*a)/4),
    -         Matrix([[1, 0, 0, 0]]),
    -         Matrix([[0, 1, 0, 0],
    -                 [0, S(1)/2 - a, 1, 0],
    -                 [0, 0, S(1)/2, 1],
    -                 [z, 0, 0, 1 - a]]))
    -    x = 2*(4*z)**(S(1)/4)*exp_polar(I*pi/4)
    -    addb([], [a, a + S.Half, 2*a],
    -         (2*sqrt(polar_lift(-1)*z))**(1 - 2*a)*gamma(2*a)**2 *
    -         Matrix([besselj(2*a - 1, x)*besseli(2*a - 1, x),
    -                 x*(besseli(2*a, x)*besselj(2*a - 1, x)
    -                    - besseli(2*a - 1, x)*besselj(2*a, x)),
    -                 x**2*besseli(2*a, x)*besselj(2*a, x),
    -                 x**3*(besseli(2*a, x)*besselj(2*a - 1, x)
    -                       + besseli(2*a - 1, x)*besselj(2*a, x))]),
    -         Matrix([[1, 0, 0, 0]]),
    -         Matrix([[0, S(1)/4, 0, 0],
    -                 [0, (1 - 2*a)/2, -S(1)/2, 0],
    -                 [0, 0, 1 - 2*a, S(1)/4],
    -                 [-32*z, 0, 0, 1 - a]]))
    -
    -    # 1F2
    -    addb([a], [a - S.Half, 2*a],
    -         Matrix([z**(S.Half - a)*besseli(a - S.Half, sqrt(z))**2,
    -                 z**(1 - a)*besseli(a - S.Half, sqrt(z))
    -                 *besseli(a - S(3)/2, sqrt(z)),
    -                 z**(S(3)/2 - a)*besseli(a - S(3)/2, sqrt(z))**2]),
    -         Matrix([[-gamma(a + S.Half)**2/4**(S.Half - a),
    -                 2*gamma(a - S.Half)*gamma(a + S.Half)/4**(1 - a),
    -                 0]]),
    -         Matrix([[1 - 2*a, 1, 0], [z/2, S.Half - a, S.Half], [0, z, 0]]))
    -    addb([S.Half], [b, 2 - b],
    -         pi*(1 - b)/sin(pi*b)*
    -         Matrix([besseli(1 - b, sqrt(z))*besseli(b - 1, sqrt(z)),
    -                 sqrt(z)*(besseli(-b, sqrt(z))*besseli(b - 1, sqrt(z))
    -                          + besseli(1 - b, sqrt(z))*besseli(b, sqrt(z))),
    -                 besseli(-b, sqrt(z))*besseli(b, sqrt(z))]),
    -         Matrix([[1, 0, 0]]),
    -         Matrix([[b - 1, S(1)/2, 0],
    -                 [z, 0, z],
    -                 [0, S(1)/2, -b]]))
    -    addb([S(1)/2], [S(3)/2, S(3)/2],
    -         Matrix([Shi(2*sqrt(z))/2/sqrt(z), sinh(2*sqrt(z))/2/sqrt(z),
    -                 cosh(2*sqrt(z))]),
    -         Matrix([[1, 0, 0]]),
    -         Matrix([[-S.Half, S.Half, 0], [0, -S.Half, S.Half], [0, 2*z, 0]]))
    -
    -    # FresnelS
    -    # Basic rule
    -    #add([S(3)/4], [S(3)/2,S(7)/4], 6*fresnels( exp(pi*I/4)*root(z,4)*2/sqrt(pi) ) / ( pi * (exp(pi*I/4)*root(z,4)*2/sqrt(pi))**3 ) )
    -    # Manually tuned rule
    -    addb([S(3)/4], [S(3)/2, S(7)/4],
    -         Matrix(
    -             [ fresnels(
    -                 exp(
    -                     pi*I/4)*root(
    -                         z, 4)*2/sqrt(
    -                             pi) ) / (
    -                                 pi * (exp(pi*I/4)*root(z, 4)*2/sqrt(pi))**3 ),
    -               sinh(2*sqrt(z))/sqrt(z),
    -               cosh(2*sqrt(z)) ]),
    -         Matrix([[6, 0, 0]]),
    -         Matrix([[-S(3)/4,  S(1)/16, 0],
    -                 [ 0,      -S(1)/2,  1],
    -                 [ 0,       z,       0]]))
    -
    -    # FresnelC
    -    # Basic rule
    -    #add([S(1)/4], [S(1)/2,S(5)/4], fresnelc( exp(pi*I/4)*root(z,4)*2/sqrt(pi) ) / ( exp(pi*I/4)*root(z,4)*2/sqrt(pi) ) )
    -    # Manually tuned rule
    -    addb([S(1)/4], [S(1)/2, S(5)/4],
    -         Matrix(
    -             [ sqrt(
    -                 pi)*exp(
    -                     -I*pi/4)*fresnelc(
    -                         2*root(z, 4)*exp(I*pi/4)/sqrt(pi))/(2*root(z, 4)),
    -               cosh(2*sqrt(z)),
    -               sinh(2*sqrt(z))*sqrt(z) ]),
    -         Matrix([[1, 0, 0]]),
    -         Matrix([[-S(1)/4,  S(1)/4, 0     ],
    -                 [ 0,       0,      1     ],
    -                 [ 0,       z,      S(1)/2]]))
    -
    -    # 2F3
    -    # XXX with this five-parameter formula is pretty slow with the current
    -    #     Formula.find_instantiations (creates 2!*3!*3**(2+3) ~ 3000
    -    #     instantiations ... But it's not too bad.
    -    addb([a, a + S.Half], [2*a, b, 2*a - b + 1],
    -         gamma(b)*gamma(2*a - b + 1) * (sqrt(z)/2)**(1 - 2*a) *
    -         Matrix([besseli(b - 1, sqrt(z))*besseli(2*a - b, sqrt(z)),
    -                 sqrt(z)*besseli(b, sqrt(z))*besseli(2*a - b, sqrt(z)),
    -                 sqrt(z)*besseli(b - 1, sqrt(z))*besseli(2*a - b + 1, sqrt(z)),
    -                 besseli(b, sqrt(z))*besseli(2*a - b + 1, sqrt(z))]),
    -         Matrix([[1, 0, 0, 0]]),
    -         Matrix([[0, S(1)/2, S(1)/2, 0],
    -                 [z/2, 1 - b, 0, z/2],
    -                 [z/2, 0, b - 2*a, z/2],
    -                 [0, S(1)/2, S(1)/2, -2*a]]))
    -    # (C/f above comment about eulergamma in the basis).
    -    addb([1, 1], [2, 2, S(3)/2],
    -         Matrix([Chi(2*sqrt(z)) - log(2*sqrt(z)),
    -                 cosh(2*sqrt(z)), sqrt(z)*sinh(2*sqrt(z)), 1, EulerGamma]),
    -         Matrix([[1/z, 0, 0, 0, -1/z]]),
    -         Matrix([[0, S(1)/2, 0, -S(1)/2, 0],
    -                 [0, 0, 1, 0, 0],
    -                 [0, z, S(1)/2, 0, 0],
    -                 [0, 0, 0, 0, 0],
    -                 [0, 0, 0, 0, 0]]))
    -
    -
    -def add_meijerg_formulae(formulae):
    -    from sympy import Matrix, gamma, uppergamma, exp, Si, Ci, sin, cos, sqrt, pi
    -
    -    a, b, c, z = list(map(Dummy, 'abcz'))
    -    rho = Dummy('rho')
    -
    -    def add(an, ap, bm, bq, B, C, M, matcher):
    -        formulae.append(MeijerFormula(an, ap, bm, bq, z, [a, b, c, rho],
    -                                      B, C, M, matcher))
    -
    -    def detect_uppergamma(iq):
    -        x = iq.an[0]
    -        y, z = iq.bm
    -        swapped = False
    -        if not Mod((x - y).simplify(), 1):
    -            swapped = True
    -            (y, z) = (z, y)
    -        if Mod((x - z).simplify(), 1) or x > z:
    -            return None
    -        l = [y, x]
    -        if swapped:
    -            l = [x, y]
    -        return {rho: y, a: x - y}, IndexQuadruple([x], [], l, [])
    -
    -    add([a + rho], [], [rho, a + rho], [],
    -        Matrix([gamma(1 - a)*z**rho*exp(z)*uppergamma(a, z),
    -                gamma(1 - a)*z**(a + rho)]),
    -        Matrix([[1, 0]]),
    -        Matrix([[rho + z, -1], [0, a + rho]]),
    -        detect_uppergamma)
    -
    -    def detect_3113(iq):
    -        """http://functions.wolfram.com/07.34.03.0984.01"""
    -        x = iq.an[0]
    -        u, v, w = iq.bm
    -        if Mod((u - v).simplify(), 1) == 0:
    -            if Mod((v - w).simplify(), 1) == 0:
    -                return
    -            sig = (S(1)/2, S(1)/2, S(0))
    -            x1, x2, y = u, v, w
    -        else:
    -            if Mod((x - u).simplify(), 1) == 0:
    -                sig = (S(1)/2, S(0), S(1)/2)
    -                x1, y, x2 = u, v, w
    -            else:
    -                sig = (S(0), S(1)/2, S(1)/2)
    -                y, x1, x2 = u, v, w
    -
    -        if (Mod((x - x1).simplify(), 1) != 0 or
    -            Mod((x - x2).simplify(), 1) != 0 or
    -            Mod((x - y).simplify(), 1) != S(1)/2 or
    -                x > x1 or x > x2):
    -            return
    -
    -        return {a: x}, IndexQuadruple([x], [], [x - S(1)/2 + t for t in sig], [])
    -
    -    s = sin(2*sqrt(z))
    -    c_ = cos(2*sqrt(z))
    -    S_ = Si(2*sqrt(z)) - pi/2
    -    C = Ci(2*sqrt(z))
    -    add([a], [], [a, a, a - S(1)/2], [],
    -        Matrix([sqrt(pi)*z**(a - S(1)/2)*(c_*S_ - s*C),
    -                sqrt(pi)*z**a*(s*S_ + c_*C),
    -                sqrt(pi)*z**a]),
    -        Matrix([[-2, 0, 0]]),
    -        Matrix([[a - S(1)/2, -1, 0], [z, a, S(1)/2], [0, 0, a]]),
    -        detect_3113)
    -
    -
    -def make_simp(z):
    -    """ Create a function that simplifies rational functions in ``z``. """
    -
    -    def simp(expr):
    -        """ Efficiently simplify the rational function ``expr``. """
    -        from sympy import poly
    -        numer, denom = expr.as_numer_denom()
    -        c, numer, denom = poly(numer, z).cancel(poly(denom, z))
    -        return c * numer.as_expr() / denom.as_expr()
    -
    -    return simp
    -
    -
    -def debug(*args):
    -    if SYMPY_DEBUG:
    -        for a in args:
    -            print(a, end=' ')
    -        print()
    -
    -
    -class IndexPair(object):
    -    """ Holds a pair of indices, and methods to compute their invariants. """
    -
    -    def __init__(self, ap, bq):
    -        from sympy import expand, Tuple
    -        self.ap = Tuple(*list(map(expand, ap)))
    -        self.bq = Tuple(*list(map(expand, bq)))
    -
    -    @property
    -    def sizes(self):
    -        return (len(self.ap), len(self.bq))
    -
    -    def __repr__(self):
    -        return 'IndexPair(%s, %s)' % (self.ap, self.bq)
    -
    -    def compute_buckets(self, oabuckets=None, obbuckets=None):
    -        """
    -        Partition ``ap`` and ``bq`` mod 1.
    -
    -        Partition parameters ``ap``, ``bq`` into buckets, i.e., return two
    -        dicts abuckets, bbuckets such that every key in (ab)buckets is a
    -        rational in the range [0, 1) [represented either by a Rational or a Mod
    -        object], and such that (ab)buckets[key] is a tuple of all elements of
    -        respectively ``ap`` or ``bq`` that are congruent to ``key`` modulo 1.
    -
    -        If oabuckets, obbuckets is specified, try to use the same Mod objects
    -        for parameters where possible.
    -
    -        >>> from sympy.simplify.hyperexpand import IndexPair
    -        >>> from sympy import S
    -        >>> ap = (S(1)/2, S(1)/3, S(-1)/2, -2)
    -        >>> bq = (1, 2)
    -        >>> IndexPair(ap, bq).compute_buckets()
    -        ({0: (-2,), 1/3: (1/3,), 1/2: (1/2, -1/2)}, {0: (1, 2)})
    -        """
    -        from collections import defaultdict
    -
    -        # TODO this should probably be cached somewhere
    -        abuckets = defaultdict(tuple)
    -        bbuckets = defaultdict(tuple)
    -
    -        # NOTE the new Mod object does so much canonization that we can ignore
    -        #      o(ab)buckets.
    -
    -        for params, bucket in [(self.ap, abuckets), (self.bq, bbuckets)]:
    -            for p in params:
    -                bucket[Mod(p, 1)] += (p, )
    -
    -        return dict(abuckets), dict(bbuckets)
    -
    -    def build_invariants(self):
    -        """
    -        Compute the invariant vector of (``ap``, ``bq``).
    -
    -        The invariant vector is:
    -            (gamma, ((s1, n1), ..., (sk, nk)), ((t1, m1), ..., (tr, mr)))
    -        where gamma is the number of integer a < 0,
    -              s1 < ... < sk
    -              nl is the number of parameters a_i congruent to sl mod 1
    -              t1 < ... < tr
    -              ml is the number of parameters b_i congruent to tl mod 1
    -
    -        If the index pair contains parameters, then this is not truly an
    -        invariant, since the parameters cannot be sorted uniquely mod1.
    -
    -        >>> from sympy.simplify.hyperexpand import IndexPair
    -        >>> from sympy import S
    -        >>> ap = (S(1)/2, S(1)/3, S(-1)/2, -2)
    -        >>> bq = (1, 2)
    -
    -        Here gamma = 1,
    -             k = 3, s1 = 0, s2 = 1/3, s3 = 1/2
    -                    n1 = 1, n2 = 1,   n2 = 2
    -             r = 1, t1 = 0
    -                    m1 = 2:
    -        >>> IndexPair(ap, bq).build_invariants()
    -        (1, ((0, 1), (1/3, 1), (1/2, 2)), ((0, 2),))
    -        """
    -        abuckets, bbuckets = self.compute_buckets()
    -
    -        gamma = 0
    -        if S(0) in abuckets:
    -            gamma = len([x for x in abuckets[S(0)] if x < 0])
    -
    -        def tr(bucket):
    -            bucket = list(bucket.items())
    -            if not any(isinstance(x[0], Mod) for x in bucket):
    -                bucket.sort(key=lambda x: x[0])
    -            bucket = tuple([(x[0], len(x[1])) for x in bucket])
    -            return bucket
    -
    -        return (gamma, tr(abuckets), tr(bbuckets))
    -
    -    def difficulty(self, ip):
    -        """ Estimate how many steps it takes to reach ``ip`` from self.
    -            Return -1 if impossible. """
    -        oabuckets, obbuckets = self.compute_buckets()
    -        abuckets, bbuckets = ip.compute_buckets(oabuckets, obbuckets)
    -
    -        gt0 = lambda x: (x > 0) is True
    -        if S(0) in abuckets and (not S(0) in oabuckets or
    -            len(list(filter(gt0, abuckets[S(0)]))) !=
    -                len(list(filter(gt0, oabuckets[S(0)])))):
    -            return -1
    -
    -        diff = 0
    -        for bucket, obucket in [(abuckets, oabuckets), (bbuckets, obbuckets)]:
    -            for mod in set(list(bucket.keys()) + list(obucket.keys())):
    -                if (not mod in bucket) or (not mod in obucket) \
    -                        or len(bucket[mod]) != len(obucket[mod]):
    -                    return -1
    -                l1 = list(bucket[mod])
    -                l2 = list(obucket[mod])
    -                l1.sort()
    -                l2.sort()
    -                for i, j in zip(l1, l2):
    -                    diff += abs(i - j)
    -
    -        return diff
    -
    -
    -class IndexQuadruple(object):
    -    """ Holds a quadruple of indices. """
    -
    -    def __init__(self, an, ap, bm, bq):
    -        from sympy import expand, Tuple
    -
    -        def tr(l):
    -            return Tuple(*list(map(expand, l)))
    -        self.an = tr(an)
    -        self.ap = tr(ap)
    -        self.bm = tr(bm)
    -        self.bq = tr(bq)
    -
    -    def compute_buckets(self):
    -        """
    -        Compute buckets for the fours sets of parameters.
    -
    -        We guarantee that any two equal Mod objects returned are actually the
    -        same, and that the buckets are sorted by real part (an and bq
    -        descendending, bm and ap ascending).
    -
    -        >>> from sympy.simplify.hyperexpand import IndexQuadruple
    -        >>> from sympy.abc import y
    -        >>> from sympy import S
    -        >>> a, b = [1, 3, 2, S(3)/2], [1 + y, y, 2, y + 3]
    -        >>> IndexQuadruple(a, b, [2], [y]).compute_buckets()
    -        ({0: [3, 2, 1], 1/2: [3/2]},
    -        {0: [2], Mod(y, 1): [y, y + 1, y + 3]}, {0: [2]}, {Mod(y, 1): [y]})
    -
    -        """
    -        from collections import defaultdict
    -        dicts = pan, pap, pbm, pbq = defaultdict(list), defaultdict(list), \
    -            defaultdict(list), defaultdict(list)
    -        for dic, lis in zip(dicts, (self.an, self.ap, self.bm, self.bq)):
    -            for x in lis:
    -                dic[Mod(x, 1)].append(x)
    -
    -        for dic, flip in zip(dicts, (True, False, False, True)):
    -            for m, items in dic.items():
    -                x0 = items[0]
    -                items.sort(key=lambda x: x - x0, reverse=flip)
    -                dic[m] = items
    -
    -        return tuple([dict(w) for w in dicts])
    -
    -    @property
    -    def signature(self):
    -        return (len(self.an), len(self.ap), len(self.bm), len(self.bq))
    -
    -    def __repr__(self):
    -        return 'IndexQuadruple(%s, %s, %s, %s)' % (self.an, self.ap,
    -                                                   self.bm, self.bq)
    -
    -# Dummy variable.
    -_x = Dummy('x')
    -
    -
    -class Formula(object):
    -    """
    -    This class represents hypergeometric formulae.
    -
    -    Its data members are:
    -    - z, the argument
    -    - closed_form, the closed form expression
    -    - symbols, the free symbols (parameters) in the formula
    -    - indices, the parameters
    -    - B, C, M (see _compute_basis)
    -    - lcms, a dictionary which maps symbol -> lcm of denominators
    -    - isolation, a dictonary which maps symbol -> (num, coeff) pairs
    -
    -    >>> from sympy.abc import a, b, z
    -    >>> from sympy.simplify.hyperexpand import Formula
    -    >>> f = Formula((a/2, a/3 + b, (1+a)/2), (a, b, (a+b)/7), z, None, [a, b])
    -
    -    The lcm of all denominators of coefficients of a is 2*3*7
    -    >>> f.lcms[a]
    -    42
    -
    -    for b it is just 7:
    -    >>> f.lcms[b]
    -    7
    -
    -    We can isolate a in the (1+a)/2 term, with denominator 2:
    -    >>> f.isolation[a]
    -    (2, 2, 1)
    -
    -    b is isolated in the b term, with coefficient one:
    -    >>> f.isolation[b]
    -    (4, 1, 1)
    -    """
    -
    -    def _compute_basis(self, closed_form):
    -        """
    -        Compute a set of functions B=(f1, ..., fn), a nxn matrix M
    -        and a 1xn matrix C such that:
    -           closed_form = C B
    -           z d/dz B = M B.
    -        """
    -        from sympy.matrices import Matrix, eye, zeros
    -
    -        afactors = [_x + a for a in self.indices.ap]
    -        bfactors = [_x + b - 1 for b in self.indices.bq]
    -        expr = _x*Mul(*bfactors) - self.z*Mul(*afactors)
    -        poly = Poly(expr, _x)
    -
    -        n = poly.degree() - 1
    -        b = [closed_form]
    -        for _ in range(n):
    -            b.append(self.z*b[-1].diff(self.z))
    -
    -        self.B = Matrix(b)
    -        self.C = Matrix([[1] + [0]*n])
    -
    -        m = eye(n)
    -        m = m.col_insert(0, zeros(n, 1))
    -        l = poly.all_coeffs()[1:]
    -        l.reverse()
    -        self.M = m.row_insert(n, -Matrix([l])/poly.all_coeffs()[0])
    -
    -    def __init__(self, ap, bq, z, res, symbols, B=None, C=None, M=None):
    -        ap = Tuple(*list(map(expand, ap)))
    -        bq = Tuple(*list(map(expand, bq)))
    -        z = sympify(z)
    -        res = sympify(res)
    -        symbols = [x for x in sympify(symbols) if ap.has(x) or bq.has(x)]
    -
    -        self.z = z
    -        self.symbols = symbols
    -        self.B = B
    -        self.C = C
    -        self.M = M
    -
    -        params = list(ap) + list(bq)
    -        lcms = {}
    -        isolation = {}
    -        for a in symbols:
    -            from sympy import ilcm
    -            l = 1
    -            isolating = []
    -            others = list(symbols[:])
    -            others.remove(a)
    -            i = 0
    -            for p in params:
    -                if p.has(a):
    -                    c, m = None, None
    -                    if p.is_Add:
    -                        c, m = p.as_independent(a)[1].as_coeff_mul(a)
    -                    else:
    -                        c, m = p.as_coeff_mul(a)
    -                    if m != (a, ) or not c.is_Rational:
    -                        raise NotImplementedError('?')
    -                    l = ilcm(l, c.q)
    -
    -                    if not p.has(*others):
    -                        isolating.append((i, c.q, c.p))
    -                lcms[a] = l
    -                i += 1
    -            if len(isolating) == 0:
    -                raise NotImplementedError('parameter is not isolated')
    -            isolating.sort(key=lambda x: x[1])
    -            isolating.sort(key=lambda x: -x[2])
    -            isolation[a] = isolating[-1]
    -
    -        self.lcms = lcms
    -        self.isolation = isolation
    -
    -        self.indices = IndexPair(ap, bq)
    -
    -        # TODO with symbolic parameters, it could be advantageous
    -        #      (for prettier answers) to compute a basis only *after*
    -        #      instantiation
    -        if res is not None:
    -            self._compute_basis(res)
    -
    -    @property
    -    def closed_form(self):
    -        return (self.C*self.B)[0]
    -
    -    def find_instantiations(self, ip):
    -        """
    -        Try to find instantiations of the free symbols that match
    -        ``ip.ap``, ``ip.bq``. Return the instantiated formulae as a list.
    -        Note that the returned instantiations need not actually match,
    -        or be valid!
    -        """
    -        ap = ip.ap
    -        bq = ip.bq
    -        if len(ap) != len(self.indices.ap) or len(bq) != len(self.indices.bq):
    -            raise TypeError('Cannot instantiate other number of parameters')
    -
    -        from sympy import solve
    -        from sympy.core.compatibility import permutations, product
    -        res = []
    -        our_params = list(self.indices.ap) + list(self.indices.bq)
    -        for na in permutations(ap):
    -            for nb in permutations(bq):
    -                all_params = list(na) + list(nb)
    -                repl = {}
    -                for a in self.symbols:
    -                    i, d, _ = self.isolation[a]
    -                    repl[a] = (solve(our_params[i] - all_params[i], a)[0], d)
    -                for change in product(*[(-1, 0, 1)]*len(self.symbols)):
    -                    rep = {}
    -                    for i, a in zip(change, list(repl.keys())):
    -                        rep[a] = repl[a][0] + i*repl[a][1]
    -                    res.append(Formula(self.indices.ap.subs(rep),
    -                                       self.indices.bq.subs(rep),
    -                                       self.z, None, [], self.B.subs(rep),
    -                                       self.C.subs(rep), self.M.subs(rep)))
    -                # if say a = -1/2, and there is 2*a in the formula, then
    -                # there will be a negative integer. But this origin is also
    -                # reachable from a = 1/2 ...
    -                # So throw this in as well.
    -                # The code is not as general as it could be, but good enough.
    -                if len(self.symbols) == 1:
    -                    a = self.symbols[0]
    -                    aval, d = repl[a]
    -                    if aval < 0 and d == 1:
    -                        from sympy import ceiling
    -                        aval -= ceiling(aval) - 1
    -                        res.append(Formula(self.indices.ap.subs(a, aval),
    -                                           self.indices.bq.subs(a, aval),
    -                                       self.z, None, [], self.B.subs(a, aval),
    -                                       self.C.subs(rep), self.M.subs(a, aval)))
    -        return res
    -
    -    def is_suitable(self):
    -        """
    -        Decide if ``self`` is a suitable origin.
    -
    -        >>> from sympy.simplify.hyperexpand import Formula
    -        >>> from sympy import S
    -
    -        If ai - bq in Z and bq >= ai this is fine:
    -        >>> Formula((S(1)/2,), (S(3)/2,), None, None, []).is_suitable()
    -        True
    -
    -        but ai = bq is not:
    -        >>> Formula((S(1)/2,), (S(1)/2,), None, None, []).is_suitable()
    -        False
    -
    -        and ai > bq is not either:
    -        >>> Formula((S(1)/2,), (-S(1)/2,), None, None, []).is_suitable()
    -        False
    -
    -        None of the bj can be a non-positive integer:
    -        >>> Formula((S(1)/2,), (0,), None, None, []).is_suitable()
    -        False
    -        >>> Formula((S(1)/2,), (-1, 1,), None, None, []).is_suitable()
    -        False
    -
    -        None of the ai can be zero:
    -        >>> Formula((S(1)/2, 0), (1,), None, None, []).is_suitable()
    -        False
    -
    -
    -        More complicated examples:
    -        >>> Formula((S(1)/2, 1), (2, -S(2)/3), None, None, []).is_suitable()
    -        True
    -        >>> a, b = (S(1)/2, 1), (2, -S(2)/3, S(3)/2)
    -        >>> Formula(a, b, None, None, []).is_suitable()
    -        True
    -        """
    -        from sympy import oo, zoo
    -        if len(self.symbols) > 0:
    -            return None
    -        for a in self.indices.ap:
    -            for b in self.indices.bq:
    -                if (a - b).is_integer and not a < b:
    -                    return False
    -        for a in self.indices.ap:
    -            if a == 0:
    -                return False
    -        for b in self.indices.bq:
    -            if b <= 0 and b.is_integer:
    -                return False
    -        for e in [self.B, self.M, self.C]:
    -            if e is None:
    -                continue
    -            if e.has(S.NaN) or e.has(oo) or e.has(-oo) or e.has(zoo):
    -                return False
    -        return True
    -
    -
    -class FormulaCollection(object):
    -    """ A collection of formulae to use as origins. """
    -
    -    def __init__(self):
    -        """ Doing this globally at module init time is a pain ... """
    -        self.symbolic_formulae = {}
    -        self.concrete_formulae = {}
    -        self.formulae = []
    -
    -        add_formulae(self.formulae)
    -
    -        # Now process the formulae into a helpful form.
    -        # These dicts are indexed by (p, q).
    -
    -        for f in self.formulae:
    -            sizes = f.indices.sizes
    -            if len(f.symbols) > 0:
    -                self.symbolic_formulae.setdefault(sizes, []).append(f)
    -            else:
    -                inv = f.indices.build_invariants()
    -                self.concrete_formulae.setdefault(sizes, {})[inv] = f
    -
    -    def lookup_origin(self, ip):
    -        """
    -        Given the suitable parameters ``ip.ap``, ``ip.bq``, try to find an
    -        origin in our knowledge base.
    -
    -        >>> from sympy.simplify.hyperexpand import FormulaCollection, IndexPair
    -        >>> f = FormulaCollection()
    -        >>> f.lookup_origin(IndexPair((), ())).closed_form
    -        exp(_z)
    -        >>> f.lookup_origin(IndexPair([1], ())).closed_form
    -        HyperRep_power1(-1, _z)
    -
    -        >>> from sympy import S
    -        >>> i = IndexPair([S('1/4'), S('3/4 + 4')], [S.Half])
    -        >>> f.lookup_origin(i).closed_form
    -        HyperRep_sqrts1(-17/4, _z)
    -        """
    -        inv = ip.build_invariants()
    -        sizes = ip.sizes
    -        if sizes in self.concrete_formulae and \
    -                inv in self.concrete_formulae[sizes]:
    -            return self.concrete_formulae[sizes][inv]
    -
    -        # We don't have a concrete formula. Try to instantiate.
    -        if not sizes in self.symbolic_formulae:
    -            return None  # Too bad...
    -
    -        possible = []
    -        for f in self.symbolic_formulae[sizes]:
    -            l = f.find_instantiations(ip)
    -            for f2 in l:
    -                if not f2.is_suitable():
    -                    continue
    -                diff = f2.indices.difficulty(ip)
    -                if diff != -1:
    -                    possible.append((diff, f2))
    -
    -        if not possible:
    -            # Give up.
    -            return None
    -
    -        # find the nearest origin
    -        possible.sort(key=lambda x: x[0])
    -        return possible[0][1]
    -
    -
    -class MeijerFormula(object):
    -    """
    -    This class represents a Meijer G-function formula.
    -
    -    Its data members are:
    -    - z, the argument
    -    - symbols, the free symbols (parameters) in the formula
    -    - indices, the parameters
    -    - B, C, M (c/f ordinary Formula)
    -    """
    -
    -    def __init__(self, an, ap, bm, bq, z, symbols, B, C, M, matcher):
    -        an, ap, bm, bq = [Tuple(*list(map(expand, w))) for w in [an, ap, bm, bq]]
    -        self.indices = IndexQuadruple(an, ap, bm, bq)
    -        self.z = z
    -        self.symbols = symbols
    -        self._matcher = matcher
    -        self.B = B
    -        self.C = C
    -        self.M = M
    -
    -    @property
    -    def closed_form(self):
    -        return (self.C*self.B)[0]
    -
    -    def try_instantiate(self, iq):
    -        """
    -        Try to instantiate the current formula to (almost) match iq.
    -        This uses the _matcher passed on init.
    -        """
    -        if iq.signature != self.indices.signature:
    -            return None
    -        res = self._matcher(iq)
    -        if res is not None:
    -            subs, niq = res
    -            return MeijerFormula(niq.an, niq.ap, niq.bm, niq.bq,
    -                                 self.z, [],
    -                                 self.B.subs(subs), self.C.subs(subs),
    -                                 self.M.subs(subs), None)
    -
    -
    -class MeijerFormulaCollection(object):
    -    """
    -    This class holds a collection of meijer g formulae.
    -    """
    -
    -    def __init__(self):
    -        from collections import defaultdict
    -        formulae = []
    -        add_meijerg_formulae(formulae)
    -        self.formulae = defaultdict(list)
    -        for formula in formulae:
    -            self.formulae[formula.indices.signature].append(formula)
    -        self.formulae = dict(self.formulae)
    -
    -    def lookup_origin(self, iq):
    -        """ Try to find a formula that matches iq. """
    -        if not iq.signature in self.formulae:
    -            return None
    -        for formula in self.formulae[iq.signature]:
    -            res = formula.try_instantiate(iq)
    -            if res is not None:
    -                return res
    -
    -
    -class Operator(object):
    -    """
    -    Base class for operators to be applied to our functions.
    -
    -    These operators are differential operators. They are by convention
    -    expressed in the variable D = z*d/dz (although this base class does
    -    not actually care).
    -    Note that when the operator is applied to an object, we typically do
    -    *not* blindly differentiate but instead use a different representation
    -    of the z*d/dz operator (see make_derivative_operator).
    -
    -    To subclass from this, define a __init__ method that initalises a
    -    self._poly variable. This variable stores a polynomial. By convention
    -    the generator is z*d/dz, and acts to the right of all coefficients.
    -
    -    Thus this poly
    -        x**2 + 2*z*x + 1
    -    represents the differential operator
    -        (z*d/dz)**2 + 2*z**2*d/dz.
    -
    -    This class is used only in the implementation of the hypergeometric
    -    function expansion algorithm.
    -    """
    -
    -    def apply(self, obj, op):
    -        """
    -        Apply ``self`` to the object ``obj``, where the generator is ``op``.
    -
    -        >>> from sympy.simplify.hyperexpand import Operator
    -        >>> from sympy.polys.polytools import Poly
    -        >>> from sympy.abc import x, y, z
    -        >>> op = Operator()
    -        >>> op._poly = Poly(x**2 + z*x + y, x)
    -        >>> op.apply(z**7, lambda f: f.diff(z))
    -        y*z**7 + 7*z**7 + 42*z**5
    -        """
    -        coeffs = self._poly.all_coeffs()
    -        coeffs.reverse()
    -        diffs = [obj]
    -        for c in coeffs[1:]:
    -            diffs.append(op(diffs[-1]))
    -        r = coeffs[0]*diffs[0]
    -        for c, d in zip(coeffs[1:], diffs[1:]):
    -            r += c*d
    -        return r
    -
    -
    -class MultOperator(Operator):
    -    """ Simply multiply by a "constant" """
    -
    -    def __init__(self, p):
    -        self._poly = Poly(p, _x)
    -
    -
    -class ShiftA(Operator):
    -    """ Increment an upper index. """
    -
    -    def __init__(self, ai):
    -        ai = sympify(ai)
    -        if ai == 0:
    -            raise ValueError('Cannot increment zero upper index.')
    -        self._poly = Poly(_x/ai + 1, _x)
    -
    -    def __str__(self):
    -        return '<Increment upper %s.>' % (1/self._poly.all_coeffs()[0])
    -
    -
    -class ShiftB(Operator):
    -    """ Decrement a lower index. """
    -
    -    def __init__(self, bi):
    -        bi = sympify(bi)
    -        if bi == 1:
    -            raise ValueError('Cannot decrement unit lower index.')
    -        self._poly = Poly(_x/(bi - 1) + 1, _x)
    -
    -    def __str__(self):
    -        return '<Decrement lower %s.>' % (1/self._poly.all_coeffs()[0] + 1)
    -
    -
    -class UnShiftA(Operator):
    -    """ Decrement an upper index. """
    -
    -    def __init__(self, ap, bq, i, z):
    -        """ Note: i counts from zero! """
    -        ap, bq, i = list(map(sympify, [ap, bq, i]))
    -
    -        self._ap = ap
    -        self._bq = bq
    -        self._i = i
    -
    -        ap = list(ap)
    -        bq = list(bq)
    -        ai = ap.pop(i) - 1
    -
    -        if ai == 0:
    -            raise ValueError('Cannot decrement unit upper index.')
    -
    -        m = Poly(z*ai, _x)
    -        for a in ap:
    -            m *= Poly(_x + a, _x)
    -        #print m
    -
    -        A = Dummy('A')
    -        n = D = Poly(ai*A - ai, A)
    -        for b in bq:
    -            n *= (D + b - 1)
    -        #print n
    -
    -        b0 = -n.nth(0)
    -        if b0 == 0:
    -            raise ValueError('Cannot decrement upper index: '
    -                             'cancels with lower')
    -        #print b0
    -
    -        n = Poly(Poly(n.all_coeffs()[:-1], A).as_expr().subs(A, _x/ai + 1), _x)
    -
    -        self._poly = Poly((n - m)/b0, _x)
    -
    -    def __str__(self):
    -        return '<Decrement upper index #%s of %s, %s.>' % (self._i,
    -                                                        self._ap, self._bq)
    -
    -
    -class UnShiftB(Operator):
    -    """ Increment a lower index. """
    -
    -    def __init__(self, ap, bq, i, z):
    -        """ Note: i counts from zero! """
    -        ap, bq, i = list(map(sympify, [ap, bq, i]))
    -
    -        self._ap = ap
    -        self._bq = bq
    -        self._i = i
    -
    -        ap = list(ap)
    -        bq = list(bq)
    -        bi = bq.pop(i) + 1
    -
    -        if bi == 0:
    -            raise ValueError('Cannot increment -1 lower index.')
    -
    -        m = Poly(_x*(bi - 1), _x)
    -        for b in bq:
    -            m *= Poly(_x + b - 1, _x)
    -        #print m
    -
    -        B = Dummy('B')
    -        D = Poly((bi - 1)*B - bi + 1, B)
    -        n = Poly(z, B)
    -        for a in ap:
    -            n *= (D + a)
    -        #print n
    -
    -        b0 = n.nth(0)
    -        #print b0
    -        if b0 == 0:
    -            raise ValueError('Cannot increment index: cancels with upper')
    -        #print b0
    -
    -        n = Poly(Poly(n.all_coeffs()[:-1], B).as_expr().subs(
    -            B, _x/(bi - 1) + 1), _x)
    -        #print n
    -
    -        self._poly = Poly((m - n)/b0, _x)
    -
    -    def __str__(self):
    -        return '<Increment lower index #%s of %s, %s.>' % (self._i,
    -                                                        self._ap, self._bq)
    -
    -
    -class MeijerShiftA(Operator):
    -    """ Increment an upper b index. """
    -
    -    def __init__(self, bi):
    -        bi = sympify(bi)
    -        self._poly = Poly(bi - _x, _x)
    -
    -    def __str__(self):
    -        return '<Increment upper b=%s.>' % (self._poly.all_coeffs()[1])
    -
    -
    -class MeijerShiftB(Operator):
    -    """ Decrement an upper a index. """
    -
    -    def __init__(self, bi):
    -        bi = sympify(bi)
    -        self._poly = Poly(1 - bi + _x, _x)
    -
    -    def __str__(self):
    -        return '<Decrement upper a=%s.>' % (1 - self._poly.all_coeffs()[1])
    -
    -
    -class MeijerShiftC(Operator):
    -    """ Increment a lower b index. """
    -
    -    def __init__(self, bi):
    -        bi = sympify(bi)
    -        self._poly = Poly(-bi + _x, _x)
    -
    -    def __str__(self):
    -        return '<Increment lower b=%s.>' % (-self._poly.all_coeffs()[1])
    -
    -
    -class MeijerShiftD(Operator):
    -    """ Decrement a lower a index. """
    -
    -    def __init__(self, bi):
    -        bi = sympify(bi)
    -        self._poly = Poly(bi - 1 - _x, _x)
    -
    -    def __str__(self):
    -        return '<Decrement lower a=%s.>' % (self._poly.all_coeffs()[1] + 1)
    -
    -
    -class MeijerUnShiftA(Operator):
    -    """ Decrement an upper b index. """
    -
    -    def __init__(self, an, ap, bm, bq, i, z):
    -        """ Note: i counts from zero! """
    -        an, ap, bm, bq, i = list(map(sympify, [an, ap, bm, bq, i]))
    -
    -        self._an = an
    -        self._ap = ap
    -        self._bm = bm
    -        self._bq = bq
    -        self._i = i
    -
    -        an = list(an)
    -        ap = list(ap)
    -        bm = list(bm)
    -        bq = list(bq)
    -        bi = bm.pop(i) - 1
    -
    -        m = Poly(1, _x)
    -        for b in bm:
    -            m *= Poly(b - _x, _x)
    -        for b in bq:
    -            m *= Poly(_x - b, _x)
    -        #print m
    -
    -        A = Dummy('A')
    -        D = Poly(bi - A, A)
    -        n = Poly(z, A)
    -        for a in an:
    -            n *= (D + 1 - a)
    -        for a in ap:
    -            n *= (-D + a - 1)
    -        #print n
    -
    -        b0 = n.nth(0)
    -        #print b0
    -        if b0 == 0:
    -            raise ValueError('Cannot decrement upper b index (cancels)')
    -        #print b0
    -
    -        n = Poly(Poly(n.all_coeffs()[:-1], A).as_expr().subs(A, bi - _x), _x)
    -        #print n
    -
    -        self._poly = Poly((m - n)/b0, _x)
    -
    -    def __str__(self):
    -        return '<Decrement upper b index #%s of %s, %s, %s, %s.>' % (self._i,
    -                                      self._an, self._ap, self._bm, self._bq)
    -
    -
    -class MeijerUnShiftB(Operator):
    -    """ Increment an upper a index. """
    -
    -    def __init__(self, an, ap, bm, bq, i, z):
    -        """ Note: i counts from zero! """
    -        an, ap, bm, bq, i = list(map(sympify, [an, ap, bm, bq, i]))
    -
    -        self._an = an
    -        self._ap = ap
    -        self._bm = bm
    -        self._bq = bq
    -        self._i = i
    -
    -        an = list(an)
    -        ap = list(ap)
    -        bm = list(bm)
    -        bq = list(bq)
    -        ai = an.pop(i) + 1
    -
    -        m = Poly(z, _x)
    -        for a in an:
    -            m *= Poly(1 - a + _x, _x)
    -        for a in ap:
    -            m *= Poly(a - 1 - _x, _x)
    -        #print m
    -
    -        B = Dummy('B')
    -        D = Poly(B + ai - 1, B)
    -        n = Poly(1, B)
    -        for b in bm:
    -            n *= (-D + b)
    -        for b in bq:
    -            n *= (D - b)
    -        #print n
    -
    -        b0 = n.nth(0)
    -        #print b0
    -        if b0 == 0:
    -            raise ValueError('Cannot increment upper a index (cancels)')
    -        #print b0
    -
    -        n = Poly(Poly(n.all_coeffs()[:-1], B).as_expr().subs(
    -            B, 1 - ai + _x), _x)
    -        #print n
    -
    -        self._poly = Poly((m - n)/b0, _x)
    -
    -    def __str__(self):
    -        return '<Increment upper a index #%s of %s, %s, %s, %s.>' % (self._i,
    -                                      self._an, self._ap, self._bm, self._bq)
    -
    -
    -class MeijerUnShiftC(Operator):
    -    """ Decrement a lower b index. """
    -    # XXX this is "essentially" the same as MeijerUnShiftA. This "essentially"
    -    #     can be made rigorous using the functional equation G(1/z) = G'(z),
    -    #     where G' denotes a G function of slightly altered parameters.
    -    #     However, sorting out the details seems harder than just coding it
    -    #     again.
    -
    -    def __init__(self, an, ap, bm, bq, i, z):
    -        """ Note: i counts from zero! """
    -        an, ap, bm, bq, i = list(map(sympify, [an, ap, bm, bq, i]))
    -
    -        self._an = an
    -        self._ap = ap
    -        self._bm = bm
    -        self._bq = bq
    -        self._i = i
    -
    -        an = list(an)
    -        ap = list(ap)
    -        bm = list(bm)
    -        bq = list(bq)
    -        bi = bq.pop(i) - 1
    -
    -        m = Poly(1, _x)
    -        for b in bm:
    -            m *= Poly(b - _x, _x)
    -        for b in bq:
    -            m *= Poly(_x - b, _x)
    -        #print m
    -
    -        C = Dummy('C')
    -        D = Poly(bi + C, C)
    -        n = Poly(z, C)
    -        for a in an:
    -            n *= (D + 1 - a)
    -        for a in ap:
    -            n *= (-D + a - 1)
    -        #print n
    -
    -        b0 = n.nth(0)
    -        #print b0
    -        if b0 == 0:
    -            raise ValueError('Cannot decrement lower b index (cancels)')
    -        #print b0
    -
    -        n = Poly(Poly(n.all_coeffs()[:-1], C).as_expr().subs(C, _x - bi), _x)
    -        #print n
    -
    -        self._poly = Poly((m - n)/b0, _x)
    -
    -    def __str__(self):
    -        return '<Decrement lower b index #%s of %s, %s, %s, %s.>' % (self._i,
    -                                      self._an, self._ap, self._bm, self._bq)
    -
    -
    -class MeijerUnShiftD(Operator):
    -    """ Increment a lower a index. """
    -    # XXX This is essentially the same as MeijerUnShiftA.
    -    #     See comment at MeijerUnShiftC.
    -
    -    def __init__(self, an, ap, bm, bq, i, z):
    -        """ Note: i counts from zero! """
    -        an, ap, bm, bq, i = list(map(sympify, [an, ap, bm, bq, i]))
    -
    -        self._an = an
    -        self._ap = ap
    -        self._bm = bm
    -        self._bq = bq
    -        self._i = i
    -
    -        an = list(an)
    -        ap = list(ap)
    -        bm = list(bm)
    -        bq = list(bq)
    -        ai = ap.pop(i) + 1
    -
    -        m = Poly(z, _x)
    -        for a in an:
    -            m *= Poly(1 - a + _x, _x)
    -        for a in ap:
    -            m *= Poly(a - 1 - _x, _x)
    -        #print m
    -
    -        B = Dummy('B')  # - this is the shift operator `D_I`
    -        D = Poly(ai - 1 - B, B)
    -        n = Poly(1, B)
    -        for b in bm:
    -            n *= (-D + b)
    -        for b in bq:
    -            n *= (D - b)
    -        #print n
    -
    -        b0 = n.nth(0)
    -        #print b0
    -        if b0 == 0:
    -            raise ValueError('Cannot increment lower a index (cancels)')
    -        #print b0
    -
    -        n = Poly(Poly(n.all_coeffs()[:-1], B).as_expr().subs(
    -            B, ai - 1 - _x), _x)
    -        #print n
    -
    -        self._poly = Poly((m - n)/b0, _x)
    -
    -    def __str__(self):
    -        return '<Increment lower a index #%s of %s, %s, %s, %s.>' % (self._i,
    -                                      self._an, self._ap, self._bm, self._bq)
    -
    -
    -class ReduceOrder(Operator):
    -    """ Reduce Order by cancelling an upper and a lower index. """
    -
    -    def __new__(cls, ai, bj):
    -        """ For convenience if reduction is not possible, return None. """
    -        ai = sympify(ai)
    -        bj = sympify(bj)
    -        n = ai - bj
    -        if n < 0 or not n.is_Integer:
    -            return None
    -        if bj.is_integer and bj <= 0 and bj + n - 1 >= 0:
    -            return None
    -
    -        self = Operator.__new__(cls)
    -
    -        p = S(1)
    -        for k in range(n):
    -            p *= (_x + bj + k)/(bj + k)
    -
    -        self._poly = Poly(p, _x)
    -        self._a = ai
    -        self._b = bj
    -
    -        return self
    -
    -    @classmethod
    -    def _meijer(cls, b, a, sign):
    -        """ Cancel b + sign*s and a + sign*s
    -            This is for meijer G functions. """
    -        from sympy import Add
    -        b = sympify(b)
    -        a = sympify(a)
    -        n = b - a
    -        if n < 0 or not n.is_Integer:
    -            return None
    -
    -        self = Operator.__new__(cls)
    -
    -        p = S(1)
    -        for k in range(n):
    -            p *= (sign*_x + a + k)
    -
    -        self._poly = Poly(p, _x)
    -        if sign == -1:
    -            self._a = b
    -            self._b = a
    -        else:
    -            self._b = Add(1, a - 1, evaluate=False)
    -            self._a = Add(1, b - 1, evaluate=False)
    -
    -        return self
    -
    -    @classmethod
    -    def meijer_minus(cls, b, a):
    -        return cls._meijer(b, a, -1)
    -
    -    @classmethod
    -    def meijer_plus(cls, a, b):
    -        return cls._meijer(1 - a, 1 - b, 1)
    -
    -    def __str__(self):
    -        return '<Reduce order by cancelling upper %s with lower %s.>' % \
    -            (self._a, self._b)
    -
    -
    -def _reduce_order(ap, bq, gen, key):
    -    """ Order reduction algorithm used in Hypergeometric and Meijer G """
    -    ap = list(ap)
    -    bq = list(bq)
    -
    -    ap.sort(key=key)
    -    bq.sort(key=key)
    -
    -    nap = []
    -    # we will edit bq in place
    -    operators = []
    -    for a in ap:
    -        op = None
    -        for i in range(len(bq)):
    -            op = gen(a, bq[i])
    -            if op is not None:
    -                bq.pop(i)
    -                break
    -        if op is None:
    -            nap.append(a)
    -        else:
    -            operators.append(op)
    -
    -    return nap, bq, operators
    -
    -
    -def reduce_order(ip):
    -    """
    -    Given the hypergeometric parameters ``ip.ap``, ``ip.bq``,
    -    find a sequence of operators to reduces order as much as possible.
    -
    -    Return (nip, [operators]), where applying the operators to the
    -    hypergeometric function specified by nip.ap, nip.bq yields ap, bq.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.simplify.hyperexpand import reduce_order, IndexPair
    -    >>> reduce_order(IndexPair((1, 2), (3, 4)))
    -    (IndexPair((1, 2), (3, 4)), [])
    -    >>> reduce_order(IndexPair((1,), (1,)))
    -    (IndexPair((), ()), [<Reduce order by cancelling upper 1 with lower 1.>])
    -    >>> reduce_order(IndexPair((2, 4), (3, 3)))
    -    (IndexPair((2,), (3,)), [<Reduce order by cancelling
    -    upper 4 with lower 3.>])
    -    """
    -    nap, nbq, operators = _reduce_order(ip.ap, ip.bq, ReduceOrder, lambda x: x)
    -
    -    return IndexPair(Tuple(*nap), Tuple(*nbq)), operators
    -
    -
    -def reduce_order_meijer(iq):
    -    """
    -    Given the Meijer G function parameters, ``iq.am``, ``iq.ap``, ``iq.bm``,
    -    ``iq.bq``, find a sequence of operators that reduces order as much as
    -    possible.
    -
    -    Return niq, [operators].
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.simplify.hyperexpand import (reduce_order_meijer,
    -    ...                                         IndexQuadruple)
    -    >>> reduce_order_meijer(IndexQuadruple([3, 4], [5, 6], [3, 4], [1, 2]))[0]
    -    IndexQuadruple((4, 3), (5, 6), (3, 4), (2, 1))
    -    >>> reduce_order_meijer(IndexQuadruple([3, 4], [5, 6], [3, 4], [1, 8]))[0]
    -    IndexQuadruple((3,), (5, 6), (3, 4), (1,))
    -    >>> reduce_order_meijer(IndexQuadruple([3, 4], [5, 6], [7, 5], [1, 5]))[0]
    -    IndexQuadruple((3,), (), (), (1,))
    -    >>> reduce_order_meijer(IndexQuadruple([3, 4], [5, 6], [7, 5], [5, 3]))[0]
    -    IndexQuadruple((), (), (), ())
    -    """
    -
    -    nan, nbq, ops1 = _reduce_order(iq.an, iq.bq, ReduceOrder.meijer_plus,
    -                                   lambda x: -x)
    -    nbm, nap, ops2 = _reduce_order(iq.bm, iq.ap, ReduceOrder.meijer_minus,
    -                                   lambda x: x)
    -
    -    return IndexQuadruple(*[Tuple(*w) for w in [nan, nap, nbm, nbq]]), \
    -        ops1 + ops2
    -
    -
    -def make_derivative_operator(M, z):
    -    """ Create a derivative operator, to be passed to Operator.apply. """
    -    from sympy import poly
    -
    -    def doit(C):
    -        r = z*C.diff(z) + C*M
    -        r = r.applyfunc(make_simp(z))
    -        return r
    -    return doit
    -
    -
    -def apply_operators(obj, ops, op):
    -    """
    -    Apply the list of operators ``ops`` to object ``obj``, substituting
    -    ``op`` for the generator.
    -    """
    -    res = obj
    -    for o in reversed(ops):
    -        res = o.apply(res, op)
    -    return res
    -
    -
    -def devise_plan(ip, nip, z):
    -    """
    -    Devise a plan (consisting of shift and un-shift operators) to be applied
    -    to the hypergeometric function (``nip.ap``, ``nip.bq``) to yield
    -    (``ip.ap``, ``ip.bq``).
    -    Returns a list of operators.
    -
    -    >>> from sympy.simplify.hyperexpand import devise_plan, IndexPair
    -    >>> from sympy.abc import z
    -
    -    Nothing to do:
    -
    -    >>> devise_plan(IndexPair((1, 2), ()), IndexPair((1, 2), ()), z)
    -    []
    -    >>> devise_plan(IndexPair((), (1, 2)), IndexPair((), (1, 2)), z)
    -    []
    -
    -    Very simple plans:
    -
    -    >>> devise_plan(IndexPair((2,), ()), IndexPair((1,), ()), z)
    -    [<Increment upper 1.>]
    -    >>> devise_plan(IndexPair((), (2,)), IndexPair((), (1,)), z)
    -    [<Increment lower index #0 of [], [1].>]
    -
    -    Several buckets:
    -
    -    >>> from sympy import S
    -    >>> devise_plan(IndexPair((1, S.Half), ()),
    -    ...             IndexPair((2, S('3/2')), ()), z) #doctest: +NORMALIZE_WHITESPACE
    -    [<Decrement upper index #0 of [3/2, 1], [].>,
    -    <Decrement upper index #0 of [2, 3/2], [].>]
    -
    -    A slightly more complicated plan:
    -
    -    >>> devise_plan(IndexPair((1, 3), ()), IndexPair((2, 2), ()), z)
    -    [<Increment upper 2.>, <Decrement upper index #0 of [2, 2], [].>]
    -
    -    Another more complicated plan: (note that the ap have to be shifted first!)
    -
    -    >>> devise_plan(IndexPair((1, -1), (2,)), IndexPair((3, -2), (4,)), z)
    -    [<Decrement lower 3.>, <Decrement lower 4.>,
    -    <Decrement upper index #1 of [-1, 2], [4].>,
    -    <Decrement upper index #1 of [-1, 3], [4].>, <Increment upper -2.>]
    -    """
    -    abuckets, bbuckets = ip.compute_buckets()
    -    nabuckets, nbbuckets = nip.compute_buckets(abuckets, bbuckets)
    -
    -    if len(list(abuckets.keys())) != len(list(nabuckets.keys())) or \
    -            len(list(bbuckets.keys())) != len(list(nbbuckets.keys())):
    -        raise ValueError('%s not reachable from %s' % (ip, nip))
    -
    -    ops = []
    -
    -    def do_shifts(fro, to, inc, dec):
    -        ops = []
    -        for i in range(len(fro)):
    -            if to[i] - fro[i] > 0:
    -                sh = inc
    -                ch = 1
    -            else:
    -                sh = dec
    -                ch = -1
    -
    -            while to[i] != fro[i]:
    -                ops += [sh(fro, i)]
    -                fro[i] += ch
    -
    -        return ops
    -
    -    def do_shifts_a(nal, nbk, al, aother, bother):
    -        """ Shift us from (nal, nbk) to (al, nbk). """
    -        return do_shifts(nal, al, lambda p, i: ShiftA(p[i]),
    -                         lambda p, i: UnShiftA(p + aother, nbk + bother, i, z))
    -
    -    def do_shifts_b(nal, nbk, bk, aother, bother):
    -        """ Shift us from (nal, nbk) to (nal, bk). """
    -        return do_shifts(nbk, bk,
    -                         lambda p, i: UnShiftB(nal + aother, p + bother, i, z),
    -                         lambda p, i: ShiftB(p[i]))
    -
    -    for r in sorted(list(abuckets.keys()) + list(bbuckets.keys()), key=default_sort_key):
    -        al = ()
    -        nal = ()
    -        bk = ()
    -        nbk = ()
    -        if r in abuckets:
    -            al = abuckets[r]
    -            nal = nabuckets[r]
    -        if r in bbuckets:
    -            bk = bbuckets[r]
    -            nbk = nbbuckets[r]
    -        if len(al) != len(nal) or len(bk) != len(nbk):
    -            raise ValueError('%s not reachable from %s' % (
    -                             (ip.ap, ip.bq), (nip.ap, nip.bq)))
    -
    -        al, nal, bk, nbk = [sorted(list(w), key=default_sort_key)
    -            for w in [al, nal, bk, nbk]]
    -
    -        def others(dic, key):
    -            l = []
    -            for k, value in dic.items():
    -                if k != key:
    -                    l += list(dic[k])
    -            return l
    -        aother = others(nabuckets, r)
    -        bother = others(nbbuckets, r)
    -
    -        if len(al) == 0:
    -            # there can be no complications, just shift the bs as we please
    -            ops += do_shifts_b([], nbk, bk, aother, bother)
    -        elif len(bk) == 0:
    -            # there can be no complications, just shift the as as we please
    -            ops += do_shifts_a(nal, [], al, aother, bother)
    -        else:
    -            namax = nal[-1]
    -            amax = al[-1]
    -
    -            if nbk[0] <= namax or bk[0] <= amax:
    -                raise ValueError('Non-suitable parameters.')
    -
    -            if namax > amax:
    -                # we are going to shift down - first do the as, then the bs
    -                ops += do_shifts_a(nal, nbk, al, aother, bother)
    -                ops += do_shifts_b(al, nbk, bk, aother, bother)
    -            else:
    -                # we are going to shift up - first do the bs, then the as
    -                ops += do_shifts_b(nal, nbk, bk, aother, bother)
    -                ops += do_shifts_a(nal, bk, al, aother, bother)
    -
    -        nabuckets[r] = al
    -        nbbuckets[r] = bk
    -
    -    ops.reverse()
    -    return ops
    -
    -
    -def try_shifted_sum(ip, z):
    -    """ Try to recognise a hypergeometric sum that starts from k > 0. """
    -    from sympy.functions import rf, factorial
    -    abuckets, bbuckets = ip.compute_buckets()
    -    if not S(0) in abuckets or len(abuckets[S(0)]) != 1:
    -        return None
    -    r = abuckets[S(0)][0]
    -    if r <= 0:
    -        return None
    -    if not S(0) in bbuckets:
    -        return None
    -    l = list(bbuckets[S(0)])
    -    l.sort()
    -    k = l[0]
    -    if k <= 0:
    -        return None
    -
    -    nap = list(ip.ap)
    -    nap.remove(r)
    -    nbq = list(ip.bq)
    -    nbq.remove(k)
    -    k -= 1
    -    nap = [x - k for x in nap]
    -    nbq = [x - k for x in nbq]
    -
    -    ops = []
    -    for n in range(r - 1):
    -        ops.append(ShiftA(n + 1))
    -    ops.reverse()
    -
    -    fac = factorial(k)/z**k
    -    for a in nap:
    -        fac /= rf(a, k)
    -    for b in nbq:
    -        fac *= rf(b, k)
    -
    -    ops += [MultOperator(fac)]
    -
    -    p = 0
    -    for n in range(k):
    -        m = z**n/factorial(n)
    -        for a in nap:
    -            m *= rf(a, n)
    -        for b in nbq:
    -            m /= rf(b, n)
    -        p += m
    -
    -    return IndexPair(nap, nbq), ops, -p
    -
    -
    -def try_polynomial(ip, z):
    -    """ Recognise polynomial cases. Returns None if not such a case.
    -        Requires order to be fully reduced. """
    -    from sympy import oo, factorial, rf, Expr
    -    abuckets, bbuckets = ip.compute_buckets()
    -    a0 = list(abuckets.get(S(0), []))
    -    b0 = list(bbuckets.get(S(0), []))
    -    a0.sort()
    -    b0.sort()
    -    al0 = [x for x in a0 if x <= 0]
    -    bl0 = [x for x in b0 if x <= 0]
    -
    -    if bl0:
    -        return oo
    -    if not al0:
    -        return None
    -
    -    a = al0[-1]
    -    fac = 1
    -    res = S(1)
    -    for n in Tuple(*list(range(-a))):
    -        fac *= z
    -        fac /= n + 1
    -        for a in ip.ap:
    -            fac *= a + n
    -        for b in ip.bq:
    -            fac /= b + n
    -        res += fac
    -    return res
    -
    -
    -def try_lerchphi(nip):
    -    """
    -    Try to find an expression for IndexPair ``nip`` in terms of Lerch
    -    Transcendents.
    -
    -    Return None if no such expression can be found.
    -    """
    -    # This is actually quite simple, and is described in Roach's paper,
    -    # section 18.
    -    # We don't need to implement the reduction to polylog here, this
    -    # is handled by expand_func.
    -    from sympy import (expand_func, lerchphi, apart, Dummy, rf, Poly, Matrix,
    -                       zeros, Add)
    -
    -    # First we need to figure out if the summation coefficient is a rational
    -    # function of the summation index, and construct that rational function.
    -    abuckets, bbuckets = nip.compute_buckets()
    -    # Update all the keys in bbuckets to be the same Mod objects as abuckets.
    -    akeys = list(abuckets.keys())
    -    bkeys = []
    -    for key in list(bbuckets.keys()):
    -        new = key
    -        for a in akeys:
    -            if a == key:
    -                new = a
    -                break
    -        bkeys += [(new, key)]
    -    bb = {}
    -    for new, key in bkeys:
    -        bb[new] = bbuckets[key]
    -    bbuckets = bb
    -
    -    paired = {}
    -    for key, value in list(abuckets.items()):
    -        if key != 0 and not key in bbuckets:
    -            return None
    -        bvalue = bbuckets.get(key, [])
    -        paired[key] = (list(value), list(bvalue))
    -        bbuckets.pop(key, None)
    -    if bbuckets != {}:
    -        return None
    -    if not S(0) in abuckets:
    -        return None
    -    aints, bints = paired[S(0)]
    -    # Account for the additional n! in denominator
    -    paired[S(0)] = (aints, bints + [1])
    -
    -    t = Dummy('t')
    -    numer = S(1)
    -    denom = S(1)
    -    for key, (avalue, bvalue) in list(paired.items()):
    -        if len(avalue) != len(bvalue):
    -            return None
    -        # Note that since order has been reduced fully, all the b are
    -        # bigger than all the a they differ from by an integer. In particular
    -        # if there are any negative b left, this function is not well-defined.
    -        for a, b in zip(avalue, bvalue):
    -            if a > b:
    -                k = a - b
    -                numer *= rf(b + t, k)
    -                denom *= rf(b, k)
    -            else:
    -                k = b - a
    -                numer *= rf(a, k)
    -                denom *= rf(a + t, k)
    -
    -    # Now do a partial fraction decomposition.
    -    # We assemble two structures: a list monomials of pairs (a, b) representing
    -    # a*t**b (b a non-negative integer), and a dict terms, where
    -    # terms[a] = [(b, c)] means that there is a term b/(t-a)**c.
    -    part = apart(numer/denom, t)
    -    args = Add.make_args(part)
    -    monomials = []
    -    terms = {}
    -    for arg in args:
    -        numer, denom = arg.as_numer_denom()
    -        if not denom.has(t):
    -            p = Poly(numer, t)
    -            assert p.is_monomial
    -            ((b, ), a) = p.LT()
    -            monomials += [(a/denom, b)]
    -            continue
    -        if numer.has(t):
    -            raise NotImplementedError('Need partial fraction decomposition'
    -                                      ' with linear denominators')
    -        indep, [dep] = denom.as_coeff_mul(t)
    -        n = 1
    -        if dep.is_Pow:
    -            n = dep.exp
    -            dep = dep.base
    -        if dep == t:
    -            a == 0
    -        elif dep.is_Add:
    -            a, tmp = dep.as_independent(t)
    -            b = 1
    -            if tmp != t:
    -                b, _ = tmp.as_independent(t)
    -            if dep != b*t + a:
    -                raise NotImplementedError('unrecognised form %s' % dep)
    -            a /= b
    -            indep *= b**n
    -        else:
    -            raise NotImplementedError('unrecognised form of partial fraction')
    -        terms.setdefault(a, []).append((numer/indep, n))
    -
    -    # Now that we have this information, assemble our formula. All the
    -    # monomials yield rational functions and go into one basis element.
    -    # The terms[a] are related by differentiation. If the largest exponent is
    -    # n, we need lerchphi(z, k, a) for k = 1, 2, ..., n.
    -    # deriv maps a basis to its derivative, expressed as a C(z)-linear
    -    # combination of other basis elements.
    -    deriv = {}
    -    coeffs = {}
    -    z = Dummy('z')
    -    monomials.sort(key=lambda x: x[1])
    -    mon = {0: 1/(1 - z)}
    -    if monomials:
    -        for k in range(monomials[-1][1]):
    -            mon[k + 1] = z*mon[k].diff(z)
    -    for a, n in monomials:
    -        coeffs.setdefault(S(1), []).append(a*mon[n])
    -    for a, l in list(terms.items()):
    -        for c, k in l:
    -            coeffs.setdefault(lerchphi(z, k, a), []).append(c)
    -        l.sort(key=lambda x: x[1])
    -        for k in range(2, l[-1][1] + 1):
    -            deriv[lerchphi(z, k, a)] = [(-a, lerchphi(z, k, a)),
    -                                        (1, lerchphi(z, k - 1, a))]
    -        deriv[lerchphi(z, 1, a)] = [(-a, lerchphi(z, 1, a)),
    -                                    (1/(1 - z), S(1))]
    -    trans = {}
    -    for n, b in enumerate([S(1)] + list(deriv.keys())):
    -        trans[b] = n
    -    basis = [expand_func(b) for (b, _) in sorted(list(trans.items()),
    -                                                 key=lambda x:x[1])]
    -    B = Matrix(basis)
    -    C = Matrix([[0]*len(B)])
    -    for b, c in list(coeffs.items()):
    -        C[trans[b]] = Add(*c)
    -    M = zeros(len(B))
    -    for b, l in list(deriv.items()):
    -        for c, b2 in l:
    -            M[trans[b], trans[b2]] = c
    -    return Formula(nip.ap, nip.bq, z, None, [], B, C, M)
    -
    -
    -def build_hypergeometric_formula(nip):
    -    """ Create a formula object representing the hypergeometric function
    -        Corresponding to nip. """
    -    # We know that no `ap` are negative integers, otherwise "detect poly"
    -    # would have kicked in. However, `ap` could be empty. In this case we can
    -    # use a different basis.
    -    # I'm not aware of a basis that works in all cases.
    -    from sympy import zeros, Dummy, Matrix, hyper, eye, Mul
    -    z = Dummy('z')
    -    if nip.ap:
    -        afactors = [_x + a for a in nip.ap]
    -        bfactors = [_x + b - 1 for b in nip.bq]
    -        expr = _x*Mul(*bfactors) - z*Mul(*afactors)
    -        poly = Poly(expr, _x)
    -        n = poly.degree()
    -        basis = []
    -        M = zeros(n)
    -        for k in range(n):
    -            a = nip.ap[0] + k
    -            basis += [hyper([a] + list(nip.ap[1:]), nip.bq, z)]
    -            if k < n - 1:
    -                M[k, k] = -a
    -                M[k, k + 1] = a
    -        B = Matrix(basis)
    -        C = Matrix([[1] + [0]*(n - 1)])
    -        derivs = [eye(n)]
    -        for k in range(n):
    -            derivs.append(M*derivs[k])
    -        l = poly.all_coeffs()
    -        l.reverse()
    -        res = [0]*n
    -        for k, c in enumerate(l):
    -            for r, d in enumerate(C*derivs[k]):
    -                res[r] += c*d
    -        for k, c in enumerate(res):
    -            M[n - 1, k] = -c/derivs[n - 1][0, n - 1]/poly.all_coeffs()[0]
    -        return Formula(nip.ap, nip.bq, z, None, [], B, C, M)
    -    else:
    -        # Since there are no `ap`, none of the `bq` can be non-positive
    -        # integers.
    -        basis = []
    -        bq = list(nip.bq[:])
    -        for i in range(len(bq)):
    -            basis += [hyper([], bq, z)]
    -            bq[i] += 1
    -        basis += [hyper([], bq, z)]
    -        B = Matrix(basis)
    -        n = len(B)
    -        C = Matrix([[1] + [0]*(n - 1)])
    -        M = zeros(n)
    -        M[0, n - 1] = z/Mul(*nip.bq)
    -        for k in range(1, n):
    -            M[k, k - 1] = nip.bq[k - 1]
    -            M[k, k] = -nip.bq[k - 1]
    -        return Formula(nip.ap, nip.bq, z, None, [], B, C, M)
    -
    -
    -def hyperexpand_special(ap, bq, z):
    -    """
    -    Try to find a closed-form expression for hyper(ap, bq, z), where ``z``
    -    is supposed to be a "special" value, e.g. 1.
    -
    -    This function tries various of the classical summation formulae
    -    (Gauss, Saalschuetz, etc).
    -    """
    -    # This code is very ad-hoc. There are many clever algorithms
    -    # (notably Zeilberger's) related to this problem.
    -    # For now we just want a few simple cases to work.
    -    from sympy import gamma, simplify, cos, unpolarify, pi
    -    p, q = len(ap), len(bq)
    -    z_ = z
    -    z = unpolarify(z)
    -    if z == 0:
    -        return S.Zero
    -    if p == 2 and q == 1:
    -        # 2F1
    -        a, b, c = ap + bq
    -        if z == 1:
    -            # Gauss
    -            return gamma(c - a - b)*gamma(c)/gamma(c - a)/gamma(c - b)
    -        if z == -1 and simplify(b - a + c) == 1:
    -            b, a = a, b
    -        if z == -1 and simplify(a - b + c) == 1:
    -            # Kummer
    -            if b.is_integer and b < 0:
    -                return 2*cos(pi*b/2)*gamma(-b)*gamma(b - a + 1) \
    -                    /gamma(-b/2)/gamma(b/2 - a + 1)
    -            else:
    -                return gamma(b/2 + 1)*gamma(b - a + 1) \
    -                    /gamma(b + 1)/gamma(b/2 - a + 1)
    -    # TODO tons of more formulae
    -    #      investigate what algorithms exist
    -    return hyper(ap, bq, z_)
    -
    -_collection = None
    -
    -
    -@_timeit
    -def _hyperexpand(ip, z, ops0=[], z0=Dummy('z0'), premult=1, prem=0,
    -                 rewrite='default'):
    -    """
    -    Try to find an expression for the hypergeometric function
    -    ``ip.ap``, ``ip.bq``.
    -
    -    The result is expressed in terms of a dummy variable z0. Then it
    -    is multiplied by premult. Then ops0 is applied.
    -    premult must be a*z**prem for some a independent of z.
    -    """
    -    from sympy.simplify import powdenest, simplify, polarify, unpolarify
    -    z = polarify(z, subs=False)
    -    if rewrite == 'default':
    -        rewrite = 'nonrepsmall'
    -
    -    def carryout_plan(f, ops):
    -        C = apply_operators(f.C.subs(f.z, z0), ops,
    -                            make_derivative_operator(f.M.subs(f.z, z0), z0))
    -        from sympy import eye
    -        C = apply_operators(C, ops0,
    -                            make_derivative_operator(f.M.subs(f.z, z0)
    -                                         + prem*eye(f.M.shape[0]), z0))
    -
    -        if premult == 1:
    -            C = C.applyfunc(make_simp(z0))
    -        r = C*f.B.subs(f.z, z0)*premult
    -        res = r[0].subs(z0, z)
    -        if rewrite:
    -            res = res.rewrite(rewrite)
    -        return res
    -
    -    # TODO
    -    # The following would be possible:
    -    # *) PFD Duplication (see Kelly Roach's paper)
    -    # *) In a similar spirit, try_lerchphi() can be generalised considerably.
    -
    -    global _collection
    -    if _collection is None:
    -        _collection = FormulaCollection()
    -
    -    debug('Trying to expand hypergeometric function corresponding to', ip)
    -
    -    # First reduce order as much as possible.
    -    nip, ops = reduce_order(ip)
    -    if ops:
    -        debug('  Reduced order to', nip)
    -    else:
    -        debug('  Could not reduce order.')
    -
    -    # Now try polynomial cases
    -    res = try_polynomial(nip, z0)
    -    if res is not None:
    -        debug('  Recognised polynomial.')
    -        p = apply_operators(res, ops, lambda f: z0*f.diff(z0))
    -        p = apply_operators(p*premult, ops0, lambda f: z0*f.diff(z0))
    -        return unpolarify(simplify(p).subs(z0, z))
    -
    -    # Try to recognise a shifted sum.
    -    p = S(0)
    -    res = try_shifted_sum(nip, z0)
    -    if res is not None:
    -        nip, nops, p = res
    -        debug('  Recognised shifted sum, reducerd order to', nip)
    -        ops += nops
    -
    -    # apply the plan for poly
    -    p = apply_operators(p, ops, lambda f: z0*f.diff(z0))
    -    p = apply_operators(p*premult, ops0, lambda f: z0*f.diff(z0))
    -    p = simplify(p).subs(z0, z)
    -
    -    # Try special expansions early.
    -    if unpolarify(z) in [1, -1] and (len(nip.ap), len(nip.bq)) == (2, 1):
    -        f = build_hypergeometric_formula(nip)
    -        r = carryout_plan(f, ops).replace(hyper, hyperexpand_special)
    -        if not r.has(hyper):
    -            return r + p
    -
    -    # Try to find a formula in our collection
    -    f = _collection.lookup_origin(nip)
    -
    -    # Now try a lerch phi formula
    -    if f is None:
    -        f = try_lerchphi(nip)
    -
    -    if f is None:
    -        debug('  Could not find an origin.',
    -              'Will return answer in terms of ' +
    -              'simpler hypergeometric functions.')
    -        f = build_hypergeometric_formula(nip)
    -
    -    debug('  Found an origin:', f.closed_form, f.indices)
    -
    -    # We need to find the operators that convert f into (nap, nbq).
    -    ops += devise_plan(nip, f.indices, z0)
    -
    -    # Now carry out the plan.
    -    r = carryout_plan(f, ops) + p
    -
    -    return powdenest(r, polar=True).replace(hyper, hyperexpand_special)
    -
    -
    -def devise_plan_meijer(fro, to, z):
    -    """
    -    Find a sequence of operators to convert index quadruple ``fro`` into
    -    index quadruple ``to``. It is assumed that fro and to have the same
    -    signatures, and that in fact any corresponding pair of parameters differs
    -    by integers, and a direct path is possible. I.e. if there are parameters
    -       a1 b1 c1  and a2 b2 c2
    -    it is assumed that a1 can be shifted to a2, etc.
    -    The only thing this routine determines is the order of shifts to apply,
    -    nothing clever will be tried.
    -    It is also assumed that fro is suitable.
    -
    -    >>> from sympy.simplify.hyperexpand import (devise_plan_meijer,
    -    ...                                         IndexQuadruple)
    -    >>> from sympy.abc import z
    -
    -    Empty plan:
    -
    -    >>> devise_plan_meijer(IndexQuadruple([1], [2], [3], [4]),
    -    ...                    IndexQuadruple([1], [2], [3], [4]), z)
    -    []
    -
    -    Very simple plans:
    -
    -    >>> devise_plan_meijer(IndexQuadruple([0], [], [], []),
    -    ...                    IndexQuadruple([1], [], [], []), z)
    -    [<Increment upper a index #0 of [0], [], [], [].>]
    -    >>> devise_plan_meijer(IndexQuadruple([0], [], [], []),
    -    ...                    IndexQuadruple([-1], [], [], []), z)
    -    [<Decrement upper a=0.>]
    -    >>> devise_plan_meijer(IndexQuadruple([], [1], [], []),
    -    ...                    IndexQuadruple([], [2], [], []), z)
    -    [<Increment lower a index #0 of [], [1], [], [].>]
    -
    -    Slightly more complicated plans:
    -
    -    >>> devise_plan_meijer(IndexQuadruple([0], [], [], []),
    -    ...                    IndexQuadruple([2], [], [], []), z)
    -    [<Increment upper a index #0 of [1], [], [], [].>,
    -    <Increment upper a index #0 of [0], [], [], [].>]
    -    >>> devise_plan_meijer(IndexQuadruple([0], [], [0], []),
    -    ...                    IndexQuadruple([-1], [], [1], []), z)
    -    [<Increment upper b=0.>, <Decrement upper a=0.>]
    -
    -    Order matters:
    -
    -    >>> devise_plan_meijer(IndexQuadruple([0], [], [0], []),
    -    ...                    IndexQuadruple([1], [], [1], []), z)
    -    [<Increment upper a index #0 of [0], [], [1], [].>, <Increment upper b=0.>]
    -    """
    -    # TODO for now, we use the following simple heuristic: inverse-shift
    -    #      when possible, shift otherwise. Give up if we cannot make progress.
    -
    -    def try_shift(f, t, shifter, diff, counter):
    -        """ Try to apply ``shifter`` in order to bring some element in ``f``
    -            nearer to its counterpart in ``to``. ``diff`` is +/- 1 and
    -            determines the effect of ``shifter``. Counter is a list of elements
    -            blocking the shift.
    -
    -            Return an operator if change was possible, else None.
    -        """
    -        for idx, (a, b) in enumerate(list(zip(f, t))):
    -            if (
    -                (a - b).is_integer and (b - a)/diff > 0 and
    -                    all(a != x for x in counter)):
    -                sh = shifter(idx)
    -                f[idx] += diff
    -                return sh
    -    fan = list(fro.an)
    -    fap = list(fro.ap)
    -    fbm = list(fro.bm)
    -    fbq = list(fro.bq)
    -    ops = []
    -    change = True
    -    while change:
    -        change = False
    -        op = try_shift(fan, to.an,
    -                       lambda i: MeijerUnShiftB(fan, fap, fbm, fbq, i, z),
    -                       1, fbm + fbq)
    -        if op is not None:
    -            ops += [op]
    -            change = True
    -            continue
    -        op = try_shift(fap, to.ap,
    -                       lambda i: MeijerUnShiftD(fan, fap, fbm, fbq, i, z),
    -                       1, fbm + fbq)
    -        if op is not None:
    -            ops += [op]
    -            change = True
    -            continue
    -        op = try_shift(fbm, to.bm,
    -                       lambda i: MeijerUnShiftA(fan, fap, fbm, fbq, i, z),
    -                       -1, fan + fap)
    -        if op is not None:
    -            ops += [op]
    -            change = True
    -            continue
    -        op = try_shift(fbq, to.bq,
    -                       lambda i: MeijerUnShiftC(fan, fap, fbm, fbq, i, z),
    -                       -1, fan + fap)
    -        if op is not None:
    -            ops += [op]
    -            change = True
    -            continue
    -        op = try_shift(fan, to.an, lambda i: MeijerShiftB(fan[i]), -1, [])
    -        if op is not None:
    -            ops += [op]
    -            change = True
    -            continue
    -        op = try_shift(fap, to.ap, lambda i: MeijerShiftD(fap[i]), -1, [])
    -        if op is not None:
    -            ops += [op]
    -            change = True
    -            continue
    -        op = try_shift(fbm, to.bm, lambda i: MeijerShiftA(fbm[i]), 1, [])
    -        if op is not None:
    -            ops += [op]
    -            change = True
    -            continue
    -        op = try_shift(fbq, to.bq, lambda i: MeijerShiftC(fbq[i]), 1, [])
    -        if op is not None:
    -            ops += [op]
    -            change = True
    -            continue
    -    if fan != list(to.an) or fap != list(to.ap) or fbm != list(to.bm) or \
    -            fbq != list(to.bq):
    -        raise NotImplementedError('Could not devise plan.')
    -    ops.reverse()
    -    return ops
    -
    -_meijercollection = None
    -
    -
    -@_timeit
    -def _meijergexpand(iq, z0, allow_hyper=False, rewrite='default'):
    -    """
    -    Try to find an expression for the Meijer G function specified
    -    by the IndexQuadruple ``iq``. If ``allow_hyper`` is True, then returning
    -    an expression in terms of hypergeometric functions is allowed.
    -
    -    Currently this just does slater's theorem.
    -    """
    -    from sympy import hyper, Piecewise, meijerg, powdenest, re, polar_lift, oo
    -    global _meijercollection
    -    if _meijercollection is None:
    -        _meijercollection = MeijerFormulaCollection()
    -    if rewrite == 'default':
    -        rewrite = None
    -
    -    iq_ = iq
    -    debug('Try to expand meijer G function corresponding to', iq)
    -
    -    # We will play games with analytic continuation - rather use a fresh symbol
    -    z = Dummy('z')
    -
    -    iq, ops = reduce_order_meijer(iq)
    -    if ops:
    -        debug('  Reduced order to', iq)
    -    else:
    -        debug('  Could not reduce order.')
    -
    -    # Try to find a direct formula
    -    f = _meijercollection.lookup_origin(iq)
    -    if f is not None:
    -        debug('  Found a Meijer G formula:', f.indices)
    -        ops += devise_plan_meijer(f.indices, iq, z)
    -
    -        # Now carry out the plan.
    -        C = apply_operators(f.C.subs(f.z, z), ops,
    -                            make_derivative_operator(f.M.subs(f.z, z), z))
    -
    -        C = C.applyfunc(make_simp(z))
    -        r = C*f.B.subs(f.z, z)
    -        r = r[0].subs(z, z0)
    -        return powdenest(r, polar=True)
    -
    -    debug("  Could not find a direct formula. Trying slater's theorem.")
    -
    -    # TODO the following would be possible:
    -    # *) Paired Index Theorems
    -    # *) PFD Duplication
    -    #    (See Kelly Roach's paper for details on either.)
    -    #
    -    # TODO Also, we tend to create combinations of gamma functions that can be
    -    #      simplified.
    -
    -    def can_do(pbm, pap):
    -        """ Test if slater applies. """
    -        for i in pbm:
    -            if len(pbm[i]) > 1:
    -                l = 0
    -                if i in pap:
    -                    l = len(pap[i])
    -                if l + 1 < len(pbm[i]):
    -                    return False
    -        return True
    -
    -    def do_slater(an, bm, ap, bq, z, zfinal):
    -        from sympy import gamma, residue, factorial, rf, expand_func, \
    -            polar_lift
    -        # zfinal is the value that will eventually be substituted for z.
    -        # We pass it to _hyperexpand to improve performance.
    -        iq = IndexQuadruple(an, bm, ap, bq)
    -        _, pbm, pap, _ = iq.compute_buckets()
    -        if not can_do(pbm, pap):
    -            return S(0), False
    -
    -        cond = len(an) + len(ap) < len(bm) + len(bq)
    -        if len(an) + len(ap) == len(bm) + len(bq):
    -            cond = abs(z) < 1
    -        if cond is False:
    -            return S(0), False
    -
    -        res = S(0)
    -        for m in pbm:
    -            if len(pbm[m]) == 1:
    -                bh = pbm[m][0]
    -                fac = 1
    -                bo = list(bm)
    -                bo.remove(bh)
    -                for bj in bo:
    -                    fac *= gamma(bj - bh)
    -                for aj in an:
    -                    fac *= gamma(1 + bh - aj)
    -                for bj in bq:
    -                    fac /= gamma(1 + bh - bj)
    -                for aj in ap:
    -                    fac /= gamma(aj - bh)
    -                nap = [1 + bh - a for a in list(an) + list(ap)]
    -                nbq = [1 + bh - b for b in list(bo) + list(bq)]
    -
    -                k = polar_lift(S(-1)**(len(ap) - len(bm)))
    -                harg = k*zfinal
    -                # NOTE even though k "is" +-1, this has to be t/k instead of
    -                #      t*k ... we are using polar numbers for consistency!
    -                premult = (t/k)**bh
    -                hyp = _hyperexpand(IndexPair(nap, nbq), harg, ops,
    -                                   t, premult, bh, rewrite=None)
    -                res += fac * hyp
    -            else:
    -                b_ = pbm[m][0]
    -                ki = [bi - b_ for bi in pbm[m][1:]]
    -                u = len(ki)
    -                li = [ai - b_ for ai in pap[m][:u + 1]]
    -                bo = list(bm)
    -                for b in pbm[m]:
    -                    bo.remove(b)
    -                ao = list(ap)
    -                for a in pap[m][:u]:
    -                    ao.remove(a)
    -                lu = li[-1]
    -                di = [l - k for (l, k) in zip(li, ki)]
    -
    -                # We first work out the integrand:
    -                s = Dummy('s')
    -                integrand = z**s
    -                for b in bm:
    -                    integrand *= gamma(b - s)
    -                for a in an:
    -                    integrand *= gamma(1 - a + s)
    -                for b in bq:
    -                    integrand /= gamma(1 - b + s)
    -                for a in ap:
    -                    integrand /= gamma(a - s)
    -
    -                # Now sum the finitely many residues:
    -                # XXX This speeds up some cases - is it a good idea?
    -                integrand = expand_func(integrand)
    -                for r in range(lu):
    -                    resid = residue(integrand, s, b_ + r)
    -                    resid = apply_operators(resid, ops, lambda f: z*f.diff(z))
    -                    res -= resid
    -
    -                # Now the hypergeometric term.
    -                au = b_ + lu
    -                k = polar_lift(S(-1)**(len(ao) + len(bo) + 1))
    -                harg = k*zfinal
    -                premult = (t/k)**au
    -                nap = [1 + au - a for a in list(an) + list(ap)] + [1]
    -                nbq = [1 + au - b for b in list(bm) + list(bq)]
    -
    -                hyp = _hyperexpand(IndexPair(nap, nbq), harg, ops,
    -                                   t, premult, au, rewrite=None)
    -
    -                C = S(-1)**(lu)/factorial(lu)
    -                for i in range(u):
    -                    C *= S(-1)**di[i]/rf(lu - li[i] + 1, di[i])
    -                for a in an:
    -                    C *= gamma(1 - a + au)
    -                for b in bo:
    -                    C *= gamma(b - au)
    -                for a in ao:
    -                    C /= gamma(a - au)
    -                for b in bq:
    -                    C /= gamma(1 - b + au)
    -
    -                res += C*hyp
    -
    -        return res, cond
    -
    -    t = Dummy('t')
    -    slater1, cond1 = do_slater(iq.an, iq.bm, iq.ap, iq.bq, z, z0)
    -
    -    def tr(l):
    -        return [1 - x for x in l]
    -
    -    for op in ops:
    -        op._poly = Poly(op._poly.subs({z: 1/t, _x: -_x}), _x)
    -    slater2, cond2 = do_slater(tr(iq.bm), tr(iq.an), tr(iq.bq), tr(iq.ap),
    -                               t, 1/z0)
    -
    -    slater1 = powdenest(slater1.subs(z, z0), polar=True)
    -    slater2 = powdenest(slater2.subs(t, 1/z0), polar=True)
    -    if not isinstance(cond2, bool):
    -        cond2 = cond2.subs(t, 1/z)
    -
    -    m = meijerg(iq.an, iq.ap, iq.bm, iq.bq, z)
    -    if m.delta > 0 or \
    -        (m.delta == 0 and len(m.ap) == len(m.bq) and
    -            (re(m.nu) < -1) is not False and polar_lift(z0) == polar_lift(1)):
    -        # The condition delta > 0 means that the convergence region is
    -        # connected. Any expression we find can be continued analytically
    -        # to the entire convergence region.
    -        # The conditions delta==0, p==q, re(nu) < -1 imply that G is continuous
    -        # on the positive reals, so the values at z=1 agree.
    -        if cond1 is not False:
    -            cond1 = True
    -        if cond2 is not False:
    -            cond2 = True
    -
    -    if cond1 is True:
    -        slater1 = slater1.rewrite(rewrite or 'nonrep')
    -    else:
    -        slater1 = slater1.rewrite(rewrite or 'nonrepsmall')
    -    if cond2 is True:
    -        slater2 = slater2.rewrite(rewrite or 'nonrep')
    -    else:
    -        slater2 = slater2.rewrite(rewrite or 'nonrepsmall')
    -
    -    if not isinstance(cond1, bool):
    -        cond1 = cond1.subs(z, z0)
    -    if not isinstance(cond2, bool):
    -        cond2 = cond2.subs(z, z0)
    -
    -    def weight(expr, cond):
    -        from sympy import oo, zoo, nan
    -        if cond is True:
    -            c0 = 0
    -        elif cond is False:
    -            c0 = 1
    -        else:
    -            c0 = 2
    -        if expr.has(oo, zoo, -oo, nan):
    -            # XXX this actually should not happen, but consider
    -            # S('meijerg(((0, -1/2, 0, -1/2, 1/2), ()), ((0,),
    -            #   (-1/2, -1/2, -1/2, -1)), exp_polar(I*pi))/4')
    -            c0 = 3
    -        return (c0, expr.count(hyper), expr.count_ops())
    -
    -    w1 = weight(slater1, cond1)
    -    w2 = weight(slater2, cond2)
    -    if min(w1, w2) <= (0, 1, oo):
    -        if w1 < w2:
    -            return slater1
    -        else:
    -            return slater2
    -    if max(w1[0], w2[0]) <= 1 and max(w1[1], w2[1]) <= 1:
    -        return Piecewise((slater1, cond1), (slater2, cond2),
    -                   (meijerg(iq_.an, iq_.ap, iq_.bm, iq_.bq, z0), True))
    -
    -    # We couldn't find an expression without hypergeometric functions.
    -    # TODO it would be helpful to give conditions under which the integral
    -    #      is known to diverge.
    -    r = Piecewise((slater1, cond1), (slater2, cond2),
    -                  (meijerg(iq_.an, iq_.ap, iq_.bm, iq_.bq, z0), True))
    -    if r.has(hyper) and not allow_hyper:
    -        debug('  Could express using hypergeometric functions, ' +
    -              'but not allowed.')
    -    if not r.has(hyper) or allow_hyper:
    -        return r
    -
    -    return meijerg(iq_.an, iq_.ap, iq_.bm, iq_.bq, z0)
    -
    -
    -
    [docs]def hyperexpand(f, allow_hyper=False, rewrite='default'): - """ - Expand hypergeometric functions. If allow_hyper is True, allow partial - simplification (that is a result different from input, - but still containing hypergeometric functions). - - Examples - ======== - - >>> from sympy.simplify.hyperexpand import hyperexpand - >>> from sympy.functions import hyper - >>> from sympy.abc import z - >>> hyperexpand(hyper([], [], z)) - exp(z) - - Non-hyperegeometric parts of the expression and hypergeometric expressions - that are not recognised are left unchanged: - - >>> hyperexpand(1 + hyper([1, 1, 1], [], z)) - hyper((1, 1, 1), (), z) + 1 - """ - from sympy.functions import hyper, meijerg - from sympy import nan, zoo, oo - f = sympify(f) - - def do_replace(ap, bq, z): - r = _hyperexpand(IndexPair(ap, bq), z, rewrite=rewrite) - if r is None: - return hyper(ap, bq, z) - else: - return r - - def do_meijer(ap, bq, z): - r = _meijergexpand(IndexQuadruple(ap[0], ap[1], bq[0], bq[1]), z, - allow_hyper, rewrite=rewrite) - if not r.has(nan, zoo, oo, -oo): - return r - return f.replace(hyper, do_replace).replace(meijerg, do_meijer) -
    -from sympy.polys.polytools import Poly -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/simplify/simplify.html b/dev-py3k/_modules/sympy/simplify/simplify.html deleted file mode 100644 index 25cc9fc4123..00000000000 --- a/dev-py3k/_modules/sympy/simplify/simplify.html +++ /dev/null @@ -1,3561 +0,0 @@ - - - - - - - - - - sympy.simplify.simplify — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.simplify.simplify

    -from collections import defaultdict
    -
    -from sympy import SYMPY_DEBUG
    -
    -from sympy.core import (Basic, S, C, Add, Mul, Pow, Rational, Integer,
    -    Derivative, Wild, Symbol, sympify, expand, expand_mul, expand_func,
    -    Function, Equality, Dummy, Atom, Expr, factor_terms,
    -    expand_multinomial, expand_power_base, symbols)
    -
    -from sympy.core.compatibility import iterable, reduce, default_sort_key
    -from sympy.core.numbers import Float
    -from sympy.core.function import expand_log, count_ops
    -from sympy.core.mul import _keep_coeff, prod
    -from sympy.core.rules import Transform
    -
    -from sympy.functions import gamma, exp, sqrt, log, root, exp_polar
    -from sympy.utilities.iterables import flatten, has_variety
    -
    -from sympy.simplify.cse_main import cse
    -from sympy.simplify.cse_opts import sub_pre, sub_post
    -from sympy.simplify.sqrtdenest import sqrtdenest
    -
    -from sympy.polys import (Poly, together, reduced, cancel, factor,
    -    ComputationFailed, lcm, gcd)
    -
    -import sympy.mpmath as mpmath
    -from functools import reduce
    -
    -
    -def _mexpand(expr):
    -    return expand_mul(expand_multinomial(expr))
    -
    -
    -
    [docs]def fraction(expr, exact=False): - """Returns a pair with expression's numerator and denominator. - If the given expression is not a fraction then this function - will return the tuple (expr, 1). - - This function will not make any attempt to simplify nested - fractions or to do any term rewriting at all. - - If only one of the numerator/denominator pair is needed then - use numer(expr) or denom(expr) functions respectively. - - >>> from sympy import fraction, Rational, Symbol - >>> from sympy.abc import x, y - - >>> fraction(x/y) - (x, y) - >>> fraction(x) - (x, 1) - - >>> fraction(1/y**2) - (1, y**2) - - >>> fraction(x*y/2) - (x*y, 2) - >>> fraction(Rational(1, 2)) - (1, 2) - - This function will also work fine with assumptions: - - >>> k = Symbol('k', negative=True) - >>> fraction(x * y**k) - (x, y**(-k)) - - If we know nothing about sign of some exponent and 'exact' - flag is unset, then structure this exponent's structure will - be analyzed and pretty fraction will be returned: - - >>> from sympy import exp - >>> fraction(2*x**(-y)) - (2, x**y) - - >>> fraction(exp(-x)) - (1, exp(x)) - - >>> fraction(exp(-x), exact=True) - (exp(-x), 1) - - """ - expr = sympify(expr) - - numer, denom = [], [] - - for term in Mul.make_args(expr): - if term.is_commutative and (term.is_Pow or term.func is exp): - b, ex = term.as_base_exp() - if ex.is_negative: - if ex is S.NegativeOne: - denom.append(b) - else: - denom.append(Pow(b, -ex)) - elif ex.is_positive: - numer.append(term) - elif not exact and ex.is_Mul: - n, d = term.as_numer_denom() - numer.append(n) - denom.append(d) - else: - numer.append(term) - elif term.is_Rational: - n, d = term.as_numer_denom() - numer.append(n) - denom.append(d) - else: - numer.append(term) - - return Mul(*numer), Mul(*denom) - -
    -def numer(expr): - return fraction(expr)[0] - - -def denom(expr): - return fraction(expr)[1] - - -def fraction_expand(expr, **hints): - return expr.expand(frac=True, **hints) - - -def numer_expand(expr, **hints): - a, b = fraction(expr) - return a.expand(numer=True, **hints) / b - - -def denom_expand(expr, **hints): - a, b = fraction(expr) - return a / b.expand(denom=True, **hints) - -expand_numer = numer_expand -expand_denom = denom_expand -expand_fraction = fraction_expand - - -
    [docs]def separate(expr, deep=False, force=False): - """ - Deprecated wrapper around ``expand_power_base()``. Use that function instead. - """ - from sympy.utilities.exceptions import SymPyDeprecationWarning - SymPyDeprecationWarning( - feature="separate()", useinstead="expand_power_base()", issue=3383, - deprecated_since_version="0.7.2", value="Note: in separate() deep " - "defaults to False, whereas in expand_power_base(), deep defaults to True.", - ).warn() - return expand_power_base(sympify(expr), deep=deep, force=force) - -
    -
    [docs]def collect(expr, syms, func=None, evaluate=True, exact=False, distribute_order_term=True): - """ - Collect additive terms of an expression. - - This function collects additive terms of an expression with respect - to a list of expression up to powers with rational exponents. By the - term symbol here are meant arbitrary expressions, which can contain - powers, products, sums etc. In other words symbol is a pattern which - will be searched for in the expression's terms. - - The input expression is not expanded by :func:`collect`, so user is - expected to provide an expression is an appropriate form. This makes - :func:`collect` more predictable as there is no magic happening behind the - scenes. However, it is important to note, that powers of products are - converted to products of powers using the :func:`expand_power_base` - function. - - There are two possible types of output. First, if ``evaluate`` flag is - set, this function will return an expression with collected terms or - else it will return a dictionary with expressions up to rational powers - as keys and collected coefficients as values. - - Examples - ======== - - >>> from sympy import S, collect, expand, factor, Wild - >>> from sympy.abc import a, b, c, x, y, z - - This function can collect symbolic coefficients in polynomials or - rational expressions. It will manage to find all integer or rational - powers of collection variable:: - - >>> collect(a*x**2 + b*x**2 + a*x - b*x + c, x) - c + x**2*(a + b) + x*(a - b) - - The same result can be achieved in dictionary form:: - - >>> d = collect(a*x**2 + b*x**2 + a*x - b*x + c, x, evaluate=False) - >>> d[x**2] - a + b - >>> d[x] - a - b - >>> d[S.One] - c - - You can also work with multivariate polynomials. However, remember that - this function is greedy so it will care only about a single symbol at time, - in specification order:: - - >>> collect(x**2 + y*x**2 + x*y + y + a*y, [x, y]) - x**2*(y + 1) + x*y + y*(a + 1) - - Also more complicated expressions can be used as patterns:: - - >>> from sympy import sin, log - >>> collect(a*sin(2*x) + b*sin(2*x), sin(2*x)) - (a + b)*sin(2*x) - - >>> collect(a*x*log(x) + b*(x*log(x)), x*log(x)) - x*(a + b)*log(x) - - You can use wildcards in the pattern:: - - >>> w = Wild('w1') - >>> collect(a*x**y - b*x**y, w**y) - x**y*(a - b) - - It is also possible to work with symbolic powers, although it has more - complicated behavior, because in this case power's base and symbolic part - of the exponent are treated as a single symbol:: - - >>> collect(a*x**c + b*x**c, x) - a*x**c + b*x**c - >>> collect(a*x**c + b*x**c, x**c) - x**c*(a + b) - - However if you incorporate rationals to the exponents, then you will get - well known behavior:: - - >>> collect(a*x**(2*c) + b*x**(2*c), x**c) - x**(2*c)*(a + b) - - Note also that all previously stated facts about :func:`collect` function - apply to the exponential function, so you can get:: - - >>> from sympy import exp - >>> collect(a*exp(2*x) + b*exp(2*x), exp(x)) - (a + b)*exp(2*x) - - If you are interested only in collecting specific powers of some symbols - then set ``exact`` flag in arguments:: - - >>> collect(a*x**7 + b*x**7, x, exact=True) - a*x**7 + b*x**7 - >>> collect(a*x**7 + b*x**7, x**7, exact=True) - x**7*(a + b) - - You can also apply this function to differential equations, where derivatives - of arbitrary order can be collected. Note that if you collect with respect - to a function or a derivative of a function, all derivatives of that function - will also be collected. Use ``exact=True`` to prevent this from happening:: - - >>> from sympy import Derivative as D, collect, Function - >>> f = Function('f') (x) - - >>> collect(a*D(f,x) + b*D(f,x), D(f,x)) - (a + b)*Derivative(f(x), x) - - >>> collect(a*D(D(f,x),x) + b*D(D(f,x),x), f) - (a + b)*Derivative(f(x), x, x) - - >>> collect(a*D(D(f,x),x) + b*D(D(f,x),x), D(f,x), exact=True) - a*Derivative(f(x), x, x) + b*Derivative(f(x), x, x) - - >>> collect(a*D(f,x) + b*D(f,x) + a*f + b*f, f) - (a + b)*f(x) + (a + b)*Derivative(f(x), x) - - Or you can even match both derivative order and exponent at the same time:: - - >>> collect(a*D(D(f,x),x)**2 + b*D(D(f,x),x)**2, D(f,x)) - (a + b)*Derivative(f(x), x, x)**2 - - Finally, you can apply a function to each of the collected coefficients. - For example you can factorize symbolic coefficients of polynomial:: - - >>> f = expand((x + a + 1)**3) - - >>> collect(f, x, factor) - x**3 + 3*x**2*(a + 1) + 3*x*(a + 1)**2 + (a + 1)**3 - - .. note:: Arguments are expected to be in expanded form, so you might have - to call :func:`expand` prior to calling this function. - - """ - def make_expression(terms): - product = [] - - for term, rat, sym, deriv in terms: - if deriv is not None: - var, order = deriv - - while order > 0: - term, order = Derivative(term, var), order - 1 - - if sym is None: - if rat is S.One: - product.append(term) - else: - product.append(Pow(term, rat)) - else: - product.append(Pow(term, rat*sym)) - - return Mul(*product) - - def parse_derivative(deriv): - # scan derivatives tower in the input expression and return - # underlying function and maximal differentiation order - expr, sym, order = deriv.expr, deriv.variables[0], 1 - - for s in deriv.variables[1:]: - if s == sym: - order += 1 - else: - raise NotImplementedError( - 'Improve MV Derivative support in collect') - - while isinstance(expr, Derivative): - s0 = expr.variables[0] - - for s in expr.variables: - if s != s0: - raise NotImplementedError( - 'Improve MV Derivative support in collect') - - if s0 == sym: - expr, order = expr.expr, order + len(expr.variables) - else: - break - - return expr, (sym, Rational(order)) - - def parse_term(expr): - """Parses expression expr and outputs tuple (sexpr, rat_expo, - sym_expo, deriv) - where: - - sexpr is the base expression - - rat_expo is the rational exponent that sexpr is raised to - - sym_expo is the symbolic exponent that sexpr is raised to - - deriv contains the derivatives the the expression - - for example, the output of x would be (x, 1, None, None) - the output of 2**x would be (2, 1, x, None) - """ - rat_expo, sym_expo = S.One, None - sexpr, deriv = expr, None - - if expr.is_Pow: - if isinstance(expr.base, Derivative): - sexpr, deriv = parse_derivative(expr.base) - else: - sexpr = expr.base - - if expr.exp.is_Number: - rat_expo = expr.exp - else: - coeff, tail = expr.exp.as_coeff_Mul() - - if coeff.is_Number: - rat_expo, sym_expo = coeff, tail - else: - sym_expo = expr.exp - elif expr.func is C.exp: - arg = expr.args[0] - if arg.is_Rational: - sexpr, rat_expo = S.Exp1, arg - elif arg.is_Mul: - coeff, tail = arg.as_coeff_Mul(rational=True) - sexpr, rat_expo = C.exp(tail), coeff - elif isinstance(expr, Derivative): - sexpr, deriv = parse_derivative(expr) - - return sexpr, rat_expo, sym_expo, deriv - - def parse_expression(terms, pattern): - """Parse terms searching for a pattern. - terms is a list of tuples as returned by parse_terms; - pattern is an expression treated as a product of factors - """ - pattern = Mul.make_args(pattern) - - if len(terms) < len(pattern): - # pattern is longer than matched product - # so no chance for positive parsing result - return None - else: - pattern = [parse_term(elem) for elem in pattern] - - terms = terms[:] # need a copy - elems, common_expo, has_deriv = [], None, False - - for elem, e_rat, e_sym, e_ord in pattern: - - if elem.is_Number and e_rat == 1 and e_sym is None: - # a constant is a match for everything - continue - - for j in range(len(terms)): - if terms[j] is None: - continue - - term, t_rat, t_sym, t_ord = terms[j] - - # keeping track of whether one of the terms had - # a derivative or not as this will require rebuilding - # the expression later - if t_ord is not None: - has_deriv = True - - if (term.match(elem) is not None and - (t_sym == e_sym or t_sym is not None and - e_sym is not None and - t_sym.match(e_sym) is not None)): - if exact is False: - # we don't have to be exact so find common exponent - # for both expression's term and pattern's element - expo = t_rat / e_rat - - if common_expo is None: - # first time - common_expo = expo - else: - # common exponent was negotiated before so - # there is no chance for a pattern match unless - # common and current exponents are equal - if common_expo != expo: - common_expo = 1 - else: - # we ought to be exact so all fields of - # interest must match in every details - if e_rat != t_rat or e_ord != t_ord: - continue - - # found common term so remove it from the expression - # and try to match next element in the pattern - elems.append(terms[j]) - terms[j] = None - - break - - else: - # pattern element not found - return None - - return [_f for _f in terms if _f], elems, common_expo, has_deriv - - if evaluate: - if expr.is_Mul: - return Mul(*[ collect(term, syms, func, True, exact, distribute_order_term) for term in expr.args ]) - elif expr.is_Pow: - b = collect( - expr.base, syms, func, True, exact, distribute_order_term) - return Pow(b, expr.exp) - - if iterable(syms): - syms = [expand_power_base(i, deep=False) for i in syms] - else: - syms = [ expand_power_base(syms, deep=False) ] - - expr = sympify(expr) - order_term = None - - if distribute_order_term: - order_term = expr.getO() - - if order_term is not None: - if order_term.has(*syms): - order_term = None - else: - expr = expr.removeO() - - summa = [expand_power_base(i, deep=False) for i in Add.make_args(expr)] - - collected, disliked = defaultdict(list), S.Zero - for product in summa: - terms = [parse_term(i) for i in Mul.make_args(product)] - - for symbol in syms: - if SYMPY_DEBUG: - print("DEBUG: parsing of expression %s with symbol %s " % ( - str(terms), str(symbol))) - - result = parse_expression(terms, symbol) - - if SYMPY_DEBUG: - print("DEBUG: returned %s" % str(result)) - - if result is not None: - terms, elems, common_expo, has_deriv = result - - # when there was derivative in current pattern we - # will need to rebuild its expression from scratch - if not has_deriv: - index = 1 - for elem in elems: - e = elem[1] - if elem[2] is not None: - e *= elem[2] - index *= Pow(elem[0], e) - else: - index = make_expression(elems) - terms = expand_power_base(make_expression(terms), deep=False) - index = expand_power_base(index, deep=False) - collected[index].append(terms) - break - else: - # none of the patterns matched - disliked += product - # add terms now for each key - collected = dict([(k, Add(*v)) for k, v in collected.items()]) - - if disliked is not S.Zero: - collected[S.One] = disliked - - if order_term is not None: - for key, val in collected.items(): - collected[key] = val + order_term - - if func is not None: - collected = dict( - [ (key, func(val)) for key, val in collected.items() ]) - - if evaluate: - return Add(*[key*val for key, val in collected.items()]) - else: - return collected - -
    -
    [docs]def rcollect(expr, *vars): - """ - Recursively collect sums in an expression. - - Examples - ======== - - >>> from sympy.simplify import rcollect - >>> from sympy.abc import x, y - - >>> expr = (x**2*y + x*y + x + y)/(x + y) - - >>> rcollect(expr, y) - (x + y*(x**2 + x + 1))/(x + y) - - """ - if expr.is_Atom or not expr.has(*vars): - return expr - else: - expr = expr.__class__(*[ rcollect(arg, *vars) for arg in expr.args ]) - - if expr.is_Add: - return collect(expr, vars) - else: - return expr - -
    -
    [docs]def separatevars(expr, symbols=[], dict=False, force=False): - """ - Separates variables in an expression, if possible. By - default, it separates with respect to all symbols in an - expression and collects constant coefficients that are - independent of symbols. - - If dict=True then the separated terms will be returned - in a dictionary keyed to their corresponding symbols. - By default, all symbols in the expression will appear as - keys; if symbols are provided, then all those symbols will - be used as keys, and any terms in the expression containing - other symbols or non-symbols will be returned keyed to the - string 'coeff'. (Passing None for symbols will return the - expression in a dictionary keyed to 'coeff'.) - - If force=True, then bases of powers will be separated regardless - of assumptions on the symbols involved. - - Notes - ===== - The order of the factors is determined by Mul, so that the - separated expressions may not necessarily be grouped together. - - Although factoring is necessary to separate variables in some - expressions, it is not necessary in all cases, so one should not - count on the returned factors being factored. - - Examples - ======== - - >>> from sympy.abc import x, y, z, alpha - >>> from sympy import separatevars, sin - >>> separatevars((x*y)**y) - (x*y)**y - >>> separatevars((x*y)**y, force=True) - x**y*y**y - - >>> e = 2*x**2*z*sin(y)+2*z*x**2 - >>> separatevars(e) - 2*x**2*z*(sin(y) + 1) - >>> separatevars(e, symbols=(x, y), dict=True) - {'coeff': 2*z, x: x**2, y: sin(y) + 1} - >>> separatevars(e, [x, y, alpha], dict=True) - {'coeff': 2*z, alpha: 1, x: x**2, y: sin(y) + 1} - - If the expression is not really separable, or is only partially - separable, separatevars will do the best it can to separate it - by using factoring. - - >>> separatevars(x + x*y - 3*x**2) - -x*(3*x - y - 1) - - If the expression is not separable then expr is returned unchanged - or (if dict=True) then None is returned. - - >>> eq = 2*x + y*sin(x) - >>> separatevars(eq) == eq - True - >>> separatevars(2*x + y*sin(x), symbols=(x, y), dict=True) == None - True - - """ - expr = sympify(expr) - if dict: - return _separatevars_dict(_separatevars(expr, force), symbols) - else: - return _separatevars(expr, force) - -
    -def _separatevars(expr, force): - if len(expr.free_symbols) == 1: - return expr - # don't destroy a Mul since much of the work may already be done - if expr.is_Mul: - args = list(expr.args) - changed = False - for i, a in enumerate(args): - args[i] = separatevars(a, force) - changed = changed or args[i] != a - if changed: - expr = Mul(*args) - return expr - - # get a Pow ready for expansion - if expr.is_Pow: - expr = Pow(separatevars(expr.base, force=force), expr.exp) - - # First try other expansion methods - expr = expr.expand(mul=False, multinomial=False, force=force) - - _expr = expr - if expr.is_commutative: # factor fails for nc - _expr, reps = posify(expr) if force else (expr, {}) - expr = factor(_expr).subs(reps) - - if not expr.is_Add: - return expr - - # Find any common coefficients to pull out - args = list(expr.args) - commonc = args[0].args_cnc(cset=True, warn=False)[0] - for i in args[1:]: - commonc &= i.args_cnc(cset=True, warn=False)[0] - commonc = Mul(*commonc) - commonc = commonc.as_coeff_Mul()[1] # ignore constants - commonc_set = commonc.args_cnc(cset=True, warn=False)[0] - - # remove them - for i, a in enumerate(args): - c, nc = a.args_cnc(cset=True, warn=False) - c = c - commonc_set - args[i] = Mul(*c)*Mul(*nc) - nonsepar = Add(*args) - - if len(nonsepar.free_symbols) > 1: - _expr = nonsepar - _expr, reps = posify(_expr) if force else (_expr, {}) - _expr = (factor(_expr)).subs(reps) - - if not _expr.is_Add: - nonsepar = _expr - - return commonc*nonsepar - - -def _separatevars_dict(expr, symbols): - if symbols: - assert all((t.is_Atom for t in symbols)), "symbols must be Atoms." - symbols = list(symbols) - elif symbols is None: - return {'coeff': expr} - else: - symbols = list(expr.free_symbols) - if not symbols: - return None - - ret = dict(((i, []) for i in symbols + ['coeff'])) - - for i in Mul.make_args(expr): - expsym = i.free_symbols - intersection = set(symbols).intersection(expsym) - if len(intersection) > 1: - return None - if len(intersection) == 0: - # There are no symbols, so it is part of the coefficient - ret['coeff'].append(i) - else: - ret[intersection.pop()].append(i) - - # rebuild - for k, v in list(ret.items()): - ret[k] = Mul(*v) - - return ret - - -
    [docs]def ratsimp(expr): - """ - Put an expression over a common denominator, cancel and reduce. - - Examples - ======== - - >>> from sympy import ratsimp - >>> from sympy.abc import x, y - >>> ratsimp(1/x + 1/y) - (x + y)/(x*y) - """ - - f, g = cancel(expr).as_numer_denom() - try: - Q, r = reduced(f, [g], field=True, expand=False) - except ComputationFailed: - return f/g - - return Add(*Q) + cancel(r/g) - -
    -def ratsimpmodprime(expr, G, *gens, **args): - """ - Simplifies a rational expression ``expr`` modulo the prime ideal - generated by ``G``. ``G`` should be a Groebner basis of the - ideal. - - >>> from sympy.simplify.simplify import ratsimpmodprime - >>> from sympy.abc import x, y - >>> ratsimpmodprime((x + y**5 + y)/(x - y), [x*y**5 - x - y], x, y, order='lex') - (x**2 + x*y + x + y)/(x**2 - x*y) - - The algorithm computes a rational simplification which minimizes - the sum of the total degrees of the numerator and the denominator. - - References - ========== - - M. Monagan, R. Pearce, Rational Simplification Modulo a Polynomial - Ideal, - http://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.163.6984 - (specifically, the second algorithm) - """ - from sympy.polys import parallel_poly_from_expr - from sympy.polys.polyerrors import PolificationFailed, DomainError - from sympy import solve, Monomial - from sympy.polys.monomialtools import monomial_div - from sympy.core.compatibility import product - - # usual preparation of polynomials: - - num, denom = cancel(expr).as_numer_denom() - - try: - polys, opt = parallel_poly_from_expr([num, denom] + G, *gens, **args) - except PolificationFailed as exc: - return expr - - domain = opt.domain - - if domain.has_assoc_Field: - opt.domain = domain.get_field() - else: - raise DomainError( - "can't compute rational simplification over %s" % domain) - - # compute only once - leading_monomials = [g.LM(opt.order) for g in polys[2:]] - - def staircase(n): - """ - Compute all monomials with degree less than ``n`` that are - not divisible by any element of ``leading_monomials``. - """ - S = [] - for m in product(*([range(n + 1)] * len(opt.gens))): - if sum(m) <= n: - if all([monomial_div(m, lmg) is None for lmg in leading_monomials]): - S.append(m) - - return [Monomial(s).as_expr(*opt.gens) for s in S] - - def _ratsimpmodprime(a, b, N=0, D=0): - """ - Computes a rational simplification of ``a/b`` which minimizes - the sum of the total degrees of the numerator and the denominator. - - The algorithm proceeds by looking at ``a * d - b * c`` modulo - the ideal generated by ``G`` for some ``c`` and ``d`` with degree - less than ``a`` and ``b`` respectively. - The coefficients of ``c`` and ``d`` are indeterminates and thus - the coefficients of the normalform of ``a * d - b * c`` are - linear polynomials in these indeterminates. - If these linear polynomials, considered as system of - equations, have a nontrivial solution, then `\frac{a}{b} - \equiv \frac{c}{d}` modulo the ideal generated by ``G``. So, - by construction, the degree of ``c`` and ``d`` is less than - the degree of ``a`` and ``b``, so a simpler representation - has been found. - After a simpler representation has been found, the algorithm - tries to reduce the degree of the numerator and denominator - and returns the result afterwards. - """ - c, d = a, b - steps = 0 - - while N + D < a.total_degree() + b.total_degree(): - M1 = staircase(N) - M2 = staircase(D) - - Cs = symbols("c:%d" % len(M1)) - Ds = symbols("d:%d" % len(M2)) - - c_hat = Poly( - sum([Cs[i] * M1[i] for i in range(len(M1))]), opt.gens) - d_hat = Poly( - sum([Ds[i] * M2[i] for i in range(len(M2))]), opt.gens) - - r = reduced(a * d_hat - b * c_hat, G, opt.gens, - order=opt.order, polys=True)[1] - - S = r.coeffs() - sol = solve(S, Cs + Ds) - - # If nontrivial solutions exist, solve will give them - # parametrized, i.e. the values of some keys will be - # exprs. Set these to any value different from 0 to obtain - # one nontrivial solution: - for key in list(sol.keys()): - sol[key] = sol[key].subs(dict(list(zip(Cs + Ds, - [1] * (len(Cs) + len(Ds)))))) - - if sol and not all([s == 0 for s in sol.values()]): - c = c_hat.subs(sol) - d = d_hat.subs(sol) - - # The "free" variables occuring before as parameters - # might still be in the substituted c, d, so set them - # to the value chosen before: - c = c.subs(dict(list(zip(Cs + Ds, [1] * (len(Cs) + len(Ds)))))) - d = d.subs(dict(list(zip(Cs + Ds, [1] * (len(Cs) + len(Ds)))))) - - c = Poly(c, opt.gens) - d = Poly(d, opt.gens) - - break - - N += 1 - D += 1 - steps += 1 - - if steps > 0: - c, d = _ratsimpmodprime(c, d, N, D - steps) - c, d = _ratsimpmodprime(c, d, N - steps, D) - - return c, d - - # preprocessing. this improves performance a bit when deg(num) - # and deg(denom) are large: - num = reduced(num, G, opt.gens, order=opt.order)[1] - denom = reduced(denom, G, opt.gens, order=opt.order)[1] - - c, d = _ratsimpmodprime(Poly(num, opt.gens), Poly(denom, opt.gens)) - - if not domain.has_Field: - c = c.clear_denoms(convert=True)[1] - d = d.clear_denoms(convert=True)[1] - - return c/d - - -
    [docs]def trigsimp(expr, deep=False, recursive=False): - """ - reduces expression by using known trig identities - - Notes - ===== - - deep: - - Apply trigsimp inside all objects with arguments - - recursive: - - Use common subexpression elimination (cse()) and apply - trigsimp recursively (this is quite expensive if the - expression is large) - - Examples - ======== - - >>> from sympy import trigsimp, sin, cos, log, cosh, sinh - >>> from sympy.abc import x, y - >>> e = 2*sin(x)**2 + 2*cos(x)**2 - >>> trigsimp(e) - 2 - >>> trigsimp(log(e)) - log(2*sin(x)**2 + 2*cos(x)**2) - >>> trigsimp(log(e), deep=True) - log(2) - - """ - if not expr.has(C.TrigonometricFunction, C.HyperbolicFunction): - return expr - - if recursive: - w, g = cse(expr) - g = _trigsimp(g[0], deep) - - for sub in reversed(w): - g = g.subs(sub[0], sub[1]) - g = _trigsimp(g, deep) - result = g - else: - result = _trigsimp(expr, deep) - - return result - -
    -def _trigsimp(expr, deep=False): - """recursive helper for trigsimp""" - a, b, c = symbols('a b c', cls=Wild) - d = Wild('d', commutative=False) - sin, cos, tan, cot = C.sin, C.cos, C.tan, C.cot - sinh, cosh, tanh, coth = C.sinh, C.cosh, C.tanh, C.coth - # for the simplifications like sinh/cosh -> tanh: - matchers_division = ( - (a*sin(b)**c/cos(b)**c, a*tan(b)**c), - (a*tan(b)**c*cos(b)**c, a*sin(b)**c), - (a*cot(b)**c*sin(b)**c, a*cos(b)**c), - (a*tan(b)**c/sin(b)**c, a/cos(b)**c), - (a*cot(b)**c/cos(b)**c, a/sin(b)**c), - (a*cot(b)**c*tan(b)**c, a), - - (a*sinh(b)**c/cosh(b)**c, a*tanh(b)**c), - (a*tanh(b)**c*cosh(b)**c, a*sinh(b)**c), - (a*coth(b)**c*sinh(b)**c, a*cosh(b)**c), - (a*tanh(b)**c/sinh(b)**c, a/cosh(b)**c), - (a*coth(b)**c/cosh(b)**c, a/sinh(b)**c), - (a*coth(b)**c*tanh(b)**c, a), - - # same as above but with noncommutative prefactor - (a*d*sin(b)**c/cos(b)**c, a*d*tan(b)**c), - (a*d*tan(b)**c*cos(b)**c, a*d*sin(b)**c), - (a*d*cot(b)**c*sin(b)**c, a*d*cos(b)**c), - (a*d*tan(b)**c/sin(b)**c, a*d/cos(b)**c), - (a*d*cot(b)**c/cos(b)**c, a*d/sin(b)**c), - (a*d*cot(b)**c*tan(b)**c, a*d), - - (a*d*sinh(b)**c/cosh(b)**c, a*d*tanh(b)**c), - (a*d*tanh(b)**c*cosh(b)**c, a*d*sinh(b)**c), - (a*d*coth(b)**c*sinh(b)**c, a*d*cosh(b)**c), - (a*d*tanh(b)**c/sinh(b)**c, a*d/cosh(b)**c), - (a*d*coth(b)**c/cosh(b)**c, a*d/sinh(b)**c), - (a*d*coth(b)**c*tanh(b)**c, a*d), - ) - # for cos(x)**2 + sin(x)**2 -> 1 - matchers_identity = ( - (a*sin(b)**2, a - a*cos(b)**2), - (a*tan(b)**2, a*(1/cos(b))**2 - a), - (a*cot(b)**2, a*(1/sin(b))**2 - a), - (a*sin(b + c), a*(sin(b)*cos(c) + sin(c)*cos(b))), - (a*cos(b + c), a*(cos(b)*cos(c) - sin(b)*sin(c))), - (a*tan(b + c), a*((tan(b) + tan(c))/(1 - tan(b)*tan(c)))), - - (a*sinh(b)**2, a*cosh(b)**2 - a), - (a*tanh(b)**2, a - a*(1/cosh(b))**2), - (a*coth(b)**2, a + a*(1/sinh(b))**2), - (a*sinh(b + c), a*(sinh(b)*cosh(c) + sinh(c)*cosh(b))), - (a*cosh(b + c), a*(cosh(b)*cosh(c) + sinh(b)*sinh(c))), - (a*tanh(b + c), a*((tanh(b) + tanh(c))/(1 + tanh(b)*tanh(c)))), - - # same as above but with noncommutative prefactor - (a*d*sin(b)**2, a*d - a*d*cos(b)**2), - (a*d*tan(b)**2, a*d*(1/cos(b))**2 - a*d), - (a*d*cot(b)**2, a*d*(1/sin(b))**2 - a*d), - (a*d*sin(b + c), a*d*(sin(b)*cos(c) + sin(c)*cos(b))), - (a*d*cos(b + c), a*d*(cos(b)*cos(c) - sin(b)*sin(c))), - (a*d*tan(b + c), a*d*((tan(b) + tan(c))/(1 - tan(b)*tan(c)))), - - (a*d*sinh(b)**2, a*d*cosh(b)**2 - a*d), - (a*d*tanh(b)**2, a*d - a*d*(1/cosh(b))**2), - (a*d*coth(b)**2, a*d + a*d*(1/sinh(b))**2), - (a*d*sinh(b + c), a*d*(sinh(b)*cosh(c) + sinh(c)*cosh(b))), - (a*d*cosh(b + c), a*d*(cosh(b)*cosh(c) + sinh(b)*sinh(c))), - (a*d*tanh(b + c), a*d*((tanh(b) + tanh(c))/(1 + tanh(b)*tanh(c)))), - ) - - # Reduce any lingering artefacts, such as sin(x)**2 changing - # to 1-cos(x)**2 when sin(x)**2 was "simpler" - artifacts = ( - (a - a*cos(b)**2 + c, a*sin(b)**2 + c, cos), - (a - a*(1/cos(b))**2 + c, -a*tan(b)**2 + c, cos), - (a - a*(1/sin(b))**2 + c, -a*cot(b)**2 + c, sin), - - (a - a*cosh(b)**2 + c, -a*sinh(b)**2 + c, cosh), - (a - a*(1/cosh(b))**2 + c, a*tanh(b)**2 + c, cosh), - (a + a*(1/sinh(b))**2 + c, a*coth(b)**2 + c, sinh), - - # same as above but with noncommutative prefactor - (a*d - a*d*cos(b)**2 + c, a*d*sin(b)**2 + c, cos), - (a*d - a*d*(1/cos(b))**2 + c, -a*d*tan(b)**2 + c, cos), - (a*d - a*d*(1/sin(b))**2 + c, -a*d*cot(b)**2 + c, sin), - - (a*d - a*d*cosh(b)**2 + c, -a*d*sinh(b)**2 + c, cosh), - (a*d - a*d*(1/cosh(b))**2 + c, a*d*tanh(b)**2 + c, cosh), - (a*d + a*d*(1/sinh(b))**2 + c, a*d*coth(b)**2 + c, sinh), - ) - - if expr.is_Mul: - # do some simplifications like sin/cos -> tan: - for pattern, simp in matchers_division: - res = expr.match(pattern) - if res and res.get(c, 0): - # if "a" contains any of sin("b"), cos("b"), tan("b"), cot("b"), - # sinh("b"), cosh("b"), tanh("b") or coth("b), - # skip the simplification: - if res[a].has(C.TrigonometricFunction, C.HyperbolicFunction): - continue - # simplify and finish: - expr = simp.subs(res) - break # process below - - if expr.is_Add: - # The types of hyper functions we are looking for - # Scan for the terms we need - args = [] - for term in expr.args: - term = _trigsimp(term, deep) - for pattern, result in matchers_identity: - res = term.match(pattern) - if res is not None: - term = result.subs(res) - break - args.append(term) - if args != expr.args: - expr = Add(*args) - expr = min(expr, expand(expr), key=count_ops) - - # Reduce any lingering artifacts, such as sin(x)**2 changing - # to 1 - cos(x)**2 when sin(x)**2 was "simpler" - for pattern, result, ex in artifacts: - if expr.is_number: - break - # Substitute a new wild that excludes some function(s) - # to help influence a better match. This is because - # sometimes, for example, 'a' would match sec(x)**2 - a_t = Wild('a', exclude=[ex]) - pattern = pattern.subs(a, a_t) - result = result.subs(a, a_t) - - m = expr.match(pattern) - while m is not None: - if m[a_t] == 0 or -m[a_t] in m[c].args or m[a_t] + m[c] == 0: - break - if d in list(m.keys()) and m[a_t]*m[d] + m[c] == 0: - break - expr = result.subs(m) - m = expr.match(pattern) - - return expr - - elif expr.is_Mul or expr.is_Pow or deep and expr.args: - return expr.func(*[_trigsimp(a, deep) for a in expr.args]) - - return expr - - -
    [docs]def collect_sqrt(expr, evaluate=True): - """Return expr with terms having common square roots collected together. - If ``evaluate`` is False a count indicating the number of sqrt-containing - terms will be returned and the returned expression will be an unevaluated - Add with args ordered by default_sort_key. - - Note: since I = sqrt(-1), it is collected, too. - - Examples - ======== - - >>> from sympy import sqrt - >>> from sympy.simplify.simplify import collect_sqrt - >>> from sympy.abc import a, b - - >>> r2, r3, r5 = [sqrt(i) for i in [2, 3, 5]] - >>> collect_sqrt(a*r2 + b*r2) - sqrt(2)*(a + b) - >>> collect_sqrt(a*r2 + b*r2 + a*r3 + b*r3) - sqrt(2)*(a + b) + sqrt(3)*(a + b) - >>> collect_sqrt(a*r2 + b*r2 + a*r3 + b*r5) - sqrt(3)*a + sqrt(5)*b + sqrt(2)*(a + b) - - If evaluate is False then the arguments will be sorted and - returned as a list and a count of the number of sqrt-containing - terms will be returned: - - >>> collect_sqrt(a*r2 + b*r2 + a*r3 + b*r5, evaluate=False) - ((sqrt(2)*(a + b), sqrt(3)*a, sqrt(5)*b), 3) - >>> collect_sqrt(a*sqrt(2) + b, evaluate=False) - ((b, sqrt(2)*a), 1) - >>> collect_sqrt(a + b, evaluate=False) - ((a + b,), 0) - - """ - coeff, expr = expr.as_content_primitive() - vars = set() - for a in Add.make_args(expr): - for m in a.args_cnc()[0]: - if m.is_number and (m.is_Pow and m.exp.is_Rational and m.exp.q == 2 or - m is S.ImaginaryUnit): - vars.add(m) - vars = list(vars) - if not evaluate: - vars.sort(key=default_sort_key) - vars.reverse() # since it will be reversed below - vars.sort(key=count_ops) - vars.reverse() - d = collect_const(expr, *vars, **dict(first=False)) - hit = expr != d - d *= coeff - - if not evaluate: - nrad = 0 - args = list(Add.make_args(d)) - for m in args: - c, nc = m.args_cnc() - for ci in c: - if ci.is_Pow and ci.exp.is_Rational and ci.exp.q == 2 or \ - ci is S.ImaginaryUnit: - nrad += 1 - break - if hit or nrad: - args.sort(key=default_sort_key) - else: - args = [Add(*args)] - return tuple(args), nrad - - return d - -
    -
    [docs]def collect_const(expr, *vars, **first): - """A non-greedy collection of terms with similar number coefficients in - an Add expr. If ``vars`` is given then only those constants will be - targeted. - - Examples - ======== - - >>> from sympy import sqrt - >>> from sympy.abc import a, s - >>> from sympy.simplify.simplify import collect_const - >>> collect_const(sqrt(3) + sqrt(3)*(1 + sqrt(2))) - sqrt(3)*(sqrt(2) + 2) - >>> collect_const(sqrt(3)*s + sqrt(7)*s + sqrt(3) + sqrt(7)) - (sqrt(3) + sqrt(7))*(s + 1) - >>> s = sqrt(2) + 2 - >>> collect_const(sqrt(3)*s + sqrt(3) + sqrt(7)*s + sqrt(7)) - (sqrt(2) + 3)*(sqrt(3) + sqrt(7)) - >>> collect_const(sqrt(3)*s + sqrt(3) + sqrt(7)*s + sqrt(7), sqrt(3)) - sqrt(7) + sqrt(3)*(sqrt(2) + 3) + sqrt(7)*(sqrt(2) + 2) - - If no constants are provided then a leading Rational might be returned: - - >>> collect_const(2*sqrt(3) + 4*a*sqrt(5)) - 2*(2*sqrt(5)*a + sqrt(3)) - >>> collect_const(2*sqrt(3) + 4*a*sqrt(5), sqrt(3)) - 4*sqrt(5)*a + 2*sqrt(3) - """ - - if first.get('first', True): - c, p = sympify(expr).as_content_primitive() - else: - c, p = S.One, expr - if c is not S.One: - if not vars: - return _keep_coeff(c, collect_const(p, *vars, **dict(first=False))) - # else don't leave the Rational on the outside - return c*collect_const(p, *vars, **dict(first=False)) - - if not (expr.is_Add or expr.is_Mul): - return expr - recurse = False - if not vars: - recurse = True - vars = set() - for a in Add.make_args(expr): - for m in Mul.make_args(a): - if m.is_number: - vars.add(m) - vars = sorted(vars, key=count_ops) - # Rationals get autodistributed on Add so don't bother with them - vars = [v for v in vars if not v.is_Rational] - - if not vars: - return expr - - for v in vars: - terms = defaultdict(list) - for m in Add.make_args(expr): - i = [] - d = [] - for a in Mul.make_args(m): - if a == v: - d.append(a) - else: - i.append(a) - ai, ad = [Mul(*w) for w in [i, d]] - terms[ad].append(ai) - args = [] - hit = False - for k, v in terms.items(): - if len(v) > 1: - v = Add(*v) - hit = True - if recurse and v != expr: - vars.append(v) - else: - v = v[0] - args.append(k*v) - if hit: - expr = Add(*args) - if not expr.is_Add: - break - return expr - -
    -def _split_gcd(*a): - """ - split the list of integers `a` into a list of integers a1 having - g = gcd(a1) and a list a2 whose elements are not divisible by g - Returns g, a1, a2 - - Examples - ======== - >>> from sympy.simplify.simplify import _split_gcd - >>> _split_gcd(55,35,22,14,77,10) - (5, [55, 35, 10], [22, 14, 77]) - """ - g = a[0] - b1 = [g] - b2 = [] - for x in a[1:]: - g1 = gcd(g, x) - if g1 == 1: - b2.append(x) - else: - g = g1 - b1.append(x) - return g, b1, b2 - - -def split_surds(expr): - """ - split an expression with terms whose squares are rationals - into a sum of terms whose surds squared have gcd equal to g - and a sum of terms with surds squared prime with g - - Examples - ======== - >>> from sympy import sqrt - >>> from sympy.simplify.simplify import split_surds - >>> split_surds(3*sqrt(3) + sqrt(5)/7 + sqrt(6) + sqrt(10) + sqrt(15)) - (3, sqrt(2) + sqrt(5) + 3, sqrt(5)/7 + sqrt(10)) - """ - args = sorted(expr.args, key=default_sort_key) - coeff_muls = [x.as_coeff_Mul() for x in args] - surds = [x[1]**2 for x in coeff_muls if x[1].is_Pow] - surds.sort(key=default_sort_key) - g, b1, b2 = _split_gcd(*surds) - g2 = g - if not b2 and len(b1) >= 2: - b1n = [x/g for x in b1] - b1n = [x for x in b1n if x != 1] - # only a common factor has been factored; split again - g1, b1n, b2 = _split_gcd(*b1n) - g2 = g*g1 - a1v, a2v = [], [] - for c, s in coeff_muls: - if s.is_Pow and s.exp == S.Half: - s1 = s.base - if s1 in b1: - a1v.append(c*sqrt(s1/g2)) - else: - a2v.append(c*s) - else: - a2v.append(c*s) - a = Add(*a1v) - b = Add(*a2v) - return g2, a, b - - -def rad_rationalize(num, den): - """ - Rationalize num/den by removing square roots in the denominator; - num and den are sum of terms whose squares are rationals - - Examples - ======== - >>> from sympy import sqrt - >>> from sympy.simplify.simplify import rad_rationalize - >>> rad_rationalize(sqrt(3), 1 + sqrt(2)/3) - (-sqrt(3) + sqrt(6)/3, -7/9) - """ - if not den.is_Add: - return num, den - g, a, b = split_surds(den) - a = a*sqrt(g) - num = _mexpand((a - b)*num) - den = _mexpand(a**2 - b**2) - return rad_rationalize(num, den) - - -
    [docs]def radsimp(expr, symbolic=True, max_terms=4): - """ - Rationalize the denominator by removing square roots. - - Note: the expression returned from radsimp must be used with caution - since if the denominator contains symbols, it will be possible to make - substitutions that violate the assumptions of the simplification process: - that for a denominator matching a + b*sqrt(c), a != +/-b*sqrt(c). (If - there are no symbols, this assumptions is made valid by collecting terms - of sqrt(c) so the match variable ``a`` does not contain ``sqrt(c)``.) If - you do not want the simplification to occur for symbolic denominators, set - ``symbolic`` to False. - - If there are more than ``max_terms`` radical terms do not simplify. - - Examples - ======== - - >>> from sympy import radsimp, sqrt, Symbol, denom, pprint, I - >>> from sympy.abc import a, b, c - - >>> radsimp(1/(I + 1)) - (1 - I)/2 - >>> radsimp(1/(2 + sqrt(2))) - (-sqrt(2) + 2)/2 - >>> x,y = list(map(Symbol, 'xy')) - >>> e = ((2 + 2*sqrt(2))*x + (2 + sqrt(8))*y)/(2 + sqrt(2)) - >>> radsimp(e) - sqrt(2)*(x + y) - - Terms are collected automatically: - - >>> r2 = sqrt(2) - >>> r5 = sqrt(5) - >>> pprint(radsimp(1/(y*r2 + x*r2 + a*r5 + b*r5))) - ___ ___ - \/ 5 *(-a - b) + \/ 2 *(x + y) - -------------------------------------------- - 2 2 2 2 - - 5*a - 10*a*b - 5*b + 2*x + 4*x*y + 2*y - - If radicals in the denominator cannot be removed, the original expression - will be returned. If the denominator was 1 then any square roots will also - be collected: - - >>> radsimp(sqrt(2)*x + sqrt(2)) - sqrt(2)*(x + 1) - - Results with symbols will not always be valid for all substitutions: - - >>> eq = 1/(a + b*sqrt(c)) - >>> eq.subs(a, b*sqrt(c)) - 1/(2*b*sqrt(c)) - >>> radsimp(eq).subs(a, b*sqrt(c)) - nan - - If symbolic=False, symbolic denominators will not be transformed (but - numeric denominators will still be processed): - - >>> radsimp(eq, symbolic=False) - 1/(a + b*sqrt(c)) - """ - - def handle(expr): - if expr.is_Atom or not symbolic and expr.free_symbols: - return expr - n, d = fraction(expr) - if d is S.One: - nexpr = expr.func(*[handle(ai) for ai in expr.args]) - return nexpr - elif d.is_Mul: - nargs = [] - dargs = [] - for di in d.args: - ni, di = fraction(handle(1/di)) - nargs.append(ni) - dargs.append(di) - return n*Mul(*nargs)/Mul(*dargs) - elif d.is_Add: - d = radsimp(d) - elif d.is_Pow and d.exp.is_Rational and d.exp.q == 2: - d = sqrtdenest(sqrt(d.base))**d.exp.p - - changed = False - while 1: - # collect similar terms - d, nterms = collect_sqrt(_mexpand(d), evaluate=False) - d = Add._from_args(d) - if nterms > max_terms: - break - - # check to see if we are done: - # - no radical terms - # - if there are more than 3 radical terms, or - # there 3 radical terms and a constant, use rad_rationalize - if not nterms: - break - if nterms > 3 or nterms == 3 and len(d.args) > 4: - if all([(x**2).is_Integer for x in d.args]): - nd, d = rad_rationalize(S.One, d) - n = _mexpand(n*nd) - else: - n, d = fraction(expr) - break - changed = True - - # now match for a radical - if d.is_Add and len(d.args) == 4: - r = d.match(a + b*sqrt(c) + D*sqrt(E)) - nmul = (a - b*sqrt(c) - D*sqrt(E)).xreplace(r) - d = (a**2 - c*b**2 - E*D**2 - 2*b*D*sqrt(c*E)).xreplace(r) - n1 = n/d - if denom(n1) is not S.One: - n = -(-n/d) - else: - n = n1 - n, d = fraction(n*nmul) - - else: - r = d.match(a + b*sqrt(c)) - if not r or r[b] == 0: - r = d.match(b*sqrt(c)) - if r is None: - break - r[a] = S.Zero - va, vb, vc = r[a], r[b], r[c] - - dold = d - d = va**2 - vc*vb**2 - nmul = va - vb*sqrt(vc) - n1 = n/d - if denom(n1) is not S.One: - n1 = -(-n/d) - n1, d1 = fraction(n1*nmul) - if d1 != dold: - n, d = n1, d1 - else: - n *= nmul - - nexpr = collect_sqrt(expand_mul(n))/d - if changed or nexpr != expr: - expr = nexpr - return expr - - a, b, c, D, E, F, G = list(map(Wild, 'abcDEFG')) - # do this at the start in case no other change is made since - # it is done if a change is made - coeff, expr = expr.as_content_primitive() - - newe = handle(expr) - if newe != expr: - co, expr = newe.as_content_primitive() - coeff *= co - else: - nexpr, hit = collect_sqrt(expand_mul(expr), evaluate=False) - nexpr = Add._from_args(nexpr) - if hit and expr.count_ops() >= nexpr.count_ops(): - expr = Add(*Add.make_args(nexpr)) - return _keep_coeff(coeff, expr) - -
    -
    [docs]def posify(eq): - """Return eq (with generic symbols made positive) and a restore - dictionary. - - Any symbol that has positive=None will be replaced with a positive dummy - symbol having the same name. This replacement will allow more symbolic - processing of expressions, especially those involving powers and - logarithms. - - A dictionary that can be sent to subs to restore eq to its original - symbols is also returned. - - >>> from sympy import posify, Symbol, log - >>> from sympy.abc import x - >>> posify(x + Symbol('p', positive=True) + Symbol('n', negative=True)) - (_x + n + p, {_x: x}) - - >> log(1/x).expand() # should be log(1/x) but it comes back as -log(x) - log(1/x) - - >>> log(posify(1/x)[0]).expand() # take [0] and ignore replacements - -log(_x) - >>> eq, rep = posify(1/x) - >>> log(eq).expand().subs(rep) - -log(x) - >>> posify([x, 1 + x]) - ([_x, _x + 1], {_x: x}) - """ - eq = sympify(eq) - if iterable(eq): - f = type(eq) - eq = list(eq) - syms = set() - for e in eq: - syms = syms.union(e.atoms(C.Symbol)) - reps = {} - for s in syms: - reps.update(dict((v, k) for k, v in list(posify(s)[1].items()))) - for i, e in enumerate(eq): - eq[i] = e.subs(reps) - return f(eq), dict([(r, s) for s, r in reps.items()]) - - reps = dict([(s, Dummy(s.name, positive=True)) - for s in eq.atoms(Symbol) if s.is_positive is None]) - eq = eq.subs(reps) - return eq, dict([(r, s) for s, r in reps.items()]) - -
    -def _polarify(eq, lift, pause=False): - from sympy import polar_lift, Integral - if eq.is_polar: - return eq - if eq.is_number and not pause: - return polar_lift(eq) - if isinstance(eq, Symbol) and not pause and lift: - return polar_lift(eq) - elif eq.is_Atom: - return eq - elif eq.is_Add: - r = eq.func(*[_polarify(arg, lift, pause=True) for arg in eq.args]) - if lift: - return polar_lift(r) - return r - elif eq.is_Function: - return eq.func(*[_polarify(arg, lift, pause=False) for arg in eq.args]) - elif isinstance(eq, Integral): - # Don't lift the integration variable - func = _polarify(eq.function, lift, pause=pause) - limits = [] - for limit in eq.args[1:]: - var = _polarify(limit[0], lift=False, pause=pause) - rest = _polarify(limit[1:], lift=lift, pause=pause) - limits.append((var,) + rest) - return Integral(*((func,) + tuple(limits))) - else: - return eq.func(*[_polarify(arg, lift, pause=pause) for arg in eq.args]) - - -def polarify(eq, subs=True, lift=False): - """ - Turn all numbers in eq into their polar equivalents (under the standard - choice of argument). - - Note that no attempt is made to guess a formal convention of adding - polar numbers, expressions like 1 + x will generally not be altered. - - Note also that this function does not promote exp(x) to exp_polar(x). - - If ``subs`` is True, all symbols which are not already polar will be - substituted for polar dummies; in this case the function behaves much - like posify. - - If ``lift`` is True, both addition statements and non-polar symbols are - changed to their polar_lift()ed versions. - Note that lift=True implies subs=False. - - >>> from sympy import polarify, sin, I - >>> from sympy.abc import x, y - >>> expr = (-x)**y - >>> expr.expand() - (-x)**y - >>> polarify(expr) - ((_x*exp_polar(I*pi))**_y, {_x: x, _y: y}) - >>> polarify(expr)[0].expand() - _x**_y*exp_polar(_y*I*pi) - >>> polarify(x, lift=True) - polar_lift(x) - >>> polarify(x*(1+y), lift=True) - polar_lift(x)*polar_lift(y + 1) - - Adds are treated carefully: - - >>> polarify(1 + sin((1 + I)*x)) - (sin(_x*polar_lift(1 + I)) + 1, {_x: x}) - """ - if lift: - subs = False - eq = _polarify(sympify(eq), lift) - if not subs: - return eq - reps = dict([(s, Dummy(s.name, polar=True)) for s in eq.atoms(Symbol)]) - eq = eq.subs(reps) - return eq, dict([(r, s) for s, r in reps.items()]) - - -def _unpolarify(eq, exponents_only, pause=False): - from sympy import polar_lift, exp, principal_branch, pi - - if isinstance(eq, bool) or eq.is_Atom: - return eq - - if not pause: - if eq.func is exp_polar: - return exp(_unpolarify(eq.exp, exponents_only)) - if eq.func is principal_branch and eq.args[1] == 2*pi: - return _unpolarify(eq.args[0], exponents_only) - if ( - eq.is_Add or eq.is_Mul or eq.is_Boolean or - eq.is_Relational and ( - eq.rel_op in ('==', '!=') and 0 in eq.args or - eq.rel_op not in ('==', '!=')) - ): - return eq.func(*[_unpolarify(x, exponents_only) for x in eq.args]) - if eq.func is polar_lift: - return _unpolarify(eq.args[0], exponents_only) - - if eq.is_Pow: - expo = _unpolarify(eq.exp, exponents_only) - base = _unpolarify(eq.base, exponents_only, - not (expo.is_integer and not pause)) - return base**expo - - if eq.is_Function and getattr(eq.func, 'unbranched', False): - return eq.func(*[_unpolarify(x, exponents_only, exponents_only) - for x in eq.args]) - - return eq.func(*[_unpolarify(x, exponents_only, True) for x in eq.args]) - - -def unpolarify(eq, subs={}, exponents_only=False): - """ - If p denotes the projection from the Riemann surface of the logarithm to - the complex line, return a simplified version eq' of `eq` such that - p(eq') == p(eq). - Also apply the substitution subs in the end. (This is a convenience, since - ``unpolarify``, in a certain sense, undoes polarify.) - - >>> from sympy import unpolarify, polar_lift, sin, I - >>> unpolarify(polar_lift(I + 2)) - 2 + I - >>> unpolarify(sin(polar_lift(I + 7))) - sin(7 + I) - """ - from sympy import exp_polar, polar_lift - if isinstance(eq, bool): - return eq - - eq = sympify(eq) - if subs != {}: - return unpolarify(eq.subs(subs)) - changed = True - pause = False - if exponents_only: - pause = True - while changed: - changed = False - res = _unpolarify(eq, exponents_only, pause) - if res != eq: - changed = True - eq = res - if isinstance(res, bool): - return res - # Finally, replacing Exp(0) by 1 is always correct. - # So is polar_lift(0) -> 0. - return res.subs({exp_polar(0): 1, polar_lift(0): 0}) - - -def _denest_pow(eq): - """ - Denest powers. - - This is a helper function for powdenest that performs the actual - transformation. - """ - b, e = eq.as_base_exp() - - # denest exp with log terms in exponent - if b is S.Exp1 and e.is_Mul: - logs = [] - other = [] - for ei in e.args: - if any(ai.func is C.log for ai in Add.make_args(ei)): - logs.append(ei) - else: - other.append(ei) - logs = logcombine(Mul(*logs)) - return Pow(exp(logs), Mul(*other)) - - _, be = b.as_base_exp() - if be is S.One and not (b.is_Mul or - b.is_Rational and b.q != 1 or - b.is_positive): - return eq - - # denest eq which is either pos**e or Pow**e or Mul**e or Mul(b1**e1, b2**e2) - - # handle polar numbers specially - polars, nonpolars = [], [] - for bb in Mul.make_args(b): - if bb.is_polar: - polars.append(bb.as_base_exp()) - else: - nonpolars.append(bb) - if len(polars) == 1 and not polars[0][0].is_Mul: - return Pow(polars[0][0], polars[0][1]*e)*powdenest(Mul(*nonpolars)**e) - elif polars: - return Mul(*[powdenest(bb**(ee*e)) for (bb, ee) in polars]) \ - *powdenest(Mul(*nonpolars)**e) - - # see if there is a positive, non-Mul base at the very bottom - exponents = [] - kernel = eq - while kernel.is_Pow: - kernel, ex = kernel.as_base_exp() - exponents.append(ex) - if kernel.is_positive: - e = Mul(*exponents) - if kernel.is_Mul: - b = kernel - else: - if kernel.is_Integer: - # use log to see if there is a power here - logkernel = log(kernel) - if logkernel.is_Mul: - c, logk = logkernel.args - e *= c - kernel = logk.args[0] - return Pow(kernel, e) - - # if any factor is an atom then there is nothing to be done - # but the kernel check may have created a new exponent - if any(s.is_Atom for s in Mul.make_args(b)): - if exponents: - return b**e - return eq - - # let log handle the case of the base of the argument being a mul, e.g. - # sqrt(x**(2*i)*y**(6*i)) -> x**i*y**(3**i) if x and y are positive; we - # will take the log, expand it, and then factor out the common powers that - # now appear as coefficient. We do this manually since terms_gcd pulls out - # fractions, terms_gcd(x+x*y/2) -> x*(y + 2)/2 and we don't want the 1/2; - # gcd won't pull out numerators from a fraction: gcd(3*x, 9*x/2) -> x but - # we want 3*x. Neither work with noncommutatives. - def nc_gcd(aa, bb): - a, b = [i.as_coeff_Mul() for i in [aa, bb]] - c = gcd(a[0], b[0]).as_numer_denom()[0] - g = Mul(*(a[1].args_cnc(cset=True)[0] & b[1].args_cnc(cset=True)[0])) - return _keep_coeff(c, g) - - glogb = expand_log(log(b)) - if glogb.is_Add: - args = glogb.args - g = reduce(nc_gcd, args) - if g != 1: - cg, rg = g.as_coeff_Mul() - glogb = _keep_coeff(cg, rg*Add(*[a/g for a in args])) - - # now put the log back together again - if glogb.func is C.log or not glogb.is_Mul: - if glogb.args[0].is_Pow or glogb.args[0].func is exp: - glogb = _denest_pow(glogb.args[0]) - if (abs(glogb.exp) < 1) is True: - return Pow(glogb.base, glogb.exp*e) - return eq - - # the log(b) was a Mul so join any adds with logcombine - add = [] - other = [] - for a in glogb.args: - if a.is_Add: - add.append(a) - else: - other.append(a) - return Pow(exp(logcombine(Mul(*add))), e*Mul(*other)) - - -
    [docs]def powdenest(eq, force=False, polar=False): - r""" - Collect exponents on powers as assumptions allow. - - Given ``(bb**be)**e``, this can be simplified as follows: - * if ``bb`` is positive, or - * ``e`` is an integer, or - * ``|be| < 1`` then this simplifies to ``bb**(be*e)`` - - Given a product of powers raised to a power, ``(bb1**be1 * - bb2**be2...)**e``, simplification can be done as follows: - - - if e is positive, the gcd of all bei can be joined with e; - - all non-negative bb can be separated from those that are negative - and their gcd can be joined with e; autosimplification already - handles this separation. - - integer factors from powers that have integers in the denominator - of the exponent can be removed from any term and the gcd of such - integers can be joined with e - - Setting ``force`` to True will make symbols that are not explicitly - negative behave as though they are positive, resulting in more - denesting. - - Setting ``polar`` to True will do simplifications on the riemann surface of - the logarithm, also resulting in more denestings. - - When there are sums of logs in exp() then a product of powers may be - obtained e.g. ``exp(3*(log(a) + 2*log(b)))`` - > ``a**3*b**6``. - - Examples - ======== - - >>> from sympy.abc import a, b, x, y, z - >>> from sympy import Symbol, exp, log, sqrt, symbols, powdenest - - >>> powdenest((x**(2*a/3))**(3*x)) - (x**(2*a/3))**(3*x) - >>> powdenest(exp(3*x*log(2))) - 2**(3*x) - - Assumptions may prevent expansion: - - >>> powdenest(sqrt(x**2)) - sqrt(x**2) - - >>> p = symbols('p', positive=True) - >>> powdenest(sqrt(p**2)) - p - - No other expansion is done. - - >>> i, j = symbols('i,j', integer=True) - >>> powdenest((x**x)**(i + j)) # -X-> (x**x)**i*(x**x)**j - x**(x*(i + j)) - - But exp() will be denested by moving all non-log terms outside of - the function; this may result in the collapsing of the exp to a power - with a different base: - - >>> powdenest(exp(3*y*log(x))) - x**(3*y) - >>> powdenest(exp(y*(log(a) + log(b)))) - (a*b)**y - >>> powdenest(exp(3*(log(a) + log(b)))) - a**3*b**3 - - If assumptions allow, symbols can also be moved to the outermost exponent: - - >>> i = Symbol('i', integer=True) - >>> p = Symbol('p', positive=True) - >>> powdenest(((x**(2*i))**(3*y))**x) - ((x**(2*i))**(3*y))**x - >>> powdenest(((x**(2*i))**(3*y))**x, force=True) - x**(6*i*x*y) - - >>> powdenest(((p**(2*a))**(3*y))**x) - p**(6*a*x*y) - - >>> powdenest(((x**(2*a/3))**(3*y/i))**x) - ((x**(2*a/3))**(3*y/i))**x - >>> powdenest((x**(2*i)*y**(4*i))**z, force=True) - (x*y**2)**(2*i*z) - - >>> n = Symbol('n', negative=True) - - >>> powdenest((x**i)**y, force=True) - x**(i*y) - >>> powdenest((n**i)**x, force=True) - (n**i)**x - - """ - - if force: - eq, rep = posify(eq) - return powdenest(eq, force=False).xreplace(rep) - - if polar: - eq, rep = polarify(eq) - return unpolarify(powdenest(unpolarify(eq, exponents_only=True)), rep) - - new = powsimp(sympify(eq)) - return new.xreplace(Transform(_denest_pow, filter=lambda m: m.is_Pow or m.func is exp)) -
    -_y = Dummy('y') - - -
    [docs]def powsimp(expr, deep=False, combine='all', force=False, measure=count_ops): - """ - reduces expression by combining powers with similar bases and exponents. - - Notes - ===== - - If deep is True then powsimp() will also simplify arguments of - functions. By default deep is set to False. - - If force is True then bases will be combined without checking for - assumptions, e.g. sqrt(x)*sqrt(y) -> sqrt(x*y) which is not true - if x and y are both negative. - - You can make powsimp() only combine bases or only combine exponents by - changing combine='base' or combine='exp'. By default, combine='all', - which does both. combine='base' will only combine:: - - a a a 2x x - x * y => (x*y) as well as things like 2 => 4 - - and combine='exp' will only combine - :: - - a b (a + b) - x * x => x - - combine='exp' will strictly only combine exponents in the way that used - to be automatic. Also use deep=True if you need the old behavior. - - When combine='all', 'exp' is evaluated first. Consider the first - example below for when there could be an ambiguity relating to this. - This is done so things like the second example can be completely - combined. If you want 'base' combined first, do something like - powsimp(powsimp(expr, combine='base'), combine='exp'). - - Examples - ======== - - >>> from sympy import powsimp, exp, log, symbols - >>> from sympy.abc import x, y, z, n - >>> powsimp(x**y*x**z*y**z, combine='all') - x**(y + z)*y**z - >>> powsimp(x**y*x**z*y**z, combine='exp') - x**(y + z)*y**z - >>> powsimp(x**y*x**z*y**z, combine='base', force=True) - x**y*(x*y)**z - - >>> powsimp(x**z*x**y*n**z*n**y, combine='all', force=True) - (n*x)**(y + z) - >>> powsimp(x**z*x**y*n**z*n**y, combine='exp') - n**(y + z)*x**(y + z) - >>> powsimp(x**z*x**y*n**z*n**y, combine='base', force=True) - (n*x)**y*(n*x)**z - - >>> x, y = symbols('x y', positive=True) - >>> powsimp(log(exp(x)*exp(y))) - log(exp(x)*exp(y)) - >>> powsimp(log(exp(x)*exp(y)), deep=True) - x + y - - Radicals with Mul bases will be combined if combine='exp' - - >>> from sympy import sqrt, Mul - >>> x, y = symbols('x y') - - Two radicals are automatically joined through Mul: - >>> a=sqrt(x*sqrt(y)) - >>> a*a**3 == a**4 - True - - But if an integer power of that radical has been - autoexpanded then Mul does not join the resulting factors: - >>> a**4 # auto expands to a Mul, no longer a Pow - x**2*y - >>> _*a # so Mul doesn't combine them - x**2*y*sqrt(x*sqrt(y)) - >>> powsimp(_) # but powsimp will - (x*sqrt(y))**(5/2) - >>> powsimp(x*y*a) # but won't when doing so would violate assumptions - x*y*sqrt(x*sqrt(y)) - - """ - - def recurse(arg, **kwargs): - _deep = kwargs.get('deep', deep) - _combine = kwargs.get('combine', combine) - _force = kwargs.get('force', force) - _measure = kwargs.get('measure', measure) - return powsimp(arg, _deep, _combine, _force, _measure) - - expr = sympify(expr) - - if not isinstance(expr, Basic) or expr.is_Atom or expr in (exp_polar(0), exp_polar(1)): - return expr - - if deep or expr.is_Add or expr.is_Mul and _y not in expr.args: - expr = expr.func(*[recurse(w) for w in expr.args]) - - if expr.is_Pow: - return recurse(expr*_y, deep=False)/_y - - if not expr.is_Mul: - return expr - - # handle the Mul - - if combine in ('exp', 'all'): - # Collect base/exp data, while maintaining order in the - # non-commutative parts of the product - c_powers = defaultdict(list) - nc_part = [] - newexpr = [] - for term in expr.args: - if term.is_commutative: - b, e = term.as_base_exp() - if deep: - b, e = [recurse(i) for i in [b, e]] - c_powers[b].append(e) - else: - # This is the logic that combines exponents for equal, - # but non-commutative bases: A**x*A**y == A**(x+y). - if nc_part: - b1, e1 = nc_part[-1].as_base_exp() - b2, e2 = term.as_base_exp() - if (b1 == b2 and - e1.is_commutative and e2.is_commutative): - nc_part[-1] = Pow(b1, Add(e1, e2)) - continue - nc_part.append(term) - - # add up exponents of common bases - for b, e in c_powers.items(): - c_powers[b] = Add(*e) - - # check for base and inverted base pairs - be = list(c_powers.items()) - skip = set() # skip if we already saw them - for b, e in be: - if b in skip: - continue - bpos = b.is_positive or b.is_polar - if bpos: - binv = 1/b - if b != binv and binv in c_powers: - if b.as_numer_denom()[0] is S.One: - c_powers.pop(b) - c_powers[binv] -= e - else: - skip.add(binv) - e = c_powers.pop(binv) - c_powers[b] -= e - - # filter c_powers and convert to a list - c_powers = [(b, e) for b, e in c_powers.items() if e] - - # ============================================================== - # check for Mul bases of Rational powers that can be combined with - # separated bases, e.g. x*sqrt(x*y)*sqrt(x*sqrt(x*y)) -> (x*sqrt(x*y))**(3/2) - # ---------------- helper functions - def ratq(x): - '''Return Rational part of x's exponent as it appears in the bkey. - ''' - return bkey(x)[0][1] - - def bkey(b, e=None): - '''Return (b**s, c.q), c.p where e -> c*s. If e is not given then - it will be taken by using as_base_exp() on the input b. - e.g. - x**3/2 -> (x, 2), 3 - x**y -> (x**y, 1), 1 - x**(2*y/3) -> (x**y, 3), 2 - exp(x/2) -> (exp(a), 2), 1 - - ''' - if e is not None: # coming from c_powers or from below - if e.is_Integer: - return (b, S.One), e - elif e.is_Rational: - return (b, Integer(e.q)), Integer(e.p) - else: - c, m = e.as_coeff_Mul(rational=True) - if c is not S.One: - return (b**m, Integer(c.q)), Integer(c.p) - else: - return (b**e, S.One), S.One - else: - return bkey(*b.as_base_exp()) - - def update(b): - '''Decide what to do with base, b. If its exponent is now an - integer multiple of the Rational denominator, then remove it - and put the factors of its base in the common_b dictionary or - update the existing bases if necessary. If it has been zeroed - out, simply remove the base. - ''' - newe, r = divmod(common_b[b], b[1]) - if not r: - common_b.pop(b) - if newe: - for m in Mul.make_args(b[0]**newe): - b, e = bkey(m) - if b not in common_b: - common_b[b] = 0 - common_b[b] += e - if b[1] != 1: - bases.append(b) - # ---------------- end of helper functions - - # assemble a dictionary of the factors having a Rational power - common_b = {} - done = [] - bases = [] - for b, e in c_powers: - b, e = bkey(b, e) - common_b[b] = e - if b[1] != 1 and b[0].is_Mul: - bases.append(b) - bases.sort(key=default_sort_key) # this makes tie-breaking canonical - bases.sort(key=measure, reverse=True) # handle longest first - for base in bases: - if base not in common_b: # it may have been removed already - continue - b, exponent = base - last = False # True when no factor of base is a radical - qlcm = 1 # the lcm of the radical denominators - while True: - bstart = b - qstart = qlcm - - bb = [] # list of factors - ee = [] # (factor's exponent, current value of that exponent in common_b) - for bi in Mul.make_args(b): - bib, bie = bkey(bi) - if bib not in common_b or common_b[bib] < bie: - ee = bb = [] # failed - break - ee.append([bie, common_b[bib]]) - bb.append(bib) - if ee: - # find the number of extractions possible - # e.g. [(1, 2), (2, 2)] -> min(2/1, 2/2) -> 1 - min1 = ee[0][1]/ee[0][0] - for i in range(len(ee)): - rat = ee[i][1]/ee[i][0] - if rat < 1: - break - min1 = min(min1, rat) - else: - # update base factor counts - # e.g. if ee = [(2, 5), (3, 6)] then min1 = 2 - # and the new base counts will be 5-2*2 and 6-2*3 - for i in range(len(bb)): - common_b[bb[i]] -= min1*ee[i][0] - update(bb[i]) - # update the count of the base - # e.g. x**2*y*sqrt(x*sqrt(y)) the count of x*sqrt(y) - # will increase by 4 to give bkey (x*sqrt(y), 2, 5) - common_b[base] += min1*qstart*exponent - if (last # no more radicals in base - or len(common_b) == 1 # nothing left to join with - or all(k[1] == 1 for k in common_b) # no radicals left in common_b - ): - break - # see what we can exponentiate base by to remove any radicals - # so we know what to search for - # e.g. if base were x**(1/2)*y**(1/3) then we should exponentiate - # by 6 and look for powers of x and y in the ratio of 2 to 3 - qlcm = lcm([ratq(bi) for bi in Mul.make_args(bstart)]) - if qlcm == 1: - break # we are done - b = bstart**qlcm - qlcm *= qstart - if all(ratq(bi) == 1 for bi in Mul.make_args(b)): - last = True # we are going to be done after this next pass - # this base no longer can find anything to join with and - # since it was longer than any other we are done with it - b, q = base - done.append((b, common_b.pop(base)*Rational(1, q))) - - # update c_powers and get ready to continue with powsimp - c_powers = done - # there may be terms still in common_b that were bases that were - # identified as needing processing, so remove those, too - for (b, q), e in list(common_b.items()): - if (b.is_Pow or b.func is exp) and \ - q is not S.One and not b.exp.is_Rational: - b, be = b.as_base_exp() - b = b**(be/q) - else: - b = root(b, q) - c_powers.append((b, e)) - check = len(c_powers) - c_powers = dict(c_powers) - assert len(c_powers) == check # there should have been no duplicates - # ============================================================== - - # rebuild the expression - newexpr = Mul( - *(newexpr + [Pow(b, e) for b, e in c_powers.items()])) - if combine == 'exp': - return Mul(newexpr, Mul(*nc_part)) - else: - return recurse(Mul(*nc_part), combine='base') * \ - recurse(newexpr, combine='base') - - elif combine == 'base': - - # Build c_powers and nc_part. These must both be lists not - # dicts because exp's are not combined. - c_powers = [] - nc_part = [] - for term in expr.args: - if term.is_commutative: - c_powers.append(list(term.as_base_exp())) - else: - # This is the logic that combines bases that are - # different and non-commutative, but with equal and - # commutative exponents: A**x*B**x == (A*B)**x. - if nc_part: - b1, e1 = nc_part[-1].as_base_exp() - b2, e2 = term.as_base_exp() - if (e1 == e2 and e2.is_commutative): - nc_part[-1] = Pow(Mul(b1, b2), e1) - continue - nc_part.append(term) - - # Pull out numerical coefficients from exponent if assumptions allow - # e.g., 2**(2*x) => 4**x - for i in range(len(c_powers)): - b, e = c_powers[i] - if not (b.is_nonnegative or e.is_integer or force or b.is_polar): - continue - exp_c, exp_t = e.as_coeff_Mul(rational=True) - if exp_c is not S.One and exp_t is not S.One: - c_powers[i] = [Pow(b, exp_c), exp_t] - - # Combine bases whenever they have the same exponent and - # assumptions allow - # first gather the potential bases under the common exponent - c_exp = defaultdict(list) - for b, e in c_powers: - if deep: - e = recurse(e) - c_exp[e].append(b) - del c_powers - - # Merge back in the results of the above to form a new product - c_powers = defaultdict(list) - for e in c_exp: - bases = c_exp[e] - - # calculate the new base for e - - if len(bases) == 1: - new_base = bases[0] - elif e.is_integer or force: - new_base = Mul(*bases) - else: - # see which ones can be joined - unk = [] - nonneg = [] - neg = [] - for bi in bases: - if bi.is_negative: - neg.append(bi) - elif bi.is_nonnegative: - nonneg.append(bi) - elif bi.is_polar: - nonneg.append( - bi) # polar can be treated like non-negative - else: - unk.append(bi) - if len(unk) == 1 and not neg or len(neg) == 1 and not unk: - # a single neg or a single unk can join the rest - nonneg.extend(unk + neg) - unk = neg = [] - elif neg: - # their negative signs cancel in groups of 2*q if we know - # that e = p/q else we have to treat them as unknown - israt = False - if e.is_Rational: - israt = True - else: - p, d = e.as_numer_denom() - if p.is_integer and d.is_integer: - israt = True - if israt: - neg = [-w for w in neg] - unk.extend([S.NegativeOne]*len(neg)) - else: - unk.extend(neg) - neg = [] - del israt - - # these shouldn't be joined - for b in unk: - c_powers[b].append(e) - # here is a new joined base - new_base = Mul(*(nonneg + neg)) - # if there are positive parts they will just get separated again - # unless some change is made - - def _terms(e): - # return the number of terms of this expression - # when multiplied out -- assuming no joining of terms - if e.is_Add: - return sum([_terms(ai) for ai in e.args]) - if e.is_Mul: - return prod([_terms(mi) for mi in e.args]) - return 1 - xnew_base = expand_mul(new_base, deep=False) - if len(Add.make_args(xnew_base)) < _terms(new_base): - new_base = factor_terms(xnew_base) - - c_powers[new_base].append(e) - - # break out the powers from c_powers now - c_part = [Pow(b, ei) for b, e in c_powers.items() for ei in e] - - # we're done - return Mul(*(c_part + nc_part)) - - else: - raise ValueError("combine must be one of ('all', 'exp', 'base').") - -
    -
    [docs]def hypersimp(f, k): - """Given combinatorial term f(k) simplify its consecutive term ratio - i.e. f(k+1)/f(k). The input term can be composed of functions and - integer sequences which have equivalent representation in terms - of gamma special function. - - The algorithm performs three basic steps: - - 1. Rewrite all functions in terms of gamma, if possible. - - 2. Rewrite all occurrences of gamma in terms of products - of gamma and rising factorial with integer, absolute - constant exponent. - - 3. Perform simplification of nested fractions, powers - and if the resulting expression is a quotient of - polynomials, reduce their total degree. - - If f(k) is hypergeometric then as result we arrive with a - quotient of polynomials of minimal degree. Otherwise None - is returned. - - For more information on the implemented algorithm refer to: - - 1. W. Koepf, Algorithms for m-fold Hypergeometric Summation, - Journal of Symbolic Computation (1995) 20, 399-417 - """ - f = sympify(f) - - g = f.subs(k, k + 1) / f - - g = g.rewrite(gamma) - g = expand_func(g) - g = powsimp(g, deep=True, combine='exp') - - if g.is_rational_function(k): - return simplify(g, ratio=S.Infinity) - else: - return None - -
    -
    [docs]def hypersimilar(f, g, k): - """Returns True if 'f' and 'g' are hyper-similar. - - Similarity in hypergeometric sense means that a quotient of - f(k) and g(k) is a rational function in k. This procedure - is useful in solving recurrence relations. - - For more information see hypersimp(). - - """ - f, g = list(map(sympify, (f, g))) - - h = (f/g).rewrite(gamma) - h = h.expand(func=True, basic=False) - - return h.is_rational_function(k) -
    -from sympy.utilities.timeutils import timethis - - -@timethis('combsimp') -
    [docs]def combsimp(expr): - r""" - Simplify combinatorial expressions. - - This function takes as input an expression containing factorials, - binomials, Pochhammer symbol and other "combinatorial" functions, - and tries to minimize the number of those functions and reduce - the size of their arguments. The result is be given in terms of - binomials and factorials. - - The algorithm works by rewriting all combinatorial functions as - expressions involving rising factorials (Pochhammer symbols) and - applies recurrence relations and other transformations applicable - to rising factorials, to reduce their arguments, possibly letting - the resulting rising factorial to cancel. Rising factorials with - the second argument being an integer are expanded into polynomial - forms and finally all other rising factorial are rewritten in terms - more familiar functions. If the initial expression contained any - combinatorial functions, the result is expressed using binomial - coefficients and gamma functions. If the initial expression consisted - of gamma functions alone, the result is expressed in terms of gamma - functions. - - If the result is expressed using gamma functions, the following three - additional steps are performed: - - 1. Reduce the number of gammas by applying the reflection theorem - gamma(x)*gamma(1-x) == pi/sin(pi*x). - 2. Reduce the number of gammas by applying the multiplication theorem - gamma(x)*gamma(x+1/n)*...*gamma(x+(n-1)/n) == C*gamma(n*x). - 3. Reduce the number of prefactors by absorbing them into gammas, where - possible. - - All transformation rules can be found (or was derived from) here: - - 1. http://functions.wolfram.com/GammaBetaErf/Pochhammer/17/01/02/ - 2. http://functions.wolfram.com/GammaBetaErf/Pochhammer/27/01/0005/ - - Examples - ======== - - >>> from sympy.simplify import combsimp - >>> from sympy import factorial, binomial - >>> from sympy.abc import n, k - - >>> combsimp(factorial(n)/factorial(n - 3)) - n*(n - 2)*(n - 1) - >>> combsimp(binomial(n+1, k+1)/binomial(n, k)) - (n + 1)/(k + 1) - - """ - factorial = C.factorial - binomial = C.binomial - gamma = C.gamma - - # as a rule of thumb, if the expression contained gammas initially, it - # probably makes sense to retain them - as_gamma = not expr.has(factorial, binomial) - - class rf(Function): - @classmethod - def eval(cls, a, b): - if b.is_Integer: - if not b: - return S.One - - n, result = int(b), S.One - - if n > 0: - for i in range(n): - result *= a + i - - return result - elif n < 0: - for i in range(1, -n + 1): - result *= a - i - - return 1/result - else: - if b.is_Add: - c, _b = b.as_coeff_Add() - - if c.is_Integer: - if c > 0: - return rf(a, _b)*rf(a + _b, c) - elif c < 0: - return rf(a, _b)/rf(a + _b + c, -c) - - if a.is_Add: - c, _a = a.as_coeff_Add() - - if c.is_Integer: - if c > 0: - return rf(_a, b)*rf(_a + b, c)/rf(_a, c) - elif c < 0: - return rf(_a, b)*rf(_a + c, -c)/rf(_a + b + c, -c) - - expr = expr.replace(binomial, - lambda n, k: rf((n - k + 1).expand(), k.expand())/rf(1, k.expand())) - expr = expr.replace(factorial, - lambda n: rf(1, n.expand())) - expr = expr.rewrite(gamma) - expr = expr.replace(gamma, - lambda n: rf(1, (n - 1).expand())) - - if as_gamma: - expr = expr.replace(rf, - lambda a, b: gamma(a + b)/gamma(a)) - else: - expr = expr.replace(rf, - lambda a, b: binomial(a + b - 1, b)*factorial(b)) - - def rule(n, k): - coeff, rewrite = S.One, False - - cn, _n = n.as_coeff_Add() - - if _n and cn.is_Integer and cn: - coeff *= rf(_n + 1, cn)/rf(_n - k + 1, cn) - rewrite = True - n = _n - - # this sort of binomial has already been removed by - # rising factorials but is left here in case the order - # of rule application is changed - if k.is_Add: - ck, _k = k.as_coeff_Add() - if _k and ck.is_Integer and ck: - coeff *= rf(n - ck - _k + 1, ck)/rf(_k + 1, ck) - rewrite = True - k = _k - - if rewrite: - return coeff*binomial(n, k) - - expr = expr.replace(binomial, rule) - - def rule_gamma(expr): - """ Simplify products of gamma functions further. """ - - if expr.is_Atom: - return expr - - expr = expr.func(*[rule_gamma(x) for x in expr.args]) - if not expr.is_Mul: - return expr - - numer_gammas = [] - denom_gammas = [] - denom_others = [] - newargs, numer_others = expr.args_cnc() - - # order newargs canonically - cexpr = expr.func(*newargs) - newargs = list(cexpr._sorted_args) if not cexpr.is_Atom else [cexpr] - del cexpr - - while newargs: - arg = newargs.pop() - b, e = arg.as_base_exp() - if e.is_Integer: - n = abs(e) - if isinstance(b, gamma): - barg = b.args[0] - if e > 0: - numer_gammas.extend([barg]*n) - elif e < 0: - denom_gammas.extend([barg]*n) - else: - if e > 0: - numer_others.extend([b]*n) - elif e < 0: - denom_others.extend([b]*n) - else: - numer_others.append(arg) - - # Try to reduce the number of gamma factors by applying the - # reflection formula gamma(x)*gamma(1-x) = pi/sin(pi*x) - for gammas, numer, denom in [( - numer_gammas, numer_others, denom_others), - (denom_gammas, denom_others, numer_others)]: - new = [] - while gammas: - g1 = gammas.pop() - if g1.is_integer: - new.append(g1) - continue - for i, g2 in enumerate(gammas): - n = g1 + g2 - 1 - if not n.is_Integer: - continue - numer.append(S.Pi) - denom.append(C.sin(S.Pi*g1)) - gammas.pop(i) - if n > 0: - for k in range(n): - numer.append(1 - g1 + k) - elif n < 0: - for k in range(-n): - denom.append(-g1 - k) - break - else: - new.append(g1) - # /!\ updating IN PLACE - gammas[:] = new - - # Try to reduce the number of gamma factors by applying the - # multiplication theorem. - - def _run(coeffs): - # find runs in coeffs such that the difference in terms (mod 1) - # of t1, t2, ..., tn is 1/n - from sympy.utilities.iterables import uniq - u = list(uniq(coeffs)) - for i in range(len(u)): - dj = ([((u[j] - u[i]) % 1, j) for j in range(i + 1, len(u))]) - for one, j in dj: - if one.p == 1 and one.q != 1: - n = one.q - got = [i] - get = list(range(1, n)) - for d, j in dj: - m = n*d - if m.is_Integer and m in get: - get.remove(m) - got.append(j) - if not get: - break - else: - continue - for i, j in enumerate(got): - c = u[j] - coeffs.remove(c) - got[i] = c - return one.q, got[0], got[1:] - - def _mult_thm(gammas, numer, denom): - # pull off and analyze the leading coefficient from each gamma arg - # looking for runs in those Rationals - - # expr -> coeff + resid -> rats[resid] = coeff - rats = {} - for g in gammas: - c, resid = g.as_coeff_Add() - rats.setdefault(resid, []).append(c) - - # look for runs in Rationals for each resid - keys = sorted(rats, key=default_sort_key) - for resid in keys: - coeffs = list(sorted(rats[resid])) - new = [] - while True: - run = _run(coeffs) - if run is None: - break - - # process the sequence that was found: - # 1) convert all the gamma functions to have the right - # argument (could be off by an integer) - # 2) append the factors corresponding to the theorem - # 3) append the new gamma function - - n, ui, other = run - - # (1) - for u in other: - con = resid + u - 1 - for k in range(int(u - ui)): - numer.append(con - k) - - con = n*(resid + ui) # for (2) and (3) - - # (2) - numer.append((2*S.Pi)**(S(n - 1)/2)* - n**(S(1)/2 - con)) - # (3) - new.append(con) - - # restore resid to coeffs - rats[resid] = [resid + c for c in coeffs] + new - - # rebuild the gamma arguments - g = [] - for resid in keys: - g += rats[resid] - # /!\ updating IN PLACE - gammas[:] = g - - for l, numer, denom in [(numer_gammas, numer_others, denom_others), - (denom_gammas, denom_others, numer_others)]: - _mult_thm(l, numer, denom) - - # Try to reduce the number of gammas by using the duplication - # theorem to cancel an upper and lower. - # e.g. gamma(2*s)/gamma(s) = gamma(s)*gamma(s+1/2)*C/gamma(s) - # (in principle this can also be done with with factors other than two, - # but two is special in that we need only matching numer and denom, not - # several in numer). - for ng, dg, no, do in [(numer_gammas, denom_gammas, numer_others, - denom_others), - (denom_gammas, numer_gammas, denom_others, - numer_others)]: - - while True: - for x in ng: - for y in dg: - n = x - 2*y - if n.is_Integer: - break - else: - continue - break - else: - break - ng.remove(x) - dg.remove(y) - if n > 0: - for k in range(n): - no.append(2*y + k) - elif n < 0: - for k in range(-n): - do.append(2*y - 1 - k) - ng.append(y + S(1)/2) - no.append(2**(2*y - 1)) - do.append(sqrt(S.Pi)) - - # Try to absorb factors into the gammas. - # This code (in particular repeated calls to find_fuzzy) can be very - # slow. - def find_fuzzy(l, x): - S1, T1 = compute_ST(x) - for y in l: - S2, T2 = inv[y] - if T1 != T2 or (not S1.intersection(S2) and - (S1 != set() or S2 != set())): - continue - # XXX we want some simplification (e.g. cancel or - # simplify) but no matter what it's slow. - a = len(cancel(x/y).free_symbols) - b = len(x.free_symbols) - c = len(y.free_symbols) - # TODO is there a better heuristic? - if a == 0 and (b > 0 or c > 0): - return y - - # We thus try to avoid expensive calls by building the following - # "invariants": For every factor or gamma function argument - # - the set of free symbols S - # - the set of functional components T - # We will only try to absorb if T1==T2 and (S1 intersect S2 != emptyset - # or S1 == S2 == emptyset) - inv = {} - - def compute_ST(expr): - from sympy import Function, Pow - if expr in inv: - return inv[expr] - return (expr.free_symbols, expr.atoms(Function).union( - set(e.exp for e in expr.atoms(Pow)))) - - def update_ST(expr): - inv[expr] = compute_ST(expr) - for expr in numer_gammas + denom_gammas + numer_others + denom_others: - update_ST(expr) - - for gammas, numer, denom in [( - numer_gammas, numer_others, denom_others), - (denom_gammas, denom_others, numer_others)]: - new = [] - while gammas: - g = gammas.pop() - cont = True - while cont: - cont = False - y = find_fuzzy(numer, g) - if y is not None: - numer.remove(y) - if y != g: - numer.append(y/g) - update_ST(y/g) - g += 1 - cont = True - y = find_fuzzy(numer, 1/(g - 1)) - if y is not None: - numer.remove(y) - if y != 1/(g - 1): - numer.append((g - 1)*y) - update_ST((g - 1)*y) - g -= 1 - cont = True - y = find_fuzzy(denom, 1/g) - if y is not None: - denom.remove(y) - if y != 1/g: - denom.append(y*g) - update_ST(y*g) - g += 1 - cont = True - y = find_fuzzy(denom, g - 1) - if y is not None: - denom.remove(y) - if y != g - 1: - numer.append((g - 1)/y) - update_ST((g - 1)/y) - g -= 1 - cont = True - new.append(g) - # /!\ updating IN PLACE - gammas[:] = new - - return C.Mul(*[gamma(g) for g in numer_gammas]) \ - / C.Mul(*[gamma(g) for g in denom_gammas]) \ - * C.Mul(*numer_others) / C.Mul(*denom_others) - - # (for some reason we cannot use Basic.replace in this case) - expr = rule_gamma(expr) - - return factor(expr) - -
    -def signsimp(expr, evaluate=True): - """Make all Add sub-expressions canonical wrt sign. - - If an Add subexpression, ``a``, can have a sign extracted, - as determined by could_extract_minus_sign, it is replaced - with Mul(-1, a, evaluate=False). This allows signs to be - extracted from powers and products. - - Examples - ======== - - >>> from sympy import signsimp, exp - >>> from sympy.abc import x, y - >>> n = -1 + 1/x - >>> n/x/(-n)**2 - 1/n/x - (-1 + 1/x)/(x*(1 - 1/x)**2) - 1/(x*(-1 + 1/x)) - >>> signsimp(_) - 0 - >>> x*n + x*-n - x*(-1 + 1/x) + x*(1 - 1/x) - >>> signsimp(_) - 0 - >>> n**3 - (-1 + 1/x)**3 - >>> signsimp(_) - -(1 - 1/x)**3 - - By default, signsimp doesn't leave behind any hollow simplification: - if making an Add canonical wrt sign didn't change the expression, the - original Add is restored. If this is not desired then the keyword - ``evaluate`` can be set to False: - - >>> e = exp(y - x) - >>> signsimp(e) == e - True - >>> signsimp(e, evaluate=False) - exp(-(x - y)) - - """ - expr = sympify(expr) - if not isinstance(expr, Expr) or expr.is_Atom: - return expr - e = sub_post(sub_pre(expr)) - if not isinstance(e, Expr) or e.is_Atom: - return e - if e.is_Add: - return Add(*[signsimp(a) for a in e.args]) - if evaluate: - e = e.xreplace(dict([(m, -(-m)) for m in e.atoms(Mul) if -(-m) != m])) - return e - - -
    [docs]def simplify(expr, ratio=1.7, measure=count_ops): - """ - Simplifies the given expression. - - Simplification is not a well defined term and the exact strategies - this function tries can change in the future versions of SymPy. If - your algorithm relies on "simplification" (whatever it is), try to - determine what you need exactly - is it powsimp()?, radsimp()?, - together()?, logcombine()?, or something else? And use this particular - function directly, because those are well defined and thus your algorithm - will be robust. - - Nonetheless, especially for interactive use, or when you don't know - anything about the structure of the expression, simplify() tries to apply - intelligent heuristics to make the input expression "simpler". For - example: - - >>> from sympy import simplify, cos, sin - >>> from sympy.abc import x, y - >>> a = (x + x**2)/(x*sin(y)**2 + x*cos(y)**2) - >>> a - (x**2 + x)/(x*sin(y)**2 + x*cos(y)**2) - >>> simplify(a) - x + 1 - - Note that we could have obtained the same result by using specific - simplification functions: - - >>> from sympy import trigsimp, cancel - >>> b = trigsimp(a) - >>> b - (x**2 + x)/x - >>> c = cancel(b) - >>> c - x + 1 - - In some cases, applying :func:`simplify` may actually result in some more - complicated expression. The default ``ratio=1.7`` prevents more extreme - cases: if (result length)/(input length) > ratio, then input is returned - unmodified. The ``measure`` parameter lets you specify the function used - to determine how complex an expression is. The function should take a - single argument as an expression and return a number such that if - expression ``a`` is more complex than expression ``b``, then - ``measure(a) > measure(b)``. The default measure function is - :func:`count_ops`, which returns the total number of operations in the - expression. - - For example, if ``ratio=1``, ``simplify`` output can't be longer - than input. - - :: - - >>> from sympy import sqrt, simplify, count_ops, oo - >>> root = 1/(sqrt(2)+3) - - Since ``simplify(root)`` would result in a slightly longer expression, - root is returned unchanged instead:: - - >>> simplify(root, ratio=1) == root - True - - If ``ratio=oo``, simplify will be applied anyway:: - - >>> count_ops(simplify(root, ratio=oo)) > count_ops(root) - True - - Note that the shortest expression is not necessary the simplest, so - setting ``ratio`` to 1 may not be a good idea. - Heuristically, the default value ``ratio=1.7`` seems like a reasonable - choice. - - You can easily define your own measure function based on what you feel - should represent the "size" or "complexity" of the input expression. Note - that some choices, such as ``lambda expr: len(str(expr))`` may appear to be - good metrics, but have other problems (in this case, the measure function - may slow down simplify too much for very large expressions). If you don't - know what a good metric would be, the default, ``count_ops``, is a good one. - - For example: - - >>> from sympy import symbols, log - >>> a, b = symbols('a b', positive=True) - >>> g = log(a) + log(b) + log(a)*log(1/b) - >>> h = simplify(g) - >>> h - log(a*b**(log(1/a) + 1)) - >>> count_ops(g) - 8 - >>> count_ops(h) - 6 - - So you can see that ``h`` is simpler than ``g`` using the count_ops metric. - However, we may not like how ``simplify`` (in this case, using - ``logcombine``) has created the ``b**(log(1/a) + 1)`` term. A simple way to - reduce this would be to give more weight to powers as operations in - ``count_ops``. We can do this by using the ``visual=True`` option: - - >>> print(count_ops(g, visual=True)) - 2*ADD + DIV + 4*LOG + MUL - >>> print(count_ops(h, visual=True)) - ADD + DIV + 2*LOG + MUL + POW - - >>> from sympy import Symbol, S - >>> def my_measure(expr): - ... POW = Symbol('POW') - ... # Discourage powers by giving POW a weight of 10 - ... count = count_ops(expr, visual=True).subs(POW, 10) - ... # Every other operation gets a weight of 1 (the default) - ... count = count.replace(Symbol, type(S.One)) - ... return count - >>> my_measure(g) - 8 - >>> my_measure(h) - 15 - >>> 15./8 > 1.7 # 1.7 is the default ratio - True - >>> simplify(g, measure=my_measure) - -log(a)*log(b) + log(a) + log(b) - - Note that because ``simplify()`` internally tries many different - simplification strategies and then compares them using the measure - function, we get a completely different result that is still different - from the input expression by doing this. - """ - - original_expr = expr = sympify(expr) - - try: - return expr._eval_simplify(ratio=ratio, measure=measure) - except AttributeError: - pass - - from sympy.simplify.hyperexpand import hyperexpand - from sympy.functions.special.bessel import BesselBase - - expr = signsimp(expr) - - if not isinstance(expr, Basic): # XXX: temporary hack - return expr - - # TODO: Apply different strategies, considering expression pattern: - # is it a purely rational function? Is there any trigonometric function?... - # See also https://github.com/sympy/sympy/pull/185. - - def shorter(*choices): - '''Return the choice that has the fewest ops. In case of a tie, - the expression listed first is selected.''' - if not has_variety(choices): - return choices[0] - return min(choices, key=measure) - - expr0 = powsimp(expr) - if expr.is_commutative is False: - expr1 = together(expr0) - expr2 = factor_terms(expr1) - else: - expr1 = cancel(expr0) - expr2 = together(expr1.expand(), deep=True) - - # sometimes factors in the denominators need to be allowed to join - # factors in numerators (see issue 3270) - n, d = expr.as_numer_denom() - if (n, d) != fraction(expr): - expr0b = powsimp(n)/powsimp(d) - if expr0b != expr0: - if expr.is_commutative is False: - expr1b = together(expr0b) - expr2b = factor_terms(expr1b) - else: - expr1b = cancel(expr0b) - expr2b = together(expr1b.expand(), deep=True) - if shorter(expr2b, expr) == expr2b: - expr1, expr2 = expr1b, expr2b - - if ratio is S.Infinity: - expr = expr2 - else: - expr = shorter(expr2, expr1, expr) - if not isinstance(expr, Basic): # XXX: temporary hack - return expr - - # hyperexpand automatically only works on hypergeometric terms - expr = hyperexpand(expr) - - if expr.has(BesselBase): - expr = besselsimp(expr) - - if expr.has(C.TrigonometricFunction) or expr.has(C.HyperbolicFunction): - expr = trigsimp(expr, deep=True) - - if expr.has(C.log): - expr = shorter(expand_log(expr, deep=True), logcombine(expr)) - - if expr.has(C.CombinatorialFunction, gamma): - expr = combsimp(expr) - - expr = powsimp(expr, combine='exp', deep=True) - short = shorter(expr, powsimp(factor_terms(expr))) - if short != expr: - # get rid of hollow 2-arg Mul factorization - from sympy.core.rules import Transform - hollow_mul = Transform( - lambda x: Mul(*x.args), - lambda x: - x.is_Mul and - len(x.args) == 2 and - x.args[0].is_Number and - x.args[1].is_Add and - x.is_commutative) - expr = shorter(short.xreplace(hollow_mul), expr) - numer, denom = expr.as_numer_denom() - if denom.is_Add: - n, d = fraction(radsimp(1/denom, symbolic=False, max_terms=1)) - if n is not S.One: - expr = (numer*n).expand()/d - - if expr.could_extract_minus_sign(): - n, d = expr.as_numer_denom() - if d != 0: - expr = -n/(-d) - - if measure(expr) > ratio*measure(original_expr): - return original_expr - - return expr - -
    -def _real_to_rational(expr): - """ - Replace all reals in expr with rationals. - - >>> from sympy import nsimplify - >>> from sympy.abc import x - - >>> nsimplify(.76 + .1*x**.5, rational=True) - sqrt(x)/10 + 19/25 - - """ - p = expr - reps = {} - for r in p.atoms(C.Float): - newr = nsimplify(r, rational=False) - if not newr.is_Rational or r.is_finite and not newr.is_finite: - if newr < 0: - newr = -r - d = Pow(10, int((mpmath.log(newr)/mpmath.log(10)))) - newr = -Rational(str(newr/d))*d - elif newr > 0: - d = Pow(10, int((mpmath.log(r)/mpmath.log(10)))) - newr = Rational(str(r/d))*d - else: - newr = Integer(0) - reps[r] = newr - return p.subs(reps, simultaneous=True) - - -
    [docs]def nsimplify(expr, constants=[], tolerance=None, full=False, rational=None): - """ - Find a simple representation for a number or, if there are free symbols or - if rational=True, then replace Floats with their Rational equivalents. If - no change is made and rational is not False then Floats will at least be - converted to Rationals. - - For numerical expressions, a simple formula that numerically matches the - given numerical expression is sought (and the input should be possible - to evalf to a precision of at least 30 digits). - - Optionally, a list of (rationally independent) constants to - include in the formula may be given. - - A lower tolerance may be set to find less exact matches. If no tolerance - is given then the least precise value will set the tolerance (e.g. Floats - default to 15 digits of precision, so would be tolerance=10**-15). - - With full=True, a more extensive search is performed - (this is useful to find simpler numbers when the tolerance - is set low). - - Examples - ======== - - >>> from sympy import nsimplify, sqrt, GoldenRatio, exp, I, exp, pi - >>> nsimplify(4/(1+sqrt(5)), [GoldenRatio]) - -2 + 2*GoldenRatio - >>> nsimplify((1/(exp(3*pi*I/5)+1))) - 1/2 - I*sqrt(sqrt(5)/10 + 1/4) - >>> nsimplify(I**I, [pi]) - exp(-pi/2) - >>> nsimplify(pi, tolerance=0.01) - 22/7 - - See Also - ======== - sympy.core.function.nfloat - - """ - expr = sympify(expr) - if rational or expr.free_symbols: - return _real_to_rational(expr) - - # sympy's default tolarance for Rationals is 15; other numbers may have - # lower tolerances set, so use them to pick the largest tolerance if none - # was given - tolerance = tolerance or 10**-min([15] + - [mpmath.libmp.libmpf.prec_to_dps(n._prec) - for n in expr.atoms(Float)]) - - prec = 30 - bprec = int(prec*3.33) - - constants_dict = {} - for constant in constants: - constant = sympify(constant) - v = constant.evalf(prec) - if not v.is_Float: - raise ValueError("constants must be real-valued") - constants_dict[str(constant)] = v._to_mpmath(bprec) - - exprval = expr.evalf(prec, chop=True) - re, im = exprval.as_real_imag() - - # Must be numerical - if not ((re.is_Float or re.is_Integer) and (im.is_Float or im.is_Integer)): - return expr - - def nsimplify_real(x): - orig = mpmath.mp.dps - xv = x._to_mpmath(bprec) - try: - # We'll be happy with low precision if a simple fraction - if not (tolerance or full): - mpmath.mp.dps = 15 - rat = mpmath.findpoly(xv, 1) - if rat is not None: - return Rational(-int(rat[1]), int(rat[0])) - mpmath.mp.dps = prec - newexpr = mpmath.identify(xv, constants=constants_dict, - tol=tolerance, full=full) - if not newexpr: - raise ValueError - if full: - newexpr = newexpr[0] - expr = sympify(newexpr) - if expr.is_finite is False and not xv in [mpmath.inf, mpmath.ninf]: - raise ValueError - return expr - finally: - # even though there are returns above, this is executed - # before leaving - mpmath.mp.dps = orig - try: - if re: - re = nsimplify_real(re) - if im: - im = nsimplify_real(im) - except ValueError: - if rational is None: - return _real_to_rational(expr) - return expr - - rv = re + im*S.ImaginaryUnit - # if there was a change or rational is explicitly not wanted - # return the value, else return the Rational representation - if rv != expr or rational is False: - return rv - return _real_to_rational(expr) - -
    -
    [docs]def logcombine(expr, force=False): - """ - Takes logarithms and combines them using the following rules: - - - log(x)+log(y) == log(x*y) - - a*log(x) == log(x**a) - - These identities are only valid if x and y are positive and if a is real, - so the function will not combine the terms unless the arguments have the - proper assumptions on them. Use logcombine(func, force=True) to - automatically assume that the arguments of logs are positive and that - coefficients are real. Note that this will not change any assumptions - already in place, so if the coefficient is imaginary or the argument - negative, combine will still not combine the equations. Change the - assumptions on the variables to make them combine. - - Examples - ======== - - >>> from sympy import Symbol, symbols, log, logcombine - >>> from sympy.abc import a, x, y, z - >>> logcombine(a*log(x)+log(y)-log(z)) - a*log(x) + log(y) - log(z) - >>> logcombine(a*log(x)+log(y)-log(z), force=True) - log(x**a*y/z) - >>> x,y,z = symbols('x,y,z', positive=True) - >>> a = Symbol('a', real=True) - >>> logcombine(a*log(x)+log(y)-log(z)) - log(x**a*y/z) - - """ - # Try to make (a+bi)*log(x) == a*log(x)+bi*log(x). This needs to be a - # separate function call to avoid infinite recursion. - expr = expand_mul(expr) - return _logcombine(expr, force) - -
    -def _logcombine(expr, force=False): - """ - Does the main work for logcombine, it's a separate function to avoid an - infinite recursion. See the docstrings of logcombine() for help. - """ - def _getlogargs(expr): - """ - Returns the arguments of the logarithm in an expression. - - Examples - ======== - - _getlogargs(a*log(x*y)) - x*y - """ - if expr.func is log: - return [expr.args[0]] - else: - args = [] - for i in expr.args: - if i.func is log: - args.append(_getlogargs(i)) - return flatten(args) - return None - - if expr.is_Number or expr.is_NumberSymbol or type(expr) == C.Integral: - return expr - - if isinstance(expr, Equality): - retval = Equality(_logcombine(expr.lhs - expr.rhs, force), - Integer(0)) - # If logcombine couldn't do much with the equality, try to make it like - # it was. Hopefully extract_additively won't become smart enough to - # take logs apart :) - right = retval.lhs.extract_additively(expr.lhs) - if right: - return Equality(expr.lhs, _logcombine(-right, force)) - else: - return retval - - if expr.is_Add: - argslist = 1 - notlogs = 0 - coeflogs = 0 - for i in expr.args: - if i.func is log: - if (i.args[0].is_positive or (force and not - i.args[0].is_nonpositive)): - argslist *= _logcombine(i.args[0], force) - else: - notlogs += i - elif i.is_Mul and any( - [getattr(t, 'func', False) == log for t in i.args]): - largs = _getlogargs(i) - assert len(largs) != 0 - loglargs = 1 - for j in largs: - loglargs *= log(j) - - if all(getattr(t, 'is_positive') for t in largs) \ - and getattr(i.extract_multiplicatively(loglargs), 'is_real', False) \ - or (force - and not all(getattr(t, 'is_nonpositive') for t in largs) - and not getattr(i.extract_multiplicatively(loglargs), - 'is_real') is False): - - coeflogs += _logcombine(i, force) - else: - notlogs += i - elif i.has(log): - notlogs += _logcombine(i, force) - else: - notlogs += i - if notlogs + log(argslist) + coeflogs == expr: - return expr - else: - alllogs = _logcombine(log(argslist) + coeflogs, force) - return notlogs + alllogs - - if expr.is_Mul: - a = Wild('a') - x = Wild('x') - coef = expr.match(a*log(x)) - if coef \ - and (coef[a].is_real - or expr.is_Number - or expr.is_NumberSymbol - or type(coef[a]) in (int, float) - or (force - and not coef[a].is_imaginary)) \ - and (coef[a].func != log - or force - or (not getattr(coef[a], 'is_real') is False - and getattr(x, 'is_positive'))): - - return log(coef[x]**coef[a]) - else: - return _logcombine(expr.args[0], force)*reduce(lambda x, y: - _logcombine(x, force)*_logcombine(y, force), - expr.args[1:], S.One) - - if expr.is_Function: - return expr.func(*[_logcombine(t, force) for t in expr.args]) - - if expr.is_Pow: - return _logcombine(expr.args[0], force) ** \ - _logcombine(expr.args[1], force) - - return expr - - -
    [docs]def besselsimp(expr): - """ - Simplify bessel-type functions. - - This routine tries to simplify bessel-type functions. Currently it only - works on the Bessel J and I functions, however. It works by looking at all - such functions in turn, and eliminating factors of "I" and "-1" (actually - their polar equivalents) in front of the argument. After that, functions of - half-integer order are rewritten using trigonometric functions. - - >>> from sympy import besselj, besseli, besselsimp, polar_lift, I, S - >>> from sympy.abc import z, nu - >>> besselsimp(besselj(nu, z*polar_lift(-1))) - exp(I*pi*nu)*besselj(nu, z) - >>> besselsimp(besseli(nu, z*polar_lift(-I))) - exp(-I*pi*nu/2)*besselj(nu, z) - >>> besselsimp(besseli(S(-1)/2, z)) - sqrt(2)*cosh(z)/(sqrt(pi)*sqrt(z)) - """ - from sympy import besselj, besseli, jn, I, pi, Dummy - # TODO - # - extension to more types of functions - # (at least rewriting functions of half integer order should be straight - # forward also for Y and K) - # - better algorithm? - # - simplify (cos(pi*b)*besselj(b,z) - besselj(-b,z))/sin(pi*b) ... - # - use contiguity relations? - - def replacer(fro, to, factors): - factors = set(factors) - - def repl(nu, z): - if factors.intersection(Mul.make_args(z)): - return to(nu, z) - return fro(nu, z) - return repl - - def torewrite(fro, to): - def tofunc(nu, z): - return fro(nu, z).rewrite(to) - return tofunc - - def tominus(fro): - def tofunc(nu, z): - return exp(I*pi*nu)*fro(nu, exp_polar(-I*pi)*z) - return tofunc - - ifactors = [I, exp_polar(I*pi/2), exp_polar(-I*pi/2)] - expr = expr.replace(besselj, replacer(besselj, - torewrite(besselj, besseli), ifactors)) - expr = expr.replace(besseli, replacer(besseli, - torewrite(besseli, besselj), ifactors)) - - minusfactors = [-1, exp_polar(I*pi)] - expr = expr.replace( - besselj, replacer(besselj, tominus(besselj), minusfactors)) - expr = expr.replace( - besseli, replacer(besseli, tominus(besseli), minusfactors)) - - z0 = Dummy('z') - - def expander(fro): - def repl(nu, z): - if (nu % 1) != S(1)/2: - return fro(nu, z) - return unpolarify(fro(nu, z0).rewrite(besselj).rewrite(jn).expand(func=True)).subs(z0, z) - return repl - - expr = expr.replace(besselj, expander(besselj)) - expr = expr.replace(besseli, expander(besseli)) - - return expr
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/simplify/sqrtdenest.html b/dev-py3k/_modules/sympy/simplify/sqrtdenest.html deleted file mode 100644 index 70acbd119e9..00000000000 --- a/dev-py3k/_modules/sympy/simplify/sqrtdenest.html +++ /dev/null @@ -1,725 +0,0 @@ - - - - - - - - - - sympy.simplify.sqrtdenest — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.simplify.sqrtdenest

    -from sympy.functions import sqrt, sign, root
    -from sympy.core import S, Wild, sympify, Mul, Add, Expr
    -from sympy.core.function import expand_multinomial, expand_mul
    -from sympy.core.symbol import Dummy
    -from sympy.polys import Poly, PolynomialError
    -from sympy.core.function import count_ops
    -from sympy.utilities import default_sort_key
    -
    -
    -def _mexpand(expr):
    -    return expand_mul(expand_multinomial(expr))
    -
    -
    -def is_sqrt(expr):
    -    """Return True if expr is a sqrt, otherwise False."""
    -
    -    return expr.is_Pow and expr.exp.is_Rational and abs(expr.exp) is S.Half
    -
    -
    -def sqrt_depth(p):
    -    """Return the maximum depth of any square root argument of p.
    -
    -    >>> from sympy.functions.elementary.miscellaneous import sqrt
    -    >>> from sympy.simplify.sqrtdenest import sqrt_depth
    -
    -    Neither of these square roots contains any other square roots
    -    so the depth is 1:
    -
    -    >>> sqrt_depth(1 + sqrt(2)*(1 + sqrt(3)))
    -    1
    -
    -    The sqrt(3) is contained within a square root so the depth is
    -    2:
    -
    -    >>> sqrt_depth(1 + sqrt(2)*sqrt(1 + sqrt(3)))
    -    2
    -    """
    -
    -    if p.is_Atom:
    -        return 0
    -    elif p.is_Add or p.is_Mul:
    -        return max([sqrt_depth(x) for x in p.args])
    -    elif is_sqrt(p):
    -        return sqrt_depth(p.base) + 1
    -    else:
    -        return 0
    -
    -
    -def is_algebraic(p):
    -    """Return True if p is comprised of only Rationals or square roots
    -    of Rationals and algebraic operations.
    -
    -    Examples
    -    ========
    -    >>> from sympy.functions.elementary.miscellaneous import sqrt
    -    >>> from sympy.simplify.sqrtdenest import is_algebraic
    -    >>> from sympy import cos
    -    >>> is_algebraic(sqrt(2)*(3/(sqrt(7) + sqrt(5)*sqrt(2))))
    -    True
    -    >>> is_algebraic(sqrt(2)*(3/(sqrt(7) + sqrt(5)*cos(2))))
    -    False
    -    """
    -
    -    if p.is_Rational:
    -        return True
    -    elif p.is_Atom:
    -        return False
    -    elif is_sqrt(p) or p.is_Pow and p.exp.is_Integer:
    -        return is_algebraic(p.base)
    -    elif p.is_Add or p.is_Mul:
    -        return all(is_algebraic(x) for x in p.args)
    -    else:
    -        return False
    -
    -
    -def _subsets(n):
    -    """
    -    Returns all possible subsets of the set (0, 1, ..., n-1) except the
    -    empty set, listed in reversed lexicographical order according to binary
    -    representation, so that the case of the fourth root is treated last.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.simplify.sqrtdenest import _subsets
    -    >>> _subsets(2)
    -    [[1, 0], [0, 1], [1, 1]]
    -
    -    """
    -    if n == 1:
    -        a = [[1]]
    -    elif n == 2:
    -        a = [[1, 0], [0, 1], [1, 1]]
    -    elif n == 3:
    -        a = [[1, 0, 0], [0, 1, 0], [1, 1, 0],
    -             [0, 0, 1], [1, 0, 1], [0, 1, 1], [1, 1, 1]]
    -    else:
    -        b = _subsets(n - 1)
    -        a0 = [x + [0] for x in b]
    -        a1 = [x + [1] for x in b]
    -        a = a0 + [[0]*(n - 1) + [1]] + a1
    -    return a
    -
    -
    -
    [docs]def sqrtdenest(expr, max_iter=3): - """Denests sqrts in an expression that contain other square roots - if possible, otherwise returns the expr unchanged. This is based on the - algorithms of [1]. - - Examples - ======== - - >>> from sympy.simplify.sqrtdenest import sqrtdenest - >>> from sympy import sqrt - >>> sqrtdenest(sqrt(5 + 2 * sqrt(6))) - sqrt(2) + sqrt(3) - - See Also - ======== - sympy.solvers.solvers.unrad - - References - ========== - [1] http://www.almaden.ibm.com/cs/people/fagin/symb85.pdf - - [2] D. J. Jeffrey and A. D. Rich, 'Symplifying Square Roots of Square Roots - by Denesting' (available at http://www.cybertester.com/data/denest.pdf) - - """ - expr = expand_mul(sympify(expr)) - for i in range(max_iter): - z = _sqrtdenest0(expr) - if expr == z: - return expr - expr = z - return expr - -
    -def _sqrt_match(p): - """Return [a, b, r] for p.match(a + b*sqrt(r)) where, in addition to - matching, sqrt(r) also has then maximal sqrt_depth among addends of p. - - Examples - ======== - >>> from sympy.functions.elementary.miscellaneous import sqrt - >>> from sympy.simplify.sqrtdenest import _sqrt_match - >>> _sqrt_match(1 + sqrt(2) + sqrt(2)*sqrt(3) + 2*sqrt(1+sqrt(5))) - [1 + sqrt(2) + sqrt(6), 2, 1 + sqrt(5)] - """ - from sympy.simplify.simplify import split_surds - - p = _mexpand(p) - if p.is_Number: - res = (p, S.Zero, S.Zero) - elif p.is_Add: - pargs = sorted(p.args, key=default_sort_key) - if all((x**2).is_Rational for x in pargs): - r, b, a = split_surds(p) - res = a, b, r - return list(res) - # to make the process canonical, the argument is included in the tuple - # so when the max is selected, it will be the largest arg having a - # given depth - v = [(sqrt_depth(x), x, i) for i, x in enumerate(pargs)] - nmax = max(v) - if nmax[0] == 0: - res = [] - else: - # select r - depth, _, i = nmax - r = pargs.pop(i) - v.pop(i) - b = S.One - if r.is_Mul: - bv = [] - rv = [] - for x in r.args: - if sqrt_depth(x) < depth: - bv.append(x) - else: - rv.append(x) - b = Mul._from_args(bv) - r = Mul._from_args(rv) - # collect terms comtaining r - a1 = [] - b1 = [b] - for x in v: - if x[0] < depth: - a1.append(x[1]) - else: - x1 = x[1] - if x1 == r: - b1.append(1) - else: - if x1.is_Mul: - x1args = list(x1.args) - if r in x1args: - x1args.remove(r) - b1.append(Mul(*x1args)) - else: - a1.append(x[1]) - else: - a1.append(x[1]) - a = Add(*a1) - b = Add(*b1) - #a = Add._from_args(pargs) - res = (a, b, r**2) - else: - b, r = p.as_coeff_Mul() - if is_sqrt(r): - res = (S.Zero, b, r**2) - else: - res = [] - return list(res) - - -class SqrtdenestStopIteration(StopIteration): - pass - - -def _sqrtdenest0(expr): - """Returns expr after denesting its arguments.""" - - if is_sqrt(expr): - n, d = expr.as_numer_denom() - if d is S.One: # n is a square root - if n.base.is_Add: - args = sorted(n.base.args, key=default_sort_key) - if len(args) > 2 and all((x**2).is_Integer for x in args): - try: - return _sqrtdenest_rec(n) - except SqrtdenestStopIteration: - pass - expr = sqrt(_mexpand(Add(*[_sqrtdenest0(x) for x in args]))) - return _sqrtdenest1(expr) - else: - n, d = [_sqrtdenest0(i) for i in (n, d)] - return n/d - if isinstance(expr, Expr): - args = expr.args - if args: - return expr.func(*[_sqrtdenest0(a) for a in args]) - return expr - - -def _sqrtdenest_rec(expr): - """Helper that denests the square root of three or more surds. - - It returns the denested expression; if it cannot be denested it - throws SqrtdenestStopIteration - - Algorithm: expr.base is in the extension Q_m = Q(sqrt(r_1),..,sqrt(r_k)); - split expr.base = a + b*sqrt(r_k), where `a` and `b` are on - Q_(m-1) = Q(sqrt(r_1),..,sqrt(r_(k-1))); then a**2 - b**2*r_k is - on Q_(m-1); denest sqrt(a**2 - b**2*r_k) and so on. - See [1], section 6. - - Examples - ======== - >>> from sympy import sqrt - >>> from sympy.simplify.sqrtdenest import _sqrtdenest_rec - >>> _sqrtdenest_rec(sqrt(-72*sqrt(2) + 158*sqrt(5) + 498)) - -sqrt(10) + sqrt(2) + 9 + 9*sqrt(5) - >>> w=-6*sqrt(55)-6*sqrt(35)-2*sqrt(22)-2*sqrt(14)+2*sqrt(77)+6*sqrt(10)+65 - >>> _sqrtdenest_rec(sqrt(w)) - -sqrt(11) - sqrt(7) + sqrt(2) + 3*sqrt(5) - """ - from sympy.simplify.simplify import radsimp, split_surds, rad_rationalize - if not expr.is_Pow: - return sqrtdenest(expr) - if expr.base < 0: - return sqrt(-1)*_sqrtdenest_rec(sqrt(-expr.base)) - g, a, b = split_surds(expr.base) - a = a*sqrt(g) - if a < b: - a, b = b, a - c2 = _mexpand(a**2 - b**2) - if len(c2.args) > 2: - g, a1, b1 = split_surds(c2) - a1 = a1*sqrt(g) - if a1 < b1: - a1, b1 = b1, a1 - c2_1 = _mexpand(a1**2 - b1**2) - c_1 = _sqrtdenest_rec(sqrt(c2_1)) - d_1 = _sqrtdenest_rec(sqrt(a1 + c_1)) - num, den = rad_rationalize(b1, d_1) - c = _mexpand(d_1/sqrt(2) + num/(den*sqrt(2))) - else: - c = _sqrtdenest1(sqrt(c2)) - - if sqrt_depth(c) > 1: - raise SqrtdenestStopIteration - ac = a + c - if len(ac.args) >= len(expr.args): - if count_ops(ac) >= count_ops(expr.base): - raise SqrtdenestStopIteration - d = sqrtdenest(sqrt(ac)) - if sqrt_depth(d) > 1: - raise SqrtdenestStopIteration - num, den = rad_rationalize(b, d) - r = d/sqrt(2) + num/(den*sqrt(2)) - r = radsimp(r) - return _mexpand(r) - - -def _sqrtdenest1(expr, denester=True): - """Return denested expr after denesting with simpler methods or, that - failing, using the denester.""" - - from sympy.simplify.simplify import radsimp - - if not is_sqrt(expr): - return expr - - a = expr.base - if a.is_Atom: - return expr - val = _sqrt_match(a) - if not val: - return expr - - a, b, r = val - # try a quick numeric denesting - d2 = _mexpand(a**2 - b**2*r) - if d2.is_Rational: - if d2.is_positive: - z = _sqrt_numeric_denest(a, b, r, d2) - if z is not None: - return z - else: - # fourth root case - # sqrtdenest(sqrt(3 + 2*sqrt(3))) = - # sqrt(2)*3**(1/4)/2 + sqrt(2)*3**(3/4)/2 - dr2 = _mexpand(-d2*r) - dr = sqrt(dr2) - if dr.is_Rational: - z = _sqrt_numeric_denest(_mexpand(b*r), a, r, dr2) - if z is not None: - return z/root(r, 4) - - else: - z = _sqrt_symbolic_denest(a, b, r) - if z is not None: - return z - - if not denester or not is_algebraic(expr): - return expr - - res = sqrt_biquadratic_denest(expr, a, b, r, d2) - if res: - return res - - # now call to the denester - av0 = [a, b, r, d2] - z = _denester([radsimp(expr**2)], av0, 0, sqrt_depth(expr))[0] - if av0[1] is None: - return expr - if z is not None: - if sqrt_depth(z) == sqrt_depth(expr) and count_ops(z) > count_ops(expr): - return expr - return z - return expr - - -def _sqrt_symbolic_denest(a, b, r): - """Given an expression, sqrt(a + b*sqrt(b)), return the denested - expression or None. - - Algorithm: - If r = ra + rb*sqrt(rr), try replacing sqrt(rr) in ``a`` with - (y**2 - ra)/rb, and if the result is a quadratic, ca*y**2 + cb*y + cc, and - (cb + b)**2 - 4*ca*cc is 0, then sqrt(a + b*sqrt(r)) can be rewritten as - sqrt(ca*(sqrt(r) + (cb + b)/(2*ca))**2). - - Examples - ======== - >>> from sympy.simplify.sqrtdenest import _sqrt_symbolic_denest, sqrtdenest - >>> from sympy import sqrt, Symbol - >>> from sympy.abc import x - - >>> a, b, r = 16 - 2*sqrt(29), 2, -10*sqrt(29) + 55 - >>> _sqrt_symbolic_denest(a, b, r) - sqrt(-2*sqrt(29) + 11) + sqrt(5) - - If the expression is numeric, it will be simplified: - - >>> w = sqrt(sqrt(sqrt(3) + 1) + 1) + 1 + sqrt(2) - >>> sqrtdenest(sqrt((w**2).expand())) - 1 + sqrt(2) + sqrt(1 + sqrt(1 + sqrt(3))) - - Otherwise, it will only be simplified if assumptions allow: - - >>> w = w.subs(sqrt(3), sqrt(x + 3)) - >>> sqrtdenest(sqrt((w**2).expand())) - sqrt((sqrt(sqrt(sqrt(x + 3) + 1) + 1) + 1 + sqrt(2))**2) - - Notice that the argument of the sqrt is a square. If x is made positive - then the sqrt of the square is resolved: - - >>> _.subs(x, Symbol('x', positive=True)) - sqrt(sqrt(sqrt(x + 3) + 1) + 1) + 1 + sqrt(2) - """ - - a, b, r = list(map(sympify, (a, b, r))) - rval = _sqrt_match(r) - if not rval: - return None - ra, rb, rr = rval - if rb: - y = Dummy('y', positive=True) - try: - newa = Poly(a.subs(sqrt(rr), (y**2 - ra)/rb), y) - except PolynomialError: - return None - if newa.degree() == 2: - ca, cb, cc = newa.all_coeffs() - cb += b - if _mexpand(cb**2 - 4*ca*cc).equals(0): - z = sqrt(ca*(sqrt(r) + cb/(2*ca))**2) - if z.is_number: - z = _mexpand(Mul._from_args(z.as_content_primitive())) - return z - - -def _sqrt_numeric_denest(a, b, r, d2): - """Helper that denest expr = a + b*sqrt(r), with d2 = a**2 - b**2*r > 0 - or returns None if not denested. - """ - from sympy.simplify.simplify import radsimp - depthr = sqrt_depth(r) - d = sqrt(d2) - vad = a + d - # sqrt_depth(res) <= sqrt_depth(vad) + 1 - # sqrt_depth(expr) = depthr + 2 - # there is denesting if sqrt_depth(vad)+1 < depthr + 2 - # if vad**2 is Number there is a fourth root - if sqrt_depth(vad) < depthr + 1 or (vad**2).is_Rational: - vad1 = radsimp(1/vad) - return (sqrt(vad/2) + sign(b)*sqrt((b**2*r*vad1/2).expand())).expand() - - -def sqrt_biquadratic_denest(expr, a, b, r, d2): - """denest expr = sqrt(a + b*sqrt(r)) - where a, b, r are linear combinations of square roots of - positive rationals on the rationals (SQRR) and r > 0, b != 0, - d2 = a**2 - b**2*r > 0 - - If it cannot denest it returns None. - - ALGORITHM - Search for a solution A of type SQRR of the biquadratic equation - 4*A**4 - 4*a*A**2 + b**2*r = 0 (1) - sqd = sqrt(a**2 - b**2*r) - Choosing the sqrt to be positive, the possible solutions are - A = sqrt(a/2 +/- sqd/2) - Since a, b, r are SQRR, then a**2 - b**2*r is a SQRR, - so if sqd can be denested, it is done by - _sqrtdenest_rec, and the result is a SQRR. - Similarly for A. - Examples of solutions (in both cases a and sqd are positive): - - Example of expr with solution sqrt(a/2 + sqd/2) but not - solution sqrt(a/2 - sqd/2): - expr = sqrt(-sqrt(15) - sqrt(2)*sqrt(-sqrt(5) + 5) - sqrt(3) + 8) - a = -sqrt(15) - sqrt(3) + 8; sqd = -2*sqrt(5) - 2 + 4*sqrt(3) - - Example of expr with solution sqrt(a/2 - sqd/2) but not - solution sqrt(a/2 + sqd/2): - w = 2 + r2 + r3 + (1 + r3)*sqrt(2 + r2 + 5*r3) - expr = sqrt((w**2).expand()) - a = 4*sqrt(6) + 8*sqrt(2) + 47 + 28*sqrt(3) - sqd = 29 + 20*sqrt(3) - - Define B = b/2*A; eq.(1) implies a = A**2 + B**2*r; then - expr**2 = a + b*sqrt(r) = (A + B*sqrt(r))**2 - - Examples - ======== - >>> from sympy import sqrt - >>> from sympy.simplify.sqrtdenest import _sqrt_match, sqrt_biquadratic_denest - >>> z = sqrt((2*sqrt(2) + 4)*sqrt(2 + sqrt(2)) + 5*sqrt(2) + 8) - >>> a, b, r = _sqrt_match(z**2) - >>> d2 = a**2 - b**2*r - >>> sqrt_biquadratic_denest(z, a, b, r, d2) - sqrt(2) + sqrt(sqrt(2) + 2) + 2 - """ - from sympy.simplify.simplify import radsimp, rad_rationalize - if r <= 0 or d2 < 0 or not b or sqrt_depth(expr.base) < 2: - return None - for x in (a, b, r): - for y in x.args: - y2 = y**2 - if not y2.is_Integer or not y2.is_positive: - return None - sqd = _mexpand(sqrtdenest(sqrt(radsimp(d2)))) - if sqrt_depth(sqd) > 1: - return None - x1, x2 = [a/2 + sqd/2, a/2 - sqd/2] - # look for a solution A with depth 1 - for x in (x1, x2): - A = sqrtdenest(sqrt(x)) - if sqrt_depth(A) > 1: - continue - Bn, Bd = rad_rationalize(b, _mexpand(2*A)) - B = Bn/Bd - z = A + B*sqrt(r) - if z < 0: - z = -z - return _mexpand(z) - return None - - -def _denester(nested, av0, h, max_depth_level): - """Denests a list of expressions that contain nested square roots. - - Algorithm based on <http://www.almaden.ibm.com/cs/people/fagin/symb85.pdf>. - - It is assumed that all of the elements of 'nested' share the same - bottom-level radicand. (This is stated in the paper, on page 177, in - the paragraph immediately preceding the algorithm.) - - When evaluating all of the arguments in parallel, the bottom-level - radicand only needs to be denested once. This means that calling - _denester with x arguments results in a recursive invocation with x+1 - arguments; hence _denester has polynomial complexity. - - However, if the arguments were evaluated separately, each call would - result in two recursive invocations, and the algorithm would have - exponential complexity. - - This is discussed in the paper in the middle paragraph of page 179. - """ - from sympy.simplify.simplify import radsimp - if h > max_depth_level: - return None, None - if av0[1] is None: - return None, None - if (av0[0] is None and - all(n.is_Number for n in nested)): # no arguments are nested - for f in _subsets(len(nested)): # test subset 'f' of nested - p = _mexpand(Mul(*[nested[i] for i in range(len(f)) if f[i]])) - if f.count(1) > 1 and f[-1]: - p = -p - sqp = sqrt(p) - if sqp.is_Rational: - return sqp, f # got a perfect square so return its square root. - # Otherwise, return the radicand from the previous invocation. - return sqrt(nested[-1]), [0]*len(nested) - else: - R = None - if av0[0] is not None: - values = [av0[:2]] - R = av0[2] - nested2 = [av0[3], R] - av0[0] = None - else: - values = [_f for _f in [_sqrt_match(expr) for expr in nested] if _f] - for v in values: - if v[2]: # Since if b=0, r is not defined - if R is not None: - if R != v[2]: - av0[1] = None - return None, None - else: - R = v[2] - if R is None: - # return the radicand from the previous invocation - return sqrt(nested[-1]), [0]*len(nested) - nested2 = [_mexpand(v[0]**2) - - _mexpand(R*v[1]**2) for v in values] + [R] - d, f = _denester(nested2, av0, h + 1, max_depth_level) - #d = sqrtdenest(d) - if not f: - return None, None - if not any(f[i] for i in range(len(nested))): - v = values[-1] - return sqrt(v[0] + _mexpand(v[1]*d)), f - else: - p = Mul(*[nested[i] for i in range(len(nested)) if f[i]]) - v = _sqrt_match(p) - if 1 in f and f.index(1) < len(nested) - 1 and f[len(nested) - 1]: - v[0] = -v[0] - v[1] = -v[1] - if not f[len(nested)]: # Solution denests with square roots - vad = _mexpand(v[0] + d) - if vad <= 0: - # return the radicand from the previous invocation. - return sqrt(nested[-1]), [0]*len(nested) - if not(sqrt_depth(vad) <= sqrt_depth(R) + 1 or - (vad**2).is_Number): - av0[1] = None - return None, None - - sqvad = _sqrtdenest1(sqrt(vad), denester=False) - if not (sqrt_depth(sqvad) <= sqrt_depth(R) + 1): - av0[1] = None - return None, None - sqvad1 = radsimp(1/sqvad) - res = _mexpand(sqvad/sqrt(2) + (v[1]*sqrt(R)*sqvad1/sqrt(2))) - return res, f - - # sign(v[1])*sqrt(_mexpand(v[1]**2*R*vad1/2))), f - else: # Solution requires a fourth root - s2 = _mexpand(v[1]*R) + d - if s2 <= 0: - return sqrt(nested[-1]), [0]*len(nested) - FR, s = root(_mexpand(R), 4), sqrt(s2) - return _mexpand(s/(sqrt(2)*FR) + v[0]*FR/(sqrt(2)*s)), f -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/simplify/traversaltools.html b/dev-py3k/_modules/sympy/simplify/traversaltools.html deleted file mode 100644 index 66b5c25b09e..00000000000 --- a/dev-py3k/_modules/sympy/simplify/traversaltools.html +++ /dev/null @@ -1,156 +0,0 @@ - - - - - - - - - - sympy.simplify.traversaltools — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.simplify.traversaltools

    -"""Tools for applying functions to specified parts of expressions. """
    -
    -from sympy.core import sympify
    -
    -
    -
    [docs]def use(expr, func, level=0, args=(), kwargs={}): - """ - Use ``func`` to transform ``expr`` at the given level. - - Examples - ======== - - >>> from sympy import use, expand - >>> from sympy.abc import x, y - - >>> f = (x + y)**2*x + 1 - - >>> use(f, expand, level=2) - x*(x**2 + 2*x*y + y**2) + 1 - >>> expand(f) - x**3 + 2*x**2*y + x*y**2 + 1 - - """ - def _use(expr, level): - if not level: - return func(expr, *args, **kwargs) - else: - if expr.is_Atom: - return expr - else: - level -= 1 - _args = [] - - for arg in expr.args: - _args.append(_use(arg, level)) - - return expr.__class__(*_args) - - return _use(sympify(expr), level)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/solvers/ode.html b/dev-py3k/_modules/sympy/solvers/ode.html deleted file mode 100644 index 275fdf1eb08..00000000000 --- a/dev-py3k/_modules/sympy/solvers/ode.html +++ /dev/null @@ -1,3409 +0,0 @@ - - - - - - - - - - sympy.solvers.ode — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.solvers.ode

    -"""
    -This module contains dsolve() and different helper functions that it
    -uses.
    -
    -dsolve() solves ordinary differential equations. See the docstring on
    -the various functions for their uses. Note that partial differential
    -equations support is in pde.py.  Note that ode_hint() functions have
    -docstrings describing their various methods, but they are intended for
    -internal use.  Use dsolve(ode, func, hint=hint) to solve an ode using a
    -specific hint.  See also the docstring on dsolve().
    -
    -**Functions in this module**
    -
    -    These are the user functions in this module:
    -
    -    - dsolve() - Solves ODEs.
    -    - classify_ode() - Classifies ODEs into possible hints for dsolve().
    -    - checkodesol() - Checks if an equation is the solution to an ODE.
    -    - ode_order() - Returns the order (degree) of an ODE.
    -    - homogeneous_order() - Returns the homogeneous order of an
    -      expression.
    -
    -    These are the non-solver helper functions that are for internal use.
    -    The user should use the various options to dsolve() to obtain the
    -    functionality provided by these functions:
    -
    -    - odesimp() - Does all forms of ODE simplification.
    -    - ode_sol_simplicity() - A key function for comparing solutions by
    -      simplicity.
    -    - constantsimp() - Simplifies arbitrary constants.
    -    - constant_renumber() - Renumber arbitrary constants
    -    - _handle_Integral() - Evaluate unevaluated Integrals.
    -    - preprocess - prepare the equation and detect function to solve for
    -
    -    See also the docstrings of these functions.
    -
    -**Currently implemented solver methods**
    -
    -The following methods are implemented for solving ordinary differential
    -equations.  See the docstrings of the various ode_hint() functions for
    -more information on each (run help(ode)):
    -
    -  - 1st order separable differential equations
    -  - 1st order differential equations whose coefficients or dx and dy
    -    are functions homogeneous of the same order.
    -  - 1st order exact differential equations.
    -  - 1st order linear differential equations
    -  - 1st order Bernoulli differential equations.
    -  - 2nd order Liouville differential equations.
    -  - nth order linear homogeneous differential equation with constant
    -    coefficients.
    -  - nth order linear inhomogeneous differential equation with constant
    -    coefficients using the method of undetermined coefficients.
    -  - nth order linear inhomogeneous differential equation with constant
    -    coefficients using the method of variation of parameters.
    -
    -**Philosophy behind this module**
    -
    -This module is designed to make it easy to add new ODE solving methods
    -without having to mess with the solving code for other methods.  The
    -idea is that there is a classify_ode() function, which takes in an ODE
    -and tells you what hints, if any, will solve the ODE.  It does this
    -without attempting to solve the ODE, so it is fast.  Each solving method
    -is a hint, and it has its own function, named ode_hint.  That function
    -takes in the ODE and any match expression gathered by classify_ode and
    -returns a solved result.  If this result has any integrals in it, the
    -ode_hint function will return an unevaluated Integral class. dsolve(),
    -which is the user wrapper function around all of this, will then call
    -odesimp() on the result, which, among other things, will attempt to
    -solve the equation for the dependent variable (the function we are
    -solving for), simplify the arbitrary constants in the expression, and
    -evaluate any integrals, if the hint allows it.
    -
    -**How to add new solution methods**
    -
    -If you have an ODE that you want dsolve() to be able to solve, try to
    -avoid adding special case code here.  Instead, try finding a general
    -method that will solve your ODE, as well as others.  This way, the ode
    -module will become more robust, and unhindered by special case hacks.
    -WolphramAlpha and Maple's DETools[odeadvisor] function are two resources
    -you can use to classify a specific ODE.  It is also better for a method
    -to work with an nth order ODE instead of only with specific orders, if
    -possible.
    -
    -To add a new method, there are a few things that you need to do.  First,
    -you need a hint name for your method.  Try to name your hint so that it
    -is unambiguous with all other methods, including ones that may not be
    -implemented yet.  If your method uses integrals, also include a
    -"hint_Integral" hint.  If there is more than one way to solve ODEs with
    -your method, include a hint for each one, as well as a "hint_best" hint.
    -Your ode_hint_best() function should choose the best using min with
    -ode_sol_simplicity as the key argument.  See
    -ode_1st_homogeneous_coeff_best(), for example. The function that uses
    -your method will be called ode_hint(), so the hint must only use
    -characters that are allowed in a Python function name (alphanumeric
    -characters and the underscore '_' character).  Include a function for
    -every hint, except for "_Integral" hints (dsolve() takes care of those
    -automatically).  Hint names should be all lowercase, unless a word is
    -commonly capitalized (such as Integral or Bernoulli). If you have a hint
    -that you do not want to run with "all_Integral" that doesn't have an
    -"_Integral" counterpart (such as a best hint that would defeat the
    -purpose of "all_Integral"), you will need to remove it manually in the
    -dsolve() code.  See also the classify_ode() docstring for guidelines on
    -writing a hint name.
    -
    -Determine *in general* how the solutions returned by your method
    -compare with other methods that can potentially solve the same ODEs.
    -Then, put your hints in the allhints tuple in the order that they should
    -be called.  The ordering of this tuple determines which hints are
    -default. Note that exceptions are ok, because it is easy for the user to
    -choose individual hints with dsolve().  In general, "_Integral" variants
    -should go at the end of the list, and "_best" variants should go before
    -the various hints they apply to.  For example, the
    -"undetermined_coefficients" hint comes before the
    -"variation_of_parameters" hint because, even though variation of
    -parameters is more general than undetermined coefficients, undetermined
    -coefficients generally returns cleaner results for the ODEs that it can
    -solve than variation of parameters does, and it does not require
    -integration, so it is much faster.
    -
    -Next, you need to have a match expression or a function that matches the
    -type of the ODE, which you should put in classify_ode() (if the match
    -function is more than just a few lines, like
    -_undetermined_coefficients_match(), it should go outside of
    -classify_ode()).  It should match the ODE without solving for it as much
    -as possible, so that classify_ode() remains fast and is not hindered by
    -bugs in solving code.  Be sure to consider corner cases. For example, if
    -your solution method involves dividing by something, make sure you
    -exclude the case where that division will be 0.
    -
    -In most cases, the matching of the ODE will also give you the various
    -parts that you need to solve it. You should put that in a dictionary
    -(.match() will do this for you), and add that as matching_hints['hint']
    -= matchdict in the relevant part of classify_ode.  classify_ode will
    -then send this to dsolve(), which will send it to your function as the
    -match argument. Your function should be named ode_hint(eq, func, order,
    -match). If you need to send more information, put it in the match
    -dictionary.  For example, if you had to substitute in a dummy variable
    -in classify_ode to match the ODE, you will need to pass it to your
    -function using the match dict to access it.  You can access the
    -independent variable using func.args[0], and the dependent variable (the
    -function you are trying to solve for) as func.func.  If, while trying to
    -solve the ODE, you find that you cannot, raise NotImplementedError.
    -dsolve() will catch this error with the "all" meta-hint, rather than
    -causing the whole routine to fail.
    -
    -Add a docstring to your function that describes the method employed.
    -Like with anything else in SymPy, you will need to add a doctest to the
    -docstring, in addition to real tests in test_ode.py.  Try to maintain
    -consistency with the other hint functions' docstrings.  Add your method
    -to the list at the top of this docstring.  Also, add your method to
    -ode.rst in the docs/src directory, so that the Sphinx docs will pull its
    -docstring into the main SymPy documentation.  Be sure to make the Sphinx
    -documentation by running "make html" from within the doc directory to
    -verify that the docstring formats correctly.
    -
    -If your solution method involves integrating, use C.Integral() instead
    -of integrate().  This allows the user to bypass hard/slow integration by
    -using the "_Integral" variant of your hint.  In most cases, calling
    -.doit() will integrate your solution.  If this is not the case, you will
    -need to write special code in _handle_Integral().  Arbitrary constants
    -should be symbols named C1, C2, and so on.  All solution methods should
    -return an equality instance.  If you need an arbitrary number of
    -arbitrary constants, you can use constants =
    -numbered_symbols(prefix='C', cls=Symbol, start=1).  If it is
    -possible to solve for the dependent function in a general way, do so.
    -Otherwise, do as best as you can, but do not call solve in your
    -ode_hint() function.  odesimp() will attempt to solve the solution for
    -you, so you do not need to do that. Lastly, if your ODE has a common
    -simplification that can be applied to your solutions, you can add a
    -special case in odesimp() for it.  For example, solutions returned from
    -the "1st_homogeneous_coeff" hints often have many log() terms, so
    -odesimp() calls logcombine() on them (it also helps to write the
    -arbitrary constant as log(C1) instead of C1 in this case).  Also
    -consider common ways that you can rearrange your solution to have
    -constantsimp() take better advantage of it.  It is better to put
    -simplification in odesimp() than in your method, because it can then be
    -turned off with the simplify flag in dsolve(). If you have any
    -extraneous simplification in your function, be sure to only run it using
    -"if match.get('simplify', True):", especially if it can be slow or if it
    -can reduce the domain of the solution.
    -
    -Finally, as with every contribution to SymPy, your method will need to
    -be tested. Add a test for each method in test_ode.py.  Follow the
    -conventions there, i.e., test the solver using dsolve(eq, f(x),
    -hint=your_hint), and also test the solution using checkodesol (you can
    -put these in a separate tests and skip/XFAIL if it runs too slow/doesn't
    -work).  Be sure to call your hint specifically in dsolve, that way the
    -test won't be broken simply by the introduction of another matching
    -hint. If your method works for higher order (>1) ODEs, you will need to
    -run sol = constant_renumber(sol, 'C', 1, order), for each solution, where
    -order is the order of the ODE. This is because constant_renumber renumbers
    -the arbitrary constants by printing order, which is platform dependent.
    -Try to test every corner case of your solver, including a range of
    -orders if it is a nth order solver, but if your solver is slow, such as
    -if it involves hard integration, try to keep the test run time down.
    -
    -Feel free to refactor existing hints to avoid duplicating code or
    -creating inconsistencies.  If you can show that your method exactly
    -duplicates an existing method, including in the simplicity and speed of
    -obtaining the solutions, then you can remove the old, less general
    -method. The existing code is tested extensively in test_ode.py, so if
    -anything is broken, one of those tests will surely fail.
    -
    -"""
    -from collections import defaultdict
    -
    -from sympy.core import Add, C, S, Mul, Pow, oo
    -from sympy.core.compatibility import iterable, is_sequence, set_union
    -from sympy.utilities.exceptions import SymPyDeprecationWarning
    -from sympy.core.function import Derivative, AppliedUndef, diff, expand_mul
    -from sympy.core.multidimensional import vectorize
    -from sympy.core.relational import Equality, Eq
    -from sympy.core.symbol import Symbol, Wild, Dummy
    -from sympy.core.sympify import sympify
    -
    -from sympy.functions import cos, exp, im, log, re, sin, tan, sqrt, sign
    -from sympy.matrices import wronskian
    -from sympy.polys import Poly, RootOf, terms_gcd
    -from sympy.series import Order
    -from sympy.simplify import collect, logcombine, powsimp, separatevars, \
    -    simplify, trigsimp, denom
    -from sympy.solvers import solve
    -
    -from sympy.utilities import numbered_symbols, default_sort_key, sift
    -from functools import reduce
    -
    -# This is a list of hints in the order that they should be applied.  That means
    -# that, in general, hints earlier in the list should produce simpler results
    -# than those later for ODEs that fit both.  This is just based on my own
    -# empirical observations, so if you find that *in general*, a hint later in
    -# the list is better than one before it, fell free to modify the list.  Note
    -# however that you can easily override the hint used in dsolve() for a specific ODE
    -# (see the docstring).  In general, "_Integral" hints should be grouped
    -# at the end of the list, unless there is a method that returns an unevaluable
    -# integral most of the time (which should surely go near the end of the list
    -# anyway).
    -# "default", "all", "best", and "all_Integral" meta-hints should not be
    -# included in this list, but "_best" and "_Integral" hints should be included.
    -allhints = (
    -    "separable", "1st_exact", "1st_linear", "Bernoulli", "Riccati_special_minus2",
    -    "1st_homogeneous_coeff_best", "1st_homogeneous_coeff_subs_indep_div_dep",
    -    "1st_homogeneous_coeff_subs_dep_div_indep", "nth_linear_constant_coeff_homogeneous",
    -    "nth_linear_euler_eq_homogeneous",
    -    "nth_linear_constant_coeff_undetermined_coefficients",
    -    "nth_linear_constant_coeff_variation_of_parameters",
    -    "Liouville", "separable_Integral", "1st_exact_Integral", "1st_linear_Integral",
    -    "Bernoulli_Integral", "1st_homogeneous_coeff_subs_indep_div_dep_Integral",
    -    "1st_homogeneous_coeff_subs_dep_div_indep_Integral",
    -    "nth_linear_constant_coeff_variation_of_parameters_Integral",
    -    "Liouville_Integral"
    -)
    -
    -
    -
    [docs]def preprocess(expr, func=None, hint='_Integral'): - """Prepare expr for solving by making sure that differentiation - is done so that only func remains in unevaluated derivatives and - (if hint doesn't end with _Integral) that doit is applied to all - other derivatives. If hint is None, don't do any differentiation. - (Currently this may cause some simple differential equations to - fail.) - - In case func is None, an attempt will be made to autodetect the - function to be solved for. - - >>> from sympy.solvers.ode import preprocess - >>> from sympy import Derivative, Function, Integral, sin - >>> from sympy.abc import x, y, z - >>> f, g = list(map(Function, 'fg')) - - Apply doit to derivatives that contain more than the function - of interest: - - >>> preprocess(Derivative(f(x) + x, x)) - (Derivative(f(x), x) + 1, f(x)) - - Do others if the differentiation variable(s) intersect with those - of the function of interest or contain the function of interest: - - >>> preprocess(Derivative(g(x), y, z), f(y)) - (0, f(y)) - >>> preprocess(Derivative(f(y), z), f(y)) - (0, f(y)) - - Do others if the hint doesn't end in '_Integral' (the default - assumes that it does): - - >>> preprocess(Derivative(g(x), y), f(x)) - (Derivative(g(x), y), f(x)) - >>> preprocess(Derivative(f(x), y), f(x), hint='') - (0, f(x)) - - Don't do any derivatives if hint is None: - - >>> preprocess(Derivative(f(x) + 1, x) + Derivative(f(x), y), f(x), hint=None) - (Derivative(f(x) + 1, x) + Derivative(f(x), y), f(x)) - - If it's not clear what the function of interest is, it must be given: - - >>> eq = Derivative(f(x) + g(x), x) - >>> preprocess(eq, g(x)) - (Derivative(f(x), x) + Derivative(g(x), x), g(x)) - >>> try: preprocess(eq) - ... except ValueError: print("A ValueError was raised.") - A ValueError was raised. - - """ - - derivs = expr.atoms(Derivative) - if not func: - funcs = set_union(*[d.atoms(AppliedUndef) for d in derivs]) - if len(funcs) != 1: - raise ValueError('The function cannot be automatically detected for %s.' % expr) - func = funcs.pop() - fvars = set(func.args) - if hint is None: - return expr, func - reps = [(d, d.doit()) for d in derivs if not hint.endswith('_Integral') or - d.has(func) or set(d.variables) & fvars] - eq = expr.subs(reps) - return eq, func - -
    -def sub_func_doit(eq, func, new): - """When replacing the func with something else, we usually - want the derivative evaluated, so this function helps in - making that happen. - - To keep subs from having to look through all derivatives, we - mask them off with dummy variables, do the func sub, and then - replace masked off derivatives with their doit values. - - Examples - ======== - - >>> from sympy import Derivative, symbols, Function - >>> from sympy.solvers.ode import sub_func_doit - >>> x, z = symbols('x, z') - >>> y = Function('y') - - >>> sub_func_doit(3*Derivative(y(x), x) - 1, y(x), x) - 2 - - >>> sub_func_doit(x*Derivative(y(x), x) - y(x)**2 + y(x), y(x), - ... 1/(x*(z + 1/x))) - x*(-1/(x**2*(z + 1/x)) + 1/(x**3*(z + 1/x)**2)) + 1/(x*(z + 1/x)) - ...- 1/(x**2*(z + 1/x)**2) - """ - reps = {} - repu = {} - for d in eq.atoms(Derivative): - u = C.Dummy('u') - repu[u] = d.subs(func, new).doit() - reps[d] = u - - return eq.subs(reps).subs(func, new).subs(repu) - - -
    [docs]def dsolve(eq, func=None, hint="default", simplify=True, prep=True, **kwargs): - """ - Solves any (supported) kind of ordinary differential equation. - - **Usage** - - dsolve(eq, f(x), hint) -> Solve ordinary differential equation - eq for function f(x), using method hint. - - - **Details** - - ``eq`` can be any supported ordinary differential equation (see - the ode docstring for supported methods). This can either - be an Equality, or an expression, which is assumed to be - equal to 0. - - ``f(x)`` is a function of one variable whose derivatives in that - variable make up the ordinary differential equation eq. In many - cases it is not necessary to provide this; it will be autodetected - (and an error raised if it couldn't be detected). - - ``hint`` is the solving method that you want dsolve to use. Use - classify_ode(eq, f(x)) to get all of the possible hints for - an ODE. The default hint, 'default', will use whatever hint - is returned first by classify_ode(). See Hints below for - more options that you can use for hint. - - ``simplify`` enables simplification by odesimp(). See its - docstring for more information. Turn this off, for example, - to disable solving of solutions for func or simplification - of arbitrary constants. It will still integrate with this - hint. Note that the solution may contain more arbitrary - constants than the order of the ODE with this option - enabled. - - ``prep``, when False and when ``func`` is given, will skip the - preprocessing step where the equation is cleaned up so it - is ready for solving. - - **Hints** - - Aside from the various solving methods, there are also some - meta-hints that you can pass to dsolve(): - - "default": - This uses whatever hint is returned first by - classify_ode(). This is the default argument to - dsolve(). - - "all": - To make dsolve apply all relevant classification hints, - use dsolve(ODE, func, hint="all"). This will return a - dictionary of hint:solution terms. If a hint causes - dsolve to raise the NotImplementedError, value of that - hint's key will be the exception object raised. The - dictionary will also include some special keys: - - - order: The order of the ODE. See also ode_order(). - - best: The simplest hint; what would be returned by - "best" below. - - best_hint: The hint that would produce the solution - given by 'best'. If more than one hint produces the - best solution, the first one in the tuple returned by - classify_ode() is chosen. - - default: The solution that would be returned by - default. This is the one produced by the hint that - appears first in the tuple returned by classify_ode(). - - "all_Integral": - This is the same as "all", except if a hint also has a - corresponding "_Integral" hint, it only returns the - "_Integral" hint. This is useful if "all" causes - dsolve() to hang because of a difficult or impossible - integral. This meta-hint will also be much faster than - "all", because integrate() is an expensive routine. - - "best": - To have dsolve() try all methods and return the simplest - one. This takes into account whether the solution is - solvable in the function, whether it contains any - Integral classes (i.e. unevaluatable integrals), and - which one is the shortest in size. - - See also the classify_ode() docstring for more info on hints, - and the ode docstring for a list of all supported hints. - - - **Tips** - - You can declare the derivative of an unknown function this way: - >>> from sympy import Function, Derivative - >>> from sympy.abc import x # x is the independent variable - >>> f = Function("f")(x) # f is a function of x - >>> # f_ will be the derivative of f with respect to x - >>> f_ = Derivative(f, x) - - - See test_ode.py for many tests, which serves also as a set of - examples for how to use dsolve(). - - dsolve always returns an Equality class (except for the case - when the hint is "all" or "all_Integral"). If possible, it - solves the solution explicitly for the function being solved - for. Otherwise, it returns an implicit solution. - - Arbitrary constants are symbols named C1, C2, and so on. - - Because all solutions should be mathematically equivalent, - some hints may return the exact same result for an ODE. Often, - though, two different hints will return the same solution - formatted differently. The two should be equivalent. Also - note that sometimes the values of the arbitrary constants in - two different solutions may not be the same, because one - constant may have "absorbed" other constants into it. - - Do help(ode.ode_hintname) to get help more information on a - specific hint, where hintname is the name of a hint without - "_Integral". - - Examples - ======== - - >>> from sympy import Function, dsolve, Eq, Derivative, sin, cos - >>> from sympy.abc import x - >>> f = Function('f') - >>> dsolve(Derivative(f(x),x,x)+9*f(x), f(x)) - f(x) == C1*sin(3*x) + C2*cos(3*x) - >>> dsolve(sin(x)*cos(f(x)) + cos(x)*sin(f(x))*f(x).diff(x), f(x), - ... hint='separable', simplify=False) - -log(sin(f(x))**2 - 1)/2 == C1 + log(sin(x)**2 - 1)/2 - >>> dsolve(sin(x)*cos(f(x)) + cos(x)*sin(f(x))*f(x).diff(x), f(x), - ... hint='1st_exact') - f(x) == acos(C1/cos(x)) - >>> dsolve(sin(x)*cos(f(x)) + cos(x)*sin(f(x))*f(x).diff(x), f(x), - ... hint='best') - f(x) == acos(C1/cos(x)) - >>> # Note that even though separable is the default, 1st_exact produces - >>> # a simpler result in this case. - - """ - # TODO: Implement initial conditions - # See issue 1621. We first need a way to represent things like f'(0). - if isinstance(eq, Equality): - eq = eq.lhs - eq.rhs - - # preprocess the equation and find func if not given - if prep or func is None: - eq, func = preprocess(eq, func) - prep = False - - # Magic that should only be used internally. Prevents classify_ode from - # being called more than it needs to be by passing its results through - # recursive calls. - if kwargs.get('classify', True): - hints = classify_ode(eq, func, dict=True, prep=prep) - else: - # Here is what all this means: - # - # hint: The hint method given to dsolve() by the user. - # hints: The dictionary of hints that match the ODE, along with - # other information (including the internal pass-through magic). - # default: The default hint to return, the first hint from allhints - # that matches the hint. This is obtained from classify_ode(). - # match: The hints dictionary contains a match dictionary for each hint - # (the parts of the ODE for solving). When going through the - # hints in "all", this holds the match string for the current - # hint. - # order: The order of the ODE, as determined by ode_order(). - hints = kwargs.get('hint', - {'default': hint, - hint: kwargs['match'], - 'order': kwargs['order']}) - - if hints['order'] == 0: - raise ValueError( - str(eq) + " is not a differential equation in " + str(func)) - - if not hints['default']: - # classify_ode will set hints['default'] to None if no hints match. - raise NotImplementedError("dsolve: Cannot solve " + str(eq)) - - if hint == 'default': - return dsolve(eq, func, hint=hints['default'], simplify=simplify, - prep=prep, classify=False, order=hints['order'], - match=hints[hints['default']]) - elif hint in ('all', 'all_Integral', 'best'): - retdict = {} - failedhints = {} - gethints = set(hints) - set(['order', 'default', 'ordered_hints']) - if hint == 'all_Integral': - for i in hints: - if i.endswith('_Integral'): - gethints.remove(i[:-len('_Integral')]) - # special case - if "1st_homogeneous_coeff_best" in gethints: - gethints.remove("1st_homogeneous_coeff_best") - for i in gethints: - try: - sol = dsolve(eq, func, hint=i, simplify=simplify, prep=prep, - classify=False, order=hints['order'], match=hints[i]) - except NotImplementedError as detail: # except NotImplementedError as detail: - failedhints[i] = detail - else: - retdict[i] = sol - retdict['best'] = min(list(retdict.values()), key=lambda x: - ode_sol_simplicity(x, func, trysolving=not simplify)) - if hint == 'best': - return retdict['best'] - for i in hints['ordered_hints']: - if retdict['best'] == retdict.get(i, None): - retdict['best_hint'] = i - break - retdict['default'] = hints['default'] - retdict['order'] = sympify(hints['order']) - retdict.update(failedhints) - return retdict - elif hint not in allhints: # and hint not in ('default', 'ordered_hints'): - raise ValueError("Hint not recognized: " + hint) - elif hint not in hints: - raise ValueError("ODE " + str(eq) + " does not match hint " + hint) - elif hint.endswith('_Integral'): - solvefunc = globals()['ode_' + hint[:-len('_Integral')]] - else: - # convert the string into a function - solvefunc = globals()['ode_' + hint] - # odesimp() will attempt to integrate, if necessary, apply constantsimp(), - # attempt to solve for func, and apply any other hint specific simplifications - if simplify: - rv = odesimp(solvefunc(eq, func, order=hints['order'], - match=hints[hint]), func, hints['order'], hint) - else: - # We still want to integrate (you can disable it separately with the hint) - r = hints[hint] - r['simplify'] = False # Some hints can take advantage of this option - rv = _handle_Integral(solvefunc(eq, func, order=hints['order'], - match=hints[hint]), func, hints['order'], hint) - return rv - -
    -
    [docs]def classify_ode(eq, func=None, dict=False, prep=True): - """ - Returns a tuple of possible dsolve() classifications for an ODE. - - The tuple is ordered so that first item is the classification that - dsolve() uses to solve the ODE by default. In general, - classifications at the near the beginning of the list will produce - better solutions faster than those near the end, thought there are - always exceptions. To make dsolve use a different classification, - use dsolve(ODE, func, hint=<classification>). See also the dsolve() - docstring for different meta-hints you can use. - - If ``dict`` is true, classify_ode() will return a dictionary of - hint:match expression terms. This is intended for internal use by - dsolve(). Note that because dictionaries are ordered arbitrarily, - this will most likely not be in the same order as the tuple. - - If ``prep`` is False or ``func`` is None then the equation - will be preprocessed to put it in standard form for classification. - - You can get help on different hints by doing help(ode.ode_hintname), - where hintname is the name of the hint without "_Integral". - - See sympy.ode.allhints or the sympy.ode docstring for a list of all - supported hints that can be returned from classify_ode. - - Notes - ===== - - These are remarks on hint names. - - *"_Integral"* - - If a classification has "_Integral" at the end, it will return - the expression with an unevaluated Integral class in it. Note - that a hint may do this anyway if integrate() cannot do the - integral, though just using an "_Integral" will do so much - faster. Indeed, an "_Integral" hint will always be faster than - its corresponding hint without "_Integral" because integrate() - is an expensive routine. If dsolve() hangs, it is probably - because integrate() is hanging on a tough or impossible - integral. Try using an "_Integral" hint or "all_Integral" to - get it return something. - - Note that some hints do not have "_Integral" counterparts. This - is because integrate() is not used in solving the ODE for those - method. For example, nth order linear homogeneous ODEs with - constant coefficients do not require integration to solve, so - there is no "nth_linear_homogeneous_constant_coeff_Integrate" - hint. You can easily evaluate any unevaluated Integrals in an - expression by doing expr.doit(). - - *Ordinals* - - Some hints contain an ordinal such as "1st_linear". This is to - help differentiate them from other hints, as well as from other - methods that may not be implemented yet. If a hint has "nth" in - it, such as the "nth_linear" hints, this means that the method - used to applies to ODEs of any order. - - *"indep" and "dep"* - - Some hints contain the words "indep" or "dep". These reference - the independent variable and the dependent function, - respectively. For example, if an ODE is in terms of f(x), then - "indep" will refer to x and "dep" will refer to f. - - *"subs"* - - If a hints has the word "subs" in it, it means the the ODE is - solved by substituting the expression given after the word - "subs" for a single dummy variable. This is usually in terms of - "indep" and "dep" as above. The substituted expression will be - written only in characters allowed for names of Python objects, - meaning operators will be spelled out. For example, indep/dep - will be written as indep_div_dep. - - *"coeff"* - - The word "coeff" in a hint refers to the coefficients of - something in the ODE, usually of the derivative terms. See the - docstring for the individual methods for more info (help(ode)). - This is contrast to "coefficients", as in - "undetermined_coefficients", which refers to the common name of - a method. - - *"_best"* - - Methods that have more than one fundamental way to solve will - have a hint for each sub-method and a "_best" - meta-classification. This will evaluate all hints and return the - best, using the same considerations as the normal "best" - meta-hint. - - - Examples - ======== - - >>> from sympy import Function, classify_ode, Eq - >>> from sympy.abc import x - >>> f = Function('f') - >>> classify_ode(Eq(f(x).diff(x), 0), f(x)) - ('separable', '1st_linear', '1st_homogeneous_coeff_best', - '1st_homogeneous_coeff_subs_indep_div_dep', - '1st_homogeneous_coeff_subs_dep_div_indep', - 'nth_linear_constant_coeff_homogeneous', 'separable_Integral', - '1st_linear_Integral', - '1st_homogeneous_coeff_subs_indep_div_dep_Integral', - '1st_homogeneous_coeff_subs_dep_div_indep_Integral') - >>> classify_ode(f(x).diff(x, 2) + 3*f(x).diff(x) + 2*f(x) - 4) - ('nth_linear_constant_coeff_undetermined_coefficients', - 'nth_linear_constant_coeff_variation_of_parameters', - 'nth_linear_constant_coeff_variation_of_parameters_Integral') - - """ - from sympy import expand - - if func and len(func.args) != 1: - raise ValueError("dsolve() and classify_ode() only work with functions " + - "of one variable") - if prep or func is None: - eq, func_ = preprocess(eq, func) - if func is None: - func = func_ - x = func.args[0] - f = func.func - y = Dummy('y') - if isinstance(eq, Equality): - if eq.rhs != 0: - return classify_ode(eq.lhs - eq.rhs, func, prep=False) - eq = eq.lhs - order = ode_order(eq, f(x)) - # hint:matchdict or hint:(tuple of matchdicts) - # Also will contain "default":<default hint> and "order":order items. - matching_hints = {"order": order} - - if not order: - if dict: - matching_hints["default"] = None - return matching_hints - else: - return () - - df = f(x).diff(x) - a = Wild('a', exclude=[f(x)]) - b = Wild('b', exclude=[f(x)]) - c = Wild('c', exclude=[f(x)]) - d = Wild('d', exclude=[df, f(x).diff(x, 2)]) - e = Wild('e', exclude=[df]) - k = Wild('k', exclude=[df]) - n = Wild('n', exclude=[f(x)]) - c1 = Wild('c1', exclude=[x]) - a2 = Wild('a2', exclude=[x, f(x), df]) - b2 = Wild('b2', exclude=[x, f(x), df]) - c2 = Wild('c2', exclude=[x, f(x), df]) - d2 = Wild('d2', exclude=[x, f(x), df]) - - eq = expand(eq) - - # Precondition to try remove f(x) from highest order derivative - reduced_eq = None - if eq.is_Add: - deriv_coef = eq.coeff(f(x).diff(x, order)) - if deriv_coef != 1: - r = deriv_coef.match(a*f(x)**c1) - if r and r[c1]: - den = f(x)**r[c1] - reduced_eq = Add(*[arg/den for arg in eq.args]) - if not reduced_eq: - reduced_eq = eq - - if order == 1: - - # Linear case: a(x)*y'+b(x)*y+c(x) == 0 - if eq.is_Add: - ind, dep = reduced_eq.as_independent(f) - else: - u = Dummy('u') - ind, dep = (reduced_eq + u).as_independent(f) - ind, dep = [tmp.subs(u, 0) for tmp in [ind, dep]] - r = {a: dep.coeff(df), - b: dep.coeff(f(x)), - c: ind} - # double check f[a] since the preconditioning may have failed - if not r[a].has(f) and (r[a]*df + r[b]*f(x) + r[c]).expand() - reduced_eq == 0: - r['a'] = a - r['b'] = b - r['c'] = c - matching_hints["1st_linear"] = r - matching_hints["1st_linear_Integral"] = r - - # Bernoulli case: a(x)*y'+b(x)*y+c(x)*y**n == 0 - r = collect( - reduced_eq, f(x), exact=True).match(a*df + b*f(x) + c*f(x)**n) - if r and r[c] != 0 and r[n] != 1: # See issue 1577 - r['a'] = a - r['b'] = b - r['c'] = c - r['n'] = n - matching_hints["Bernoulli"] = r - matching_hints["Bernoulli_Integral"] = r - - # Riccati special n == -2 case: a2*y'+b2*y**2+c2*y/x+d2/x**2 == 0 - r = collect(reduced_eq, f( - x), exact=True).match(a2*df + b2*f(x)**2 + c2*f(x)/x + d2/x**2) - if r and r[b2] != 0 and (r[c2] != 0 or r[d2] != 0): - r['a2'] = a2 - r['b2'] = b2 - r['c2'] = c2 - r['d2'] = d2 - matching_hints["Riccati_special_minus2"] = r - - # Exact Differential Equation: P(x,y)+Q(x,y)*y'=0 where dP/dy == dQ/dx - # WITH NON-REDUCED FORM OF EQUATION - r = collect(eq, df, exact=True).match(d + e * df) - if r: - r['d'] = d - r['e'] = e - r['y'] = y - r[d] = r[d].subs(f(x), y) - r[e] = r[e].subs(f(x), y) - try: - if r[d] != 0 and simplify(r[d].diff(y)) == simplify(r[e].diff(x)): - matching_hints["1st_exact"] = r - matching_hints["1st_exact_Integral"] = r - except NotImplementedError: - # Differentiating the coefficients might fail because of things - # like f(2*x).diff(x). See issue 1525 and issue 1620. - pass - - # This match is used for several cases below; we now collect on - # f(x) so the matching works. - r = collect(reduced_eq, df, exact=True).match(d + e*df) - if r: - r['d'] = d - r['e'] = e - r['y'] = y - r[d] = r[d].subs(f(x), y) - r[e] = r[e].subs(f(x), y) - - # Separable Case: y' == P(y)*Q(x) - r[d] = separatevars(r[d]) - r[e] = separatevars(r[e]) - # m1[coeff]*m1[x]*m1[y] + m2[coeff]*m2[x]*m2[y]*y' - m1 = separatevars(r[d], dict=True, symbols=(x, y)) - m2 = separatevars(r[e], dict=True, symbols=(x, y)) - if m1 and m2: - r1 = {'m1': m1, 'm2': m2, 'y': y} - matching_hints["separable"] = r1 - matching_hints["separable_Integral"] = r1 - - # First order equation with homogeneous coefficients: - # dy/dx == F(y/x) or dy/dx == F(x/y) - ordera = homogeneous_order(r[d], x, y) - orderb = homogeneous_order(r[e], x, y) - if ordera == orderb and ordera is not None: - # u1=y/x and u2=x/y - u1 = Dummy('u1') - u2 = Dummy('u2') - if simplify((r[d] + u1*r[e]).subs({x: 1, y: u1})) != 0: - matching_hints[ - "1st_homogeneous_coeff_subs_dep_div_indep"] = r - matching_hints["1st_homogeneous_coeff_subs_dep_div_indep_Integral"] = r - if simplify((r[e] + u2*r[d]).subs({x: u2, y: 1})) != 0: - matching_hints[ - "1st_homogeneous_coeff_subs_indep_div_dep"] = r - matching_hints["1st_homogeneous_coeff_subs_indep_div_dep_Integral"] = r - if "1st_homogeneous_coeff_subs_dep_div_indep" in matching_hints \ - and "1st_homogeneous_coeff_subs_indep_div_dep" in matching_hints: - matching_hints["1st_homogeneous_coeff_best"] = r - - if order == 2: - # Liouville ODE f(x).diff(x, 2) + g(f(x))*(f(x).diff(x))**2 + h(x)*f(x).diff(x) - # See Goldstein and Braun, "Advanced Methods for the Solution of - # Differential Equations", pg. 98 - - s = d*f(x).diff(x, 2) + e*df**2 + k*df - r = reduced_eq.match(s) - if r and r[d] != 0: - y = Dummy('y') - g = simplify(r[e]/r[d]).subs(f(x), y) - h = simplify(r[k]/r[d]) - if h.has(f(x)) or g.has(x): - pass - else: - r = {'g': g, 'h': h, 'y': y} - matching_hints["Liouville"] = r - matching_hints["Liouville_Integral"] = r - - if order > 0: - # nth order linear ODE - # a_n(x)y^(n) + ... + a_1(x)y' + a_0(x)y = F(x) = b - - r = _nth_linear_match(reduced_eq, func, order) - - # Constant coefficient case (a_i is constant for all i) - if r and not any(r[i].has(x) for i in r if i >= 0): - # Inhomogeneous case: F(x) is not identically 0 - if r[-1]: - undetcoeff = _undetermined_coefficients_match(r[-1], x) - matching_hints[ - "nth_linear_constant_coeff_variation_of_parameters"] = r - matching_hints["nth_linear_constant_coeff_variation_of_parameters" + - "_Integral"] = r - if undetcoeff['test']: - r['trialset'] = undetcoeff['trialset'] - matching_hints["nth_linear_constant_coeff_undetermined_" + - "coefficients"] = r - # Homogeneous case: F(x) is identically 0 - else: - matching_hints["nth_linear_constant_coeff_homogeneous"] = r - - # Euler equation case (a_i * x**i for all i) - def _test_term(coeff, order): - """ - Linear Euler ODEs have the form K*x**order*diff(y(x),x,order), - where K is independent of x and y(x), order>= 0. - So we need to check that for each term, coeff == K*x**order from - some K. We have a few cases, since coeff may have several - different types. - """ - assert order >= 0 - if coeff == 0: - return True - if order == 0: - if x in coeff.free_symbols: - return False - return f(x) not in coeff.atoms() - if coeff.is_Mul: - if coeff.has(f(x)): - return False - return x**order in coeff.args - elif coeff.is_Pow: - return coeff.as_base_exp() == (x, order) - elif order == 1: - return x == coeff - return False - if r and not any(not _test_term(r[i], i) for i in r if i >= 0): - if not r[-1]: - matching_hints["nth_linear_euler_eq_homogeneous"] = r - - # Order keys based on allhints. - retlist = [] - for i in allhints: - if i in matching_hints: - retlist.append(i) - - if dict: - # Dictionaries are ordered arbitrarily, so we need to make note of which - # hint would come first for dsolve(). In Python 3, this should be replaced - # with an ordered dictionary. - matching_hints["default"] = None - matching_hints["ordered_hints"] = tuple(retlist) - for i in allhints: - if i in matching_hints: - matching_hints["default"] = i - break - return matching_hints - else: - return tuple(retlist) - -
    -@vectorize(0) -
    [docs]def odesimp(eq, func, order, hint): - r""" - Simplifies ODEs, including trying to solve for func and running - constantsimp(). - - It may use knowledge of the type of solution that that hint returns - to apply additional simplifications. - - It also attempts to integrate any Integrals in the expression, if - the hint is not an "_Integral" hint. - - This function should have no effect on expressions returned by - dsolve(), as dsolve already calls odesimp(), but the individual hint - functions do not call odesimp (because the dsolve() wrapper does). - Therefore, this function is designed for mainly internal use. - - Examples - ======== - - >>> from sympy import sin, symbols, dsolve, pprint, Function - >>> from sympy.solvers.ode import odesimp - >>> x , u2, C1= symbols('x,u2,C1') - >>> f = Function('f') - - >>> eq = dsolve(x*f(x).diff(x) - f(x) - x*sin(f(x)/x), f(x), - ... hint='1st_homogeneous_coeff_subs_indep_div_dep_Integral', - ... simplify=False) - >>> pprint(eq) - x - ---- - f(x) - / - | - /f(x)\ | / 1 1 \ - log|----| - | |- -- - -----------| d(u2) = 0 - \ C1 / | | u2 2 /1 \| - | | u2 *sin|--|| - | \ \u2// - | - / - - >>> pprint(odesimp(eq, f(x), 1, - ... hint='1st_homogeneous_coeff_subs_indep_div_dep' - ... )) #doctest: +SKIP - x - --------- = C1 - /f(x)\ - tan|----| - \2*x / - - """ - x = func.args[0] - f = func.func - C1 = Symbol('C1') - - # First, integrate if the hint allows it. - eq = _handle_Integral(eq, func, order, hint) - assert isinstance(eq, Equality) - - # Second, clean up the arbitrary constants. - # Right now, nth linear hints can put as many as 2*order constants in an - # expression. If that number grows with another hint, the third argument - # here should be raised accordingly, or constantsimp() rewritten to handle - # an arbitrary number of constants. - eq = constantsimp(eq, x, 2*order) - - # Lastly, now that we have cleaned up the expression, try solving for func. - # When RootOf is implemented in solve(), we will want to return a RootOf - # everytime instead of an Equality. - - # Get the f(x) on the left if possible. - if eq.rhs == func and not eq.lhs.has(func): - eq = [Eq(eq.rhs, eq.lhs)] - - # make sure we are working with lists of solutions in simplified form. - if eq.lhs == func and not eq.rhs.has(func): - # The solution is already solved - eq = [eq] - - # special simplification of the rhs - if hint.startswith("nth_linear_constant_coeff"): - # Collect terms to make the solution look nice. - # This is also necessary for constantsimp to remove unnecessary terms - # from the particular solution from variation of parameters - global collectterms - assert len(eq) == 1 and eq[0].lhs == f(x) - sol = eq[0].rhs - sol = expand_mul(sol) - for i, reroot, imroot in collectterms: - sol = collect(sol, x**i*exp(reroot*x)*sin(abs(imroot)*x)) - sol = collect(sol, x**i*exp(reroot*x)*cos(imroot*x)) - for i, reroot, imroot in collectterms: - sol = collect(sol, x**i*exp(reroot*x)) - del collectterms - eq[0] = Eq(f(x), sol) - - else: - # The solution is not solved, so try to solve it - try: - eqsol = solve(eq, func) - if not eqsol: - raise NotImplementedError - except NotImplementedError: - eq = [eq] - else: - def _expand(expr): - numer, denom = expr.as_numer_denom() - - if denom.is_Add: - return expr - else: - return powsimp(expr.expand(), combine='exp', deep=True) - - # XXX: the rest of odesimp() expects each ``t`` to be in a - # specific normal form: rational expression with numerator - # expanded, but with combined exponential functions (at - # least in this setup all tests pass). - eq = [Eq(f(x), _expand(t)) for t in eqsol] - - # special simplification of the lhs. - if hint.startswith("1st_homogeneous_coeff"): - for j, eqi in enumerate(eq): - newi = logcombine(eqi, force=True) - if newi.lhs.is_Function and newi.lhs.func is log and newi.rhs == 0: - newi = Eq(newi.lhs.args[0]/C1, C1) - eq[j] = newi - - # We cleaned up the costants before solving to help the solve engine with - # a simpler expression, but the solved expression could have introduced - # things like -C1, so rerun constantsimp() one last time before returning. - for i, eqi in enumerate(eq): - eq[i] = constant_renumber( - constantsimp(eqi, x, 2*order), 'C', 1, 2*order) - - # If there is only 1 solution, return it; - # otherwise return the list of solutions. - if len(eq) == 1: - eq = eq[0] - - return eq - -
    -
    [docs]def checkodesol(ode, sol, func=None, order='auto', solve_for_func=True): - """ - Substitutes sol into the ode and checks that the result is 0. - - This only works when func is one function, like f(x). sol can be a - single solution or a list of solutions. Each solution may be an Equality - that the solution satisfies, e.g. Eq(f(x), C1), Eq(f(x) + C1, 0); or simply - an Expr, e.g. f(x) - C1. In most cases it will not be necessary to - explicitly identify the function, but if the function cannot be inferred - from the original equation it can be supplied through the 'func' argument. - - If a sequence of solutions is passed, the same sort of container will be used - to return the result for each solution. - - It tries the following methods, in order, until it finds zero - equivalence: - - 1. Substitute the solution for f in the original equation. This - only works if the ode is solved for f. It will attempt to solve - it first unless solve_for_func == False - 2. Take n derivatives of the solution, where n is the order of - ode, and check to see if that is equal to the solution. This - only works on exact odes. - 3. Take the 1st, 2nd, ..., nth derivatives of the solution, each - time solving for the derivative of f of that order (this will - always be possible because f is a linear operator). Then back - substitute each derivative into ode in reverse order. - - This function returns a tuple. The first item in the tuple is True - if the substitution results in 0, and False otherwise. The second - item in the tuple is what the substitution results in. It should - always be 0 if the first item is True. Note that sometimes this - function will False, but with an expression that is identically - equal to 0, instead of returning True. This is because simplify() - cannot reduce the expression to 0. If an expression returned by - this function vanishes identically, then sol really is a solution to - ode. - - If this function seems to hang, it is probably because of a hard - simplification. - - To use this function to test, test the first item of the tuple. - - Examples - ======== - - >>> from sympy import Eq, Function, checkodesol, symbols - >>> x, C1 = symbols('x,C1') - >>> f = Function('f') - >>> checkodesol(f(x).diff(x), Eq(f(x), C1)) - (True, 0) - >>> assert checkodesol(f(x).diff(x), C1)[0] - >>> assert not checkodesol(f(x).diff(x), x)[0] - >>> checkodesol(f(x).diff(x, 2), x**2) - (False, 2) - - """ - if not isinstance(ode, Equality): - ode = Eq(ode, 0) - if func is None: - try: - _, func = preprocess(ode.lhs) - except ValueError: - funcs = [s.atoms(AppliedUndef) for s in ( - sol if is_sequence(sol, set) else [sol])] - funcs = reduce(set.union, funcs, set()) - if len(funcs) != 1: - raise ValueError( - 'must pass func arg to checkodesol for this case.') - func = funcs.pop() - # ========== deprecation handling - # After the deprecation period this handling section becomes: - # ---------- - # if not is_unfunc(func) or len(func.args) != 1: - # raise ValueError("func must be a function of one variable, not %s" % func) - # ---------- - # assume, during deprecation that sol and func are reversed - if isinstance(sol, AppliedUndef) and len(sol.args) == 1: - if isinstance(func, AppliedUndef) and len(func.args) == 1: - msg = "If you really do want sol to be just %s, use Eq(%s, 0) " % \ - (sol, sol) + "instead." - else: - msg = "" - SymPyDeprecationWarning(msg, feature="The order of the " - "arguments sol and func to checkodesol()", - useinstead="checkodesol(ode, sol, func)", issue=3384, - ).warn() - sol, func = func, sol - elif not (isinstance(func, AppliedUndef) and len(func.args) == 1): - from sympy.utilities.misc import filldedent - raise ValueError(filldedent(''' - func (or sol, during deprecation) must be a function - of one variable. Got sol = %s, func = %s''' % (sol, func))) - # ========== end of deprecation handling - if is_sequence(sol, set): - return type(sol)([checkodesol(ode, i, order=order, - solve_for_func=solve_for_func) for i in sol]) - - if not isinstance(sol, Equality): - sol = Eq(func, sol) - x = func.args[0] - s = True - testnum = 0 - if order == 'auto': - order = ode_order(ode, func) - if solve_for_func and not (sol.lhs == func and not sol.rhs.has(func)) and not \ - (sol.rhs == func and not sol.lhs.has(func)): - try: - solved = solve(sol, func) - if not solved: - raise NotImplementedError - except NotImplementedError: - pass - else: - if len(solved) == 1: - result = checkodesol(ode, Eq(func, solved[0]), - order=order, solve_for_func=False) - else: - result = checkodesol(ode, [Eq(func, t) for t in solved], - order=order, solve_for_func=False) - - return result - - while s: - if testnum == 0: - # First pass, try substituting a solved solution directly into the ode - # This has the highest chance of succeeding. - ode_diff = ode.lhs - ode.rhs - - if sol.lhs == func: - s = sub_func_doit(ode_diff, func, sol.rhs) - elif sol.rhs == func: - s = sub_func_doit(ode_diff, func, sol.lhs) - else: - testnum += 1 - continue - ss = simplify(s) - if ss: - # with the new numer_denom in power.py, if we do a simple - # expansion then testnum == 0 verifies all solutions. - s = ss.expand() - else: - s = 0 - testnum += 1 - elif testnum == 1: - # Second pass. If we cannot substitute f, try seeing if the nth - # derivative is equal, this will only work for odes that are exact, - # by definition. - s = simplify(trigsimp(diff(sol.lhs, x, order) - diff(sol.rhs, x, order)) - - trigsimp(ode.lhs) + trigsimp(ode.rhs)) -# s2 = simplify(diff(sol.lhs, x, order) - diff(sol.rhs, x, order) - \ -# ode.lhs + ode.rhs) - testnum += 1 - elif testnum == 2: - # Third pass. Try solving for df/dx and substituting that into the ode. - # Thanks to Chris Smith for suggesting this method. Many of the - # comments below are his too. - # The method: - # - Take each of 1..n derivatives of the solution. - # - Solve each nth derivative for d^(n)f/dx^(n) - # (the differential of that order) - # - Back substitute into the ode in decreasing order - # (i.e., n, n-1, ...) - # - Check the result for zero equivalence - if sol.lhs == func and not sol.rhs.has(func): - diffsols = {0: sol.rhs} - elif sol.rhs == func and not sol.lhs.has(func): - diffsols = {0: sol.lhs} - else: - diffsols = {} - sol = sol.lhs - sol.rhs - for i in range(1, order + 1): - # Differentiation is a linear operator, so there should always - # be 1 solution. Nonetheless, we test just to make sure. - # We only need to solve once. After that, we will automatically - # have the solution to the differential in the order we want. - if i == 1: - ds = sol.diff(x) - try: - sdf = solve(ds, func.diff(x, i)) - if not sdf: - raise NotImplementedError - except NotImplementedError: - testnum += 1 - break - else: - diffsols[i] = sdf[0] - else: - # This is what the solution says df/dx should be. - diffsols[i] = diffsols[i - 1].diff(x) - - # Make sure the above didn't fail. - if testnum > 2: - continue - else: - # Substitute it into ode to check for self consistency. - lhs, rhs = ode.lhs, ode.rhs - for i in range(order, -1, -1): - if i == 0 and 0 not in diffsols: - # We can only substitute f(x) if the solution was - # solved for f(x). - break - lhs = sub_func_doit(lhs, func.diff(x, i), diffsols[i]) - rhs = sub_func_doit(rhs, func.diff(x, i), diffsols[i]) - ode_or_bool = Eq(lhs, rhs) - ode_or_bool = simplify(ode_or_bool) - - if isinstance(ode_or_bool, bool): - if ode_or_bool: - lhs = rhs = S.Zero - else: - lhs = ode_or_bool.lhs - rhs = ode_or_bool.rhs - # No sense in overworking simplify--just prove the numerator goes to zero - s = simplify(trigsimp((lhs - rhs).as_numer_denom()[0])) - testnum += 1 - else: - break - - if not s: - return (True, s) - elif s is True: # The code above never was able to change s - raise NotImplementedError("Unable to test if " + str(sol) + - " is a solution to " + str(ode) + ".") - else: - return (False, s) - -
    -
    [docs]def ode_sol_simplicity(sol, func, trysolving=True): - """ - Returns an extended integer representing how simple a solution to an - ODE is. - - The following things are considered, in order from most simple to - least: - - sol is solved for func. - - sol is not solved for func, but can be if passed to solve (e.g., - a solution returned by dsolve(ode, func, simplify=False) - - If sol is not solved for func, then base the result on the length - of sol, as computed by len(str(sol)). - - If sol has any unevaluated Integrals, this will automatically be - considered less simple than any of the above. - - This function returns an integer such that if solution A is simpler - than solution B by above metric, then ode_sol_simplicity(sola, func) - < ode_sol_simplicity(solb, func). - - Currently, the following are the numbers returned, but if the - heuristic is ever improved, this may change. Only the ordering is - guaranteed. - - sol solved for func -2 - sol not solved for func but can be -1 - sol is not solved or solvable for func len(str(sol)) - sol contains an Integral oo - - oo here means the SymPy infinity, which should compare greater than - any integer. - - If you already know solve() cannot solve sol, you can use - trysolving=False to skip that step, which is the only potentially - slow step. For example, dsolve with the simplify=False flag should - do this. - - If sol is a list of solutions, if the worst solution in the list - returns oo it returns that, otherwise it returns len(str(sol)), that - is, the length of the string representation of the whole list. - - Examples - ======== - - This function is designed to be passed to min as the key argument, - such as min(listofsolutions, key=lambda i: ode_sol_simplicity(i, f(x))). - - >>> from sympy import symbols, Function, Eq, tan, cos, sqrt, Integral - >>> from sympy.solvers.ode import ode_sol_simplicity - >>> x, C1, C2 = symbols('x, C1, C2') - >>> f = Function('f') - - >>> ode_sol_simplicity(Eq(f(x), C1*x**2), f(x)) - -2 - >>> ode_sol_simplicity(Eq(x**2 + f(x), C1), f(x)) - -1 - >>> ode_sol_simplicity(Eq(f(x), C1*Integral(2*x, x)), f(x)) - oo - >>> eq1 = Eq(f(x)/tan(f(x)/(2*x)), C1) - >>> eq2 = Eq(f(x)/tan(f(x)/(2*x) + f(x)), C2) - >>> [ode_sol_simplicity(eq, f(x)) for eq in [eq1, eq2]] - [26, 33] - >>> min([eq1, eq2], key=lambda i: ode_sol_simplicity(i, f(x))) - f(x)/tan(f(x)/(2*x)) == C1 - - """ - # TODO: if two solutions are solved for f(x), we still want to be - # able to get the simpler of the two - - # See the docstring for the coercion rules. We check easier (faster) - # things here first, to save time. - - if iterable(sol): - # See if there are Integrals - for i in sol: - if ode_sol_simplicity(i, func, trysolving=trysolving) == oo: - return oo - - return len(str(sol)) - - if sol.has(C.Integral): - return oo - - # Next, try to solve for func. This code will change slightly when RootOf - # is implemented in solve(). Probably a RootOf solution should fall somewhere - # between a normal solution and an unsolvable expression. - - # First, see if they are already solved - if sol.lhs == func and not sol.rhs.has(func) or \ - sol.rhs == func and not sol.lhs.has(func): - return -2 - # We are not so lucky, try solving manually - if trysolving: - try: - sols = solve(sol, func) - if not sols: - raise NotImplementedError - except NotImplementedError: - pass - else: - return -1 - - # Finally, a naive computation based on the length of the string version - # of the expression. This may favor combined fractions because they - # will not have duplicate denominators, and may slightly favor expressions - # with fewer additions and subtractions, as those are separated by spaces - # by the printer. - - # Additional ideas for simplicity heuristics are welcome, like maybe - # checking if a equation has a larger domain, or if constantsimp has - # introduced arbitrary constants numbered higher than the order of a - # given ode that sol is a solution of. - return len(str(sol)) - -
    -@vectorize(0) -
    [docs]def constantsimp(expr, independentsymbol, endnumber, startnumber=1, - symbolname='C'): - """ - Simplifies an expression with arbitrary constants in it. - - This function is written specifically to work with dsolve(), and is - not intended for general use. - - Simplification is done by "absorbing" the arbitrary constants in to - other arbitrary constants, numbers, and symbols that they are not - independent of. - - The symbols must all have the same name with numbers after it, for - example, C1, C2, C3. The symbolname here would be 'C', the - startnumber would be 1, and the end number would be 3. If the - arbitrary constants are independent of the variable x, then the - independent symbol would be x. There is no need to specify the - dependent function, such as f(x), because it already has the - independent symbol, x, in it. - - Because terms are "absorbed" into arbitrary constants and because - constants are renumbered after simplifying, the arbitrary constants - in expr are not necessarily equal to the ones of the same name in - the returned result. - - If two or more arbitrary constants are added, multiplied, or raised - to the power of each other, they are first absorbed together into a - single arbitrary constant. Then the new constant is combined into - other terms if necessary. - - Absorption is done with limited assistance: terms of Adds are collected - to try join constants and powers with exponents that are Adds are expanded - so (C1*cos(x) + C2*cos(x))*exp(x) will simplify to C1*cos(x)*exp(x) and - exp(C1 + x) will be simplified to C1*exp(x). - - Use constant_renumber() to renumber constants after simplification or else - arbitrary numbers on constants may appear, e.g. C1 + C3*x. - - In rare cases, a single constant can be "simplified" into two - constants. Every differential equation solution should have as many - arbitrary constants as the order of the differential equation. The - result here will be technically correct, but it may, for example, - have C1 and C2 in an expression, when C1 is actually equal to C2. - Use your discretion in such situations, and also take advantage of - the ability to use hints in dsolve(). - - Examples - ======== - - >>> from sympy import symbols - >>> from sympy.solvers.ode import constantsimp - >>> C1, C2, C3, x, y = symbols('C1,C2,C3,x,y') - >>> constantsimp(2*C1*x, x, 3) - C1*x - >>> constantsimp(C1 + 2 + x + y, x, 3) - C1 + x - >>> constantsimp(C1*C2 + 2 + x + y + C3*x, x, 3) - C1 + C3*x - - """ - # This function works recursively. The idea is that, for Mul, - # Add, Pow, and Function, if the class has a constant in it, then - # we can simplify it, which we do by recursing down and - # simplifying up. Otherwise, we can skip that part of the - # expression. - - constant_iter = numbered_symbols('C', start=startnumber) - constantsymbols = [next(constant_iter) for t in range(startnumber, - endnumber + 1)] - constantsymbols_set = set(constantsymbols) - x = independentsymbol - if isinstance(expr, Equality): - # For now, only treat the special case where one side of the equation - # is a constant - if expr.lhs in constantsymbols_set: - return Eq(expr.lhs, constantsimp(expr.rhs + expr.lhs, x, endnumber, - startnumber, symbolname) - expr.lhs) - # this could break if expr.lhs is absorbed into another constant, - # but for now, the only solutions that return Eq's with a constant - # on one side are first order. At any rate, it will still be - # technically correct. The expression will just have too many - # constants in it - elif expr.rhs in constantsymbols_set: - return Eq(constantsimp(expr.lhs + expr.rhs, x, endnumber, - startnumber, symbolname) - expr.rhs, expr.rhs) - else: - return Eq(constantsimp(expr.lhs, x, endnumber, startnumber, - symbolname), constantsimp(expr.rhs, x, endnumber, - startnumber, symbolname)) - - if not expr.has(*constantsymbols): - return expr - else: - # ================ pre-processing ================ - # collect terms to get constants together - def _take(i): - t = sorted([s for s in i.atoms(Symbol) if s in constantsymbols]) - if not t: - return i - return t[0] - - if not (expr.has(x) and x in expr.free_symbols): - return constantsymbols[0] - new_expr = terms_gcd(expr, clear=False, deep=True) - if new_expr.is_Mul: - # don't let C1*exp(x) + C2*exp(2*x) become exp(x)*(C1 + C2*exp(x)) - infac = False - asfac = False - for m in new_expr.args: - if m.func is exp: - asfac = True - elif m.is_Add: - infac = any(fi.func is exp for t in m.args for fi in Mul.make_args(t)) - if asfac and infac: - new_expr = expr - break - expr = new_expr - # don't allow a number to be factored out of an expression - # that has no denominator - if expr.is_Mul: - h, t = expr.as_coeff_Mul() - if h != 1 and (t.is_Add or denom(t) == 1): - args = list(Mul.make_args(t)) - for i, a in enumerate(args): - if a.is_Add: - args[i] = h*a - expr = Mul._from_args(args) - break - # let numbers absorb into constants of an Add, perhaps - # in the base of a power, if all its terms have a constant - # symbol in them, e.g. sqrt(2)*(C1 + C2*x) -> C1 + C2*x - if expr.is_Mul: - d = sift(expr.args, lambda m: m.is_number is True) - num = d[True] - other = d[False] - con_set = set(constantsymbols) - if num: - for o in other: - b, e = o.as_base_exp() - if b.is_Add and \ - all(a.args_cnc(cset=True, warn=False)[0] & - con_set for a in b.args): - expr = sign(Mul(*num))*Mul._from_args(other) - break - if expr.is_Mul: # check again that it's still a Mul - i, d = expr.as_independent(x, strict=True) - newi = _take(i) - if newi != i: - expr = newi*d - elif expr.is_Add: - i, d = expr.as_independent(x, strict=True) - expr = _take(i) + d - if expr.is_Add: - terms = {} - for ai in expr.args: - i, d = ai.as_independent(x, strict=True, as_Add=False) - terms.setdefault(d, []).append(i) - expr = Add(*[k*Add(*v) for k, v in list(terms.items())]) - # handle powers like exp(C0 + g(x)) -> C0*exp(g(x)) - pows = [p for p in expr.atoms(C.Function, C.Pow) if - (p.is_Pow or p.func is exp) and - p.exp.is_Add and - p.exp.as_independent(x, strict=True)[1]] - if pows: - reps = [] - for p in pows: - b, e = p.as_base_exp() - ei, ed = e.as_independent(x, strict=True) - e = _take(ei) - if e != ei or e in constantsymbols: - reps.append((p, e*b**ed)) - expr = expr.subs(reps) - # a C1*C2 may have been introduced and the code below won't - # handle that so handle it now: once to handle the C1*C2 - # and once to handle any C0*f(x) + C0*f(x) - for _ in range(2): - muls = [m for m in expr.atoms(Mul) if m.has(*constantsymbols)] - reps = [] - for m in muls: - i, d = m.as_independent(x, strict=True) - newi = _take(i) - if newi != i: - reps.append((m, _take(i)*d)) - expr = expr.subs(reps) - # ================ end of pre-processing ================ - newargs = [] - hasconst = False - isPowExp = False - reeval = False - for i in expr.args: - if i not in constantsymbols: - newargs.append(i) - else: - newconst = i - hasconst = True - if expr.is_Pow and i == expr.exp: - isPowExp = True - - for i in range(len(newargs)): - isimp = constantsimp(newargs[i], x, endnumber, startnumber, - symbolname) - if isimp in constantsymbols: - reeval = True - hasconst = True - newconst = isimp - if expr.is_Pow and i == 1: - isPowExp = True - newargs[i] = isimp - if hasconst: - newargs = [i for i in newargs if i.has(x)] - if isPowExp: - newargs = newargs + [newconst] # Order matters in this case - else: - newargs = [newconst] + newargs - if expr.is_Pow and len(newargs) == 1: - newargs.append(S.One) - if expr.is_Function: - if (len(newargs) == 0 or hasconst and len(newargs) == 1): - return newconst - else: - newfuncargs = [constantsimp(t, x, endnumber, startnumber, - symbolname) for t in expr.args] - return expr.func(*newfuncargs) - else: - newexpr = expr.func(*newargs) - if reeval: - return constantsimp(newexpr, x, endnumber, startnumber, - symbolname) - else: - return newexpr - -
    -
    [docs]def constant_renumber(expr, symbolname, startnumber, endnumber): - """ - Renumber arbitrary constants in expr to have numbers 1 through N - where N is ``endnumber`` - ``startnumber`` + 1 at most. - - This is a simple function that goes through and renumbers any Symbol - with a name in the form symbolname + num where num is in the range - from startnumber to endnumber. - - Symbols are renumbered based on ``.sort_key()``, so they should be - numbered roughly in the order that they appear in the final, printed - expression. Note that this ordering is based in part on hashes, so - it can produce different results on different machines. - - The structure of this function is very similar to that of - constantsimp(). - - Examples - ======== - - >>> from sympy import symbols, Eq, pprint - >>> from sympy.solvers.ode import constant_renumber - >>> x, C0, C1, C2, C3, C4 = symbols('x,C:5') - - Only constants in the given range (inclusive) are renumbered; - the renumbering always starts from 1: - - >>> constant_renumber(C1 + C3 + C4, 'C', 1, 3) - C1 + C2 + C4 - >>> constant_renumber(C0 + C1 + C3 + C4, 'C', 2, 4) - C0 + 2*C1 + C2 - >>> constant_renumber(C0 + 2*C1 + C2, 'C', 0, 1) - C1 + 3*C2 - >>> pprint(C2 + C1*x + C3*x**2) - 2 - C1*x + C2 + C3*x - >>> pprint(constant_renumber(C2 + C1*x + C3*x**2, 'C', 1, 3)) - 2 - C1 + C2*x + C3*x - - """ - if type(expr) in (set, list, tuple): - return type( - expr)([constant_renumber(i, symbolname=symbolname, - startnumber=startnumber, endnumber=endnumber) for i in expr]) - global newstartnumber - newstartnumber = 1 - - def _constant_renumber(expr, symbolname, startnumber, endnumber): - """ - We need to have an internal recursive function so that - newstartnumber maintains its values throughout recursive calls. - - """ - constantsymbols = [Symbol( - symbolname + "%d" % t) for t in range(startnumber, - endnumber + 1)] - global newstartnumber - - if isinstance(expr, Equality): - return Eq( - _constant_renumber( - expr.lhs, symbolname, startnumber, endnumber), - _constant_renumber(expr.rhs, symbolname, startnumber, endnumber)) - - if type(expr) not in (Mul, Add, Pow) and not expr.is_Function and \ - not expr.has(*constantsymbols): - # Base case, as above. We better hope there aren't constants inside - # of some other class, because they won't be renumbered. - return expr - elif expr in constantsymbols: - # Renumbering happens here - newconst = Symbol(symbolname + str(newstartnumber)) - newstartnumber += 1 - return newconst - else: - if expr.is_Function or expr.is_Pow: - return expr.func( - *[_constant_renumber(x, symbolname, startnumber, - endnumber) for x in expr.args]) - else: - sortedargs = list(expr.args) - # make a mapping to send all constantsymbols to S.One and use - # that to make sure that term ordering is not dependent on - # the indexed value of C - C_1 = [(ci, S.One) for ci in constantsymbols] - sortedargs.sort( - key=lambda arg: default_sort_key(arg.subs(C_1))) - return expr.func( - *[_constant_renumber(x, symbolname, startnumber, - endnumber) for x in sortedargs]) - - return _constant_renumber(expr, symbolname, startnumber, endnumber) - -
    -def _handle_Integral(expr, func, order, hint): - """ - Converts a solution with Integrals in it into an actual solution. - - For most hints, this simply runs expr.doit() - - """ - x = func.args[0] - f = func.func - if hint == "1st_exact": - global exactvars - x0 = exactvars['x0'] - y0 = exactvars['y0'] - y = exactvars['y'] - tmpsol = expr.lhs.doit() - sol = 0 - assert tmpsol.is_Add - for i in tmpsol.args: - if not i.has(x0) and not i.has(y0): - sol += i - assert sol != 0 - sol = Eq(sol.subs(y, f(x)), expr.rhs) # expr.rhs == C1 - del exactvars - elif hint == "1st_exact_Integral": - # FIXME: We still need to back substitute y - # y = exactvars['y'] - # sol = expr.subs(y, f(x)) - # For now, we are going to have to return an expression with f(x) replaced - # with y. Substituting results in the y's in the second integral - # becoming f(x), which prevents the integral from being evaluatable. - # For example, Integral(cos(f(x)), (x, x0, x)). If there were a way to - # do inert substitution, that could maybe be used here instead. - del exactvars - sol = expr - elif hint == "nth_linear_constant_coeff_homogeneous": - sol = expr - elif not hint.endswith("_Integral"): - sol = expr.doit() - else: - sol = expr - return sol - - -
    [docs]def ode_order(expr, func): - """ - Returns the order of a given ODE with respect to func. - - This function is implemented recursively. - - Examples - ======== - - >>> from sympy import Function, ode_order - >>> from sympy.abc import x - >>> f, g = list(map(Function, ['f', 'g'])) - >>> ode_order(f(x).diff(x, 2) + f(x).diff(x)**2 + - ... f(x).diff(x), f(x)) - 2 - >>> ode_order(f(x).diff(x, 2) + g(x).diff(x, 3), f(x)) - 2 - >>> ode_order(f(x).diff(x, 2) + g(x).diff(x, 3), g(x)) - 3 - - """ - a = Wild('a', exclude=[func]) - if expr.match(a): - return 0 - - if isinstance(expr, Derivative): - if expr.args[0] == func: - return len(expr.variables) - else: - order = 0 - for arg in expr.args[0].args: - order = max(order, ode_order(arg, func) + len(expr.variables)) - return order - else: - order = 0 - for arg in expr.args: - order = max(order, ode_order(arg, func)) - return order - - -# FIXME: replace the general solution in the docstring with -# dsolve(equation, hint='1st_exact_Integral'). You will need to be able -# to have assumptions on P and Q that dP/dy = dQ/dx.
    -
    [docs]def ode_1st_exact(eq, func, order, match): - r""" - Solves 1st order exact ordinary differential equations. - - A 1st order differential equation is called exact if it is the total - differential of a function. That is, the differential equation - P(x, y)dx + Q(x, y)dy = 0 is exact if there is some function F(x, y) - such that P(x, y) = dF/dx and Q(x, y) = dF/dy (d here refers to the - partial derivative). It can be shown that a necessary and - sufficient condition for a first order ODE to be exact is that - dP/dy = dQ/dx. Then, the solution will be as given below:: - - >>> from sympy import Function, Eq, Integral, symbols, pprint - >>> x, y, t, x0, y0, C1= symbols('x,y,t,x0,y0,C1') - >>> P, Q, F= list(map(Function, ['P', 'Q', 'F'])) - >>> pprint(Eq(Eq(F(x, y), Integral(P(t, y), (t, x0, x)) + - ... Integral(Q(x0, t), (t, y0, y))), C1)) - x y - / / - | | - F(x, y) = | P(t, y) dt + | Q(x0, t) dt = C1 - | | - / / - x0 y0 - - Where the first partials of P and Q exist and are continuous in a - simply connected region. - - A note: SymPy currently has no way to represent inert substitution on - an expression, so the hint '1st_exact_Integral' will return an integral - with dy. This is supposed to represent the function that you are - solving for. - - Examples - ======== - - >>> from sympy import Function, dsolve, cos, sin - >>> from sympy.abc import x - >>> f = Function('f') - >>> dsolve(cos(f(x)) - (x*sin(f(x)) - f(x)**2)*f(x).diff(x), - ... f(x), hint='1st_exact') - x*cos(f(x)) + f(x)**3/3 == C1 - - References - ========== - - - http://en.wikipedia.org/wiki/Exact_differential_equation - - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations", - Dover 1963, pp. 73 - - # indirect doctest - - """ - x = func.args[0] - f = func.func - r = match # d+e*diff(f(x),x) - C1 = Symbol('C1') - x0 = Dummy('x0') - y0 = Dummy('y0') - global exactvars # This is the only way to pass these dummy variables to - # _handle_Integral - exactvars = {'y0': y0, 'x0': x0, 'y': r['y']} - # If we ever get a Constant class, x0 and y0 should be constants, I think - sol = C.Integral(r[r['e']].subs( - x, x0), (r['y'], y0, f(x))) + C.Integral(r[r['d']], (x, x0, x)) - return Eq(sol, C1) - -
    -
    [docs]def ode_1st_homogeneous_coeff_best(eq, func, order, match): - r""" - Returns the best solution to an ODE from the two hints - '1st_homogeneous_coeff_subs_dep_div_indep' and - '1st_homogeneous_coeff_subs_indep_div_dep'. - - This is as determined by ode_sol_simplicity(). - - See the ode_1st_homogeneous_coeff_subs_indep_div_dep() and - ode_1st_homogeneous_coeff_subs_dep_div_indep() docstrings for more - information on these hints. Note that there is no - '1st_homogeneous_coeff_best_Integral' hint. - - Examples - ======== - - >>> from sympy import Function, dsolve, pprint - >>> from sympy.abc import x - >>> f = Function('f') - >>> pprint(dsolve(2*x*f(x) + (x**2 + f(x)**2)*f(x).diff(x), f(x), - ... hint='1st_homogeneous_coeff_best', simplify=False)) - / 2 \ - | 3*x | - log|----- + 1| - | 2 | - /f(x)\ \f (x) / - log|----| + -------------- = 0 - \ C1 / 3 - - References - ========== - - - http://en.wikipedia.org/wiki/Homogeneous_differential_equation - - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations", - Dover 1963, pp. 59 - - # indirect doctest - - """ - # There are two substitutions that solve the equation, u1=y/x and u2=x/y - # They produce different integrals, so try them both and see which - # one is easier. - sol1 = ode_1st_homogeneous_coeff_subs_indep_div_dep(eq, - func, order, match) - sol2 = ode_1st_homogeneous_coeff_subs_dep_div_indep(eq, - func, order, match) - simplify = match.get('simplify', True) - if simplify: - sol1 = odesimp( - sol1, func, order, "1st_homogeneous_coeff_subs_indep_div_dep") - sol2 = odesimp( - sol2, func, order, "1st_homogeneous_coeff_subs_dep_div_indep") - return min([sol1, sol2], key=lambda x: ode_sol_simplicity(x, func, - trysolving=not simplify)) - -
    -
    [docs]def ode_1st_homogeneous_coeff_subs_dep_div_indep(eq, func, order, match): - r""" - Solves a 1st order differential equation with homogeneous coefficients - using the substitution - u1 = <dependent variable>/<independent variable>. - - This is a differential equation P(x, y) + Q(x, y)dy/dx = 0, that P - and Q are homogeneous of the same order. A function F(x, y) is - homogeneous of order n if F(xt, yt) = t**n*F(x, y). Equivalently, - F(x, y) can be rewritten as G(y/x) or H(x/y). See also the - docstring of homogeneous_order(). - - If the coefficients P and Q in the differential equation above are - homogeneous functions of the same order, then it can be shown that - the substitution y = u1*x (u1 = y/x) will turn the differential - equation into an equation separable in the variables x and u. If - h(u1) is the function that results from making the substitution - u1 = f(x)/x on P(x, f(x)) and g(u2) is the function that results - from the substitution on Q(x, f(x)) in the differential equation - P(x, f(x)) + Q(x, f(x))*diff(f(x), x) = 0, then the general solution - is:: - - >>> from sympy import Function, dsolve, pprint - >>> from sympy.abc import x - >>> f, g, h = list(map(Function, ['f', 'g', 'h'])) - >>> genform = g(f(x)/x) + h(f(x)/x)*f(x).diff(x) - >>> pprint(genform) - /f(x)\ /f(x)\ d - g|----| + h|----|*--(f(x)) - \ x / \ x / dx - >>> pprint(dsolve(genform, f(x), - ... hint='1st_homogeneous_coeff_subs_dep_div_indep_Integral')) - f(x) - ---- - x - / - | - | -h(u1) - log(C1*x) - | ---------------- d(u1) = 0 - | u1*h(u1) + g(u1) - | - / - - Where u1*h(u1) + g(u1) != 0 and x != 0. - - See also the docstrings of ode_1st_homogeneous_coeff_best() and - ode_1st_homogeneous_coeff_subs_indep_div_dep(). - - Examples - ======== - - >>> from sympy import Function, dsolve - >>> from sympy.abc import x - >>> f = Function('f') - >>> pprint(dsolve(2*x*f(x) + (x**2 + f(x)**2)*f(x).diff(x), f(x), - ... hint='1st_homogeneous_coeff_subs_dep_div_indep', simplify=False)) - / 3 \ - |3*f(x) f (x)| - log|------ + -----| - | x 3 | - /x \ \ x / - log|--| + ------------------- = 0 - \C1/ 3 - - References - ========== - - - http://en.wikipedia.org/wiki/Homogeneous_differential_equation - - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations", - Dover 1963, pp. 59 - - # indirect doctest - - """ - x = func.args[0] - f = func.func - u1 = Dummy('u1') # u1 == f(x)/x - r = match # d+e*diff(f(x),x) - C1 = Symbol('C1') - int = C.Integral( - (-r[r['e']]/(r[r['d']] + u1*r[r['e']])).subs({x: 1, r['y']: u1}), - (u1, None, f(x)/x)) - sol = logcombine(Eq(log(x), int + log(C1)), force=True) - return sol - -
    -
    [docs]def ode_1st_homogeneous_coeff_subs_indep_div_dep(eq, func, order, match): - r""" - Solves a 1st order differential equation with homogeneous coefficients - using the substitution - u2 = <independent variable>/<dependent variable>. - - This is a differential equation P(x, y) + Q(x, y)dy/dx = 0, that P - and Q are homogeneous of the same order. A function F(x, y) is - homogeneous of order n if F(xt, yt) = t**n*F(x, y). Equivalently, - F(x, y) can be rewritten as G(y/x) or H(x/y). See also the - docstring of homogeneous_order(). - - If the coefficients P and Q in the differential equation above are - homogeneous functions of the same order, then it can be shown that - the substitution x = u2*y (u2 = x/y) will turn the differential - equation into an equation separable in the variables y and u2. If - h(u2) is the function that results from making the substitution - u2 = x/f(x) on P(x, f(x)) and g(u2) is the function that results - from the substitution on Q(x, f(x)) in the differential equation - P(x, f(x)) + Q(x, f(x))*diff(f(x), x) = 0, then the general solution - is: - - >>> from sympy import Function, dsolve, pprint - >>> from sympy.abc import x - >>> f, g, h = list(map(Function, ['f', 'g', 'h'])) - >>> genform = g(x/f(x)) + h(x/f(x))*f(x).diff(x) - >>> pprint(genform) - / x \ / x \ d - g|----| + h|----|*--(f(x)) - \f(x)/ \f(x)/ dx - >>> pprint(dsolve(genform, f(x), - ... hint='1st_homogeneous_coeff_subs_indep_div_dep_Integral')) - x - ---- - f(x) - / - | - | g(u2) - | ----------------- d(u2) - | -u2*g(u2) - h(u2) - | - / - <BLANKLINE> - f(x) = C1*e - - Where u2*g(u2) + h(u2) != 0 and f(x) != 0. - - See also the docstrings of ode_1st_homogeneous_coeff_best() and - ode_1st_homogeneous_coeff_subs_dep_div_indep(). - - Examples - ======== - - >>> from sympy import Function, pprint - >>> from sympy.abc import x - >>> f = Function('f') - >>> pprint(dsolve(2*x*f(x) + (x**2 + f(x)**2)*f(x).diff(x), f(x), - ... hint='1st_homogeneous_coeff_subs_indep_div_dep')) - ___________ - / 2 - / 3*x - / ----- + 1 *f(x) = C1 - 3 / 2 - \/ f (x) - - References - ========== - - - http://en.wikipedia.org/wiki/Homogeneous_differential_equation - - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations", - Dover 1963, pp. 59 - - # indirect doctest - - """ - x = func.args[0] - f = func.func - u2 = Dummy('u2') # u2 == x/f(x) - r = match # d+e*diff(f(x),x) - C1 = Symbol('C1') - int = C.Integral( - simplify( - (-r[r['d']]/(r[r['e']] + u2*r[r['d']])).subs({x: u2, r['y']: 1})), - (u2, None, x/f(x))) - sol = logcombine(Eq(log(f(x)), int + log(C1)), force=True) - return sol - -# XXX: Should this function maybe go somewhere else? - -
    -
    [docs]def homogeneous_order(eq, *symbols): - """ - Returns the order n if g is homogeneous and None if it is not - homogeneous. - - Determines if a function is homogeneous and if so of what order. - A function f(x,y,...) is homogeneous of order n if - f(t*x,t*y,t*...) == t**n*f(x,y,...). - - If the function is of two variables, F(x, y), then f being - homogeneous of any order is equivalent to being able to rewrite - F(x, y) as G(x/y) or H(y/x). This fact is used to solve 1st order - ordinary differential equations whose coefficients are homogeneous - of the same order (see the docstrings of - ode.ode_1st_homogeneous_coeff_subs_indep_div_dep() and - ode.ode_1st_homogeneous_coeff_subs_indep_div_dep() - - Symbols can be functions, but every argument of the function must be - a symbol, and the arguments of the function that appear in the - expression must match those given in the list of symbols. If a - declared function appears with different arguments than given in the - list of symbols, None is returned. - - Examples - ======== - - >>> from sympy import Function, homogeneous_order, sqrt - >>> from sympy.abc import x, y - >>> f = Function('f') - >>> homogeneous_order(f(x), f(x)) is None - True - >>> homogeneous_order(f(x,y), f(y, x), x, y) is None - True - >>> homogeneous_order(f(x), f(x), x) - 1 - >>> homogeneous_order(x**2*f(x)/sqrt(x**2+f(x)**2), x, f(x)) - 2 - >>> homogeneous_order(x**2+f(x), x, f(x)) is None - True - - """ - from sympy.simplify.simplify import separatevars - - if not symbols: - raise ValueError("homogeneous_order: no symbols were given.") - symset = set(symbols) - eq = sympify(eq) - - # The following are not supported - if eq.has(Order, Derivative): - return None - - # These are all constants - if (eq.is_Number or - eq.is_NumberSymbol or - eq.is_number - ): - return S.Zero - - # Replace all functions with dummy variables - dum = numbered_symbols(prefix='d', cls=Dummy) - newsyms = set() - for i in [j for j in symset if getattr(j, 'is_Function')]: - iargs = set(i.args) - if iargs.difference(symset): - return None - else: - dummyvar = next(dum) - eq = eq.subs(i, dummyvar) - symset.remove(i) - newsyms.add(dummyvar) - symset.update(newsyms) - - if not eq.free_symbols & symset: - return None - - # make the replacement of x with x*t and see if t can be factored out - t = Dummy('t', positive=True) # It is sufficient that t > 0 - eqs = separatevars(eq.subs([(i, t*i) for i in symset]), [t], dict=True)[t] - if eqs is S.One: - return S.Zero # there was no term with only t - i, d = eqs.as_independent(t, as_Add=False) - b, e = d.as_base_exp() - if b == t: - return e - -
    -
    [docs]def ode_1st_linear(eq, func, order, match): - r""" - Solves 1st order linear differential equations. - - These are differential equations of the form dy/dx _ P(x)*y = Q(x). - These kinds of differential equations can be solved in a general - way. The integrating factor exp(Integral(P(x), x)) will turn the - equation into a separable equation. The general solution is:: - - >>> from sympy import Function, dsolve, Eq, pprint, diff, sin - >>> from sympy.abc import x - >>> f, P, Q = list(map(Function, ['f', 'P', 'Q'])) - >>> genform = Eq(f(x).diff(x) + P(x)*f(x), Q(x)) - >>> pprint(genform) - d - P(x)*f(x) + --(f(x)) = Q(x) - dx - >>> pprint(dsolve(genform, f(x), hint='1st_linear_Integral')) - / / \ - | | | - | | / | / - | | | | | - | | | P(x) dx | - | P(x) dx - | | | | | - | | / | / - f(x) = |C1 + | Q(x)*e dx|*e - | | | - \ / / - - - Examples - ======== - - >>> f = Function('f') - >>> pprint(dsolve(Eq(x*diff(f(x), x) - f(x), x**2*sin(x)), - ... f(x), '1st_linear')) - f(x) = x*(C1 - cos(x)) - - References - ========== - - - http://en.wikipedia.org/wiki/Linear_differential_equation#First_order_equation - - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations", - Dover 1963, pp. 92 - - # indirect doctest - - """ - x = func.args[0] - f = func.func - r = match # a*diff(f(x),x) + b*f(x) + c - C1 = Symbol('C1') - t = exp(C.Integral(r[r['b']]/r[r['a']], x)) - tt = C.Integral(t*(-r[r['c']]/r[r['a']]), x) - return Eq(f(x), (tt + C1)/t) - -
    -
    [docs]def ode_Bernoulli(eq, func, order, match): - r""" - Solves Bernoulli differential equations. - - These are equations of the form dy/dx + P(x)*y = Q(x)*y**n, n != 1. - The substitution w = 1/y**(1-n) will transform an equation of this - form into one that is linear (see the docstring of - ode_1st_linear()). The general solution is:: - - >>> from sympy import Function, dsolve, Eq, pprint - >>> from sympy.abc import x, n - >>> f, P, Q = list(map(Function, ['f', 'P', 'Q'])) - >>> genform = Eq(f(x).diff(x) + P(x)*f(x), Q(x)*f(x)**n) - >>> pprint(genform) - d n - P(x)*f(x) + --(f(x)) = Q(x)*f (x) - dx - >>> pprint(dsolve(genform, f(x), hint='Bernoulli_Integral')) #doctest: +SKIP - 1 - ---- - 1 - n - // / \ \ - || | | | - || | / | / | - || | | | | | - || | (1 - n)* | P(x) dx | (-1 + n)* | P(x) dx| - || | | | | | - || | / | / | - f(x) = ||C1 + (-1 + n)* | -Q(x)*e dx|*e | - || | | | - \\ / / / - - - Note that when n = 1, then the equation is separable (see the - docstring of ode_separable()). - - >>> pprint(dsolve(Eq(f(x).diff(x) + P(x)*f(x), Q(x)*f(x)), f(x), - ... hint='separable_Integral')) - f(x) - / - | / - | 1 | - | - dy = C1 + | (-P(x) + Q(x)) dx - | y | - | / - / - - - Examples - ======== - - >>> from sympy import Function, dsolve, Eq, pprint, log - >>> from sympy.abc import x - >>> f = Function('f') - - >>> pprint(dsolve(Eq(x*f(x).diff(x) + f(x), log(x)*f(x)**2), - ... f(x), hint='Bernoulli')) - 1 - f(x) = ------------------- - / log(x) 1\ - x*|C1 + ------ + -| - \ x x/ - - References - ========== - - - http://en.wikipedia.org/wiki/Bernoulli_differential_equation - - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations", - Dover 1963, pp. 95 - - # indirect doctest - - """ - x = func.args[0] - f = func.func - r = match # a*diff(f(x),x) + b*f(x) + c*f(x)**n, n != 1 - C1 = Symbol('C1') - t = exp((1 - r[r['n']])*C.Integral(r[r['b']]/r[r['a']], x)) - tt = (r[r['n']] - 1)*C.Integral(t*r[r['c']]/r[r['a']], x) - return Eq(f(x), ((tt + C1)/t)**(1/(1 - r[r['n']]))) - -
    -
    [docs]def ode_Riccati_special_minus2(eq, func, order, match): - r""" - The general Riccati equation has the form dy/dx = f(x)*y**2 + g(x)*y + h(x). - While it does not have a general solution [1], the "special" form, - dy/dx = a*y**2 - b*x**c, does have solutions in many cases [2]. This routine - returns a solution for a*dy/dx = b*y**2 + c*y/x + d/x**2 that is obtained by - using a suitable change of variables to reduce it to the special form and is - valid when neither a nor b are zero and either c or d is zero. - - >>> from sympy.abc import x, y, a, b, c, d - >>> from sympy.solvers.ode import dsolve, checkodesol - >>> from sympy import pprint, Function - >>> f = Function('f') - >>> y = f(x) - >>> genform = a*y.diff(x) - (b*y**2 + c*y/x + d/x**2) - >>> sol = dsolve(genform, y) - >>> pprint(sol) - / / __________________ \\ - | __________________ | / 2 || - | / 2 | \/ 4*b*d - (a + c) *log(x)|| - -|a + c - \/ 4*b*d - (a + c) *tan|C1 + ----------------------------|| - \ \ 2*a // - f(x) = ----------------------------------------------------------------------- - 2*b*x - - >>> checkodesol(genform, sol, order=1)[0] - True - - References - ========== - - 1. http://www.maplesoft.com/support/help/Maple/view.aspx?path=odeadvisor/Riccati - 2. http://eqworld.ipmnet.ru/en/solutions/ode/ode0106.pdf - - http://eqworld.ipmnet.ru/en/solutions/ode/ode0123.pdf - """ - - x = func.args[0] - f = func.func - r = match # a2*diff(f(x),x) + b2*f(x) + c2*f(x)/x + d2/x**2 - a2, b2, c2, d2 = [r[r[s]] for s in 'a2 b2 c2 d2'.split()] - C1 = Symbol('C1') - mu = sqrt(4*d2*b2 - (a2 - c2)**2) - return Eq(f(x), (a2 - c2 - mu*tan(mu/(2*a2)*log(x) + C1))/(2*b2*x)) - -
    -
    [docs]def ode_Liouville(eq, func, order, match): - r""" - Solves 2nd order Liouville differential equations. - - The general form of a Liouville ODE is - d^2y/dx^2 + g(y)*(dy/dx)**2 + h(x)*dy/dx. The general solution is: - - >>> from sympy import Function, dsolve, Eq, pprint, diff - >>> from sympy.abc import x - >>> f, g, h = list(map(Function, ['f', 'g', 'h'])) - >>> genform = Eq(diff(f(x),x,x) + g(f(x))*diff(f(x),x)**2 + - ... h(x)*diff(f(x),x), 0) - >>> pprint(genform) - 2 2 - d d d - g(f(x))*--(f(x)) + h(x)*--(f(x)) + ---(f(x)) = 0 - dx dx 2 - dx - >>> pprint(dsolve(genform, f(x), hint='Liouville_Integral')) - f(x) - / / - | | - | / | / - | | | | - | - | h(x) dx | | g(y) dy - | | | | - | / | / - C1 + C2* | e dx + | e dy = 0 - | | - / / - - Examples - ======== - - >>> from sympy import Function, dsolve, Eq, pprint - >>> from sympy.abc import x - >>> f = Function('f') - >>> pprint(dsolve(diff(f(x), x, x) + diff(f(x), x)**2/f(x) + - ... diff(f(x), x)/x, f(x), hint='Liouville')) - ________________ ________________ - [f(x) = -\/ C1 + C2*log(x) , f(x) = \/ C1 + C2*log(x) ] - - References - ========== - - - Goldstein and Braun, "Advanced Methods for the Solution of - Differential Equations", pp. 98 - - http://www.maplesoft.com/support/help/Maple/view.aspx?path=odeadvisor/Liouville - - # indirect doctest - - """ - # Liouville ODE f(x).diff(x, 2) + g(f(x))*(f(x).diff(x, 2))**2 + h(x)*f(x).diff(x) - # See Goldstein and Braun, "Advanced Methods for the Solution of - # Differential Equations", pg. 98, as well as - # http://www.maplesoft.com/support/help/view.aspx?path=odeadvisor/Liouville - x = func.args[0] - f = func.func - r = match # f(x).diff(x, 2) + g*f(x).diff(x)**2 + h*f(x).diff(x) - y = r['y'] - C1 = Symbol('C1') - C2 = Symbol('C2') - int = C.Integral(exp(C.Integral(r['g'], y)), (y, None, f(x))) - sol = Eq(int + C1*C.Integral(exp(-C.Integral(r['h'], x)), x) + C2, 0) - return sol - -
    -def _nth_linear_match(eq, func, order): - """ - Matches a differential equation to the linear form: - - a_n(x)y^(n) + ... + a_1(x)y' + a_0(x)y + B(x) = 0 - - Returns a dict of order:coeff terms, where order is the order of the - derivative on each term, and coeff is the coefficient of that - derivative. The key -1 holds the function B(x). Returns None if - the ode is not linear. This function assumes that func has already - been checked to be good. - - Examples - ======== - - >>> from sympy import Function, cos, sin - >>> from sympy.abc import x - >>> from sympy.solvers.ode import _nth_linear_match - >>> f = Function('f') - >>> _nth_linear_match(f(x).diff(x, 3) + 2*f(x).diff(x) + - ... x*f(x).diff(x, 2) + cos(x)*f(x).diff(x) + x - f(x) - - ... sin(x), f(x), 3) - {-1: x - sin(x), 0: -1, 1: cos(x) + 2, 2: x, 3: 1} - >>> _nth_linear_match(f(x).diff(x, 3) + 2*f(x).diff(x) + - ... x*f(x).diff(x, 2) + cos(x)*f(x).diff(x) + x - f(x) - - ... sin(f(x)), f(x), 3) == None - True - - """ - x = func.args[0] - one_x = set([x]) - terms = dict([(i, S.Zero) for i in range(-1, order + 1)]) - for i in Add.make_args(eq): - if not i.has(func): - terms[-1] += i - else: - c, f = i.as_independent(func) - if not ((isinstance(f, Derivative) and set(f.variables) == one_x) or - f == func): - return None - else: - terms[len(f.args[1:])] += c - return terms - - -def ode_nth_linear_euler_eq_homogeneous(eq, func, order, match, returns='sol'): - r""" - Solves an nth order linear homogeneous variable-coefficient - Cauchy-Euler equidimensional ordinary differential equation. - - This is an equation with form 0 = a0*f(x) + a1*x*f'(x) + a2*x**2*f"(x)... - - These equations can be solved in a general manner, by substituting - solutions of the form f(x) = x**r, and deriving a characteristic equation - for r. When there are repeated roots, we include extra terms of the form - Crk*ln(x)**k*x**r, where Cnk is an arbitrary integration constant, r is a - root of the characteristic equation, and k ranges over the multiplicity of - r. In the cases where the roots are complex, solutions of the form - C1*x**a*sin(b*log(x)) + C2*x**a*cos(b*log(x)) are returned, based on - expansions with Eulers formula. The general solution is the sum of the - terms found. If SymPy cannot find exact roots to the characteristic - equation, a RootOf instance will be return in its stead. - - >>> from sympy import Function, dsolve, Eq - >>> from sympy.abc import x - >>> f = Function('f') - >>> dsolve(4*x**2*f(x).diff(x, 2) + f(x), f(x), - ... hint='nth_linear_euler_eq_homogeneous') - ... # doctest: +NORMALIZE_WHITESPACE - f(x) == sqrt(x)*(C1 + C2*log(x)) - - Note that because this method does not involve integration, there is - no 'nth_linear_euler_eq_homogeneous_Integral' hint. - - The following is for internal use: - - - returns = 'sol' returns the solution to the ODE. - - returns = 'list' returns a list of linearly independent - solutions, corresponding to the fundamental solution set, for use with - non homogeneous solution methods like variation of parameters and - undetermined coefficients. Note that, though the solutions should be - linearly independent, this function does not explicitly check that. You - can do "assert simplify(wronskian(sollist)) != 0" to check for linear - independence. Also, "assert len(sollist) == order" will need to pass. - - returns = 'both', return a dictionary {'sol':solution to ODE, - 'list': list of linearly independent solutions}. - - Examples - ======== - - >>> from sympy import Function, dsolve, pprint - >>> from sympy.abc import x - >>> f = Function('f') - >>> eq = f(x).diff(x, 2)*x**2 - 4*f(x).diff(x)*x + 6*f(x) - >>> pprint(dsolve(eq, f(x), - ... hint='nth_linear_euler_eq_homogeneous')) - 2 - f(x) = x *(C1 + C2*x) - - References - ========== - - - http://en.wikipedia.org/wiki/Cauchy%E2%80%93Euler_equation - - C. Bender & S. Orszag, "Advanced Mathematical Methods for - Scientists and Engineers", Springer 1999, pp. 12 - # indirect doctest - - """ - global collectterms - collectterms = [] - - x = func.args[0] - f = func.func - r = match - - # A generator of constants - constants = numbered_symbols(prefix='C', cls=Symbol, start=1) - - # First, set up characteristic equation. - chareq, symbol = S.Zero, Dummy('x') - - for i in list(r.keys()): - if not isinstance(i, str) and i >= 0: - chareq += (r[i]*diff(x**symbol, x, i)*x**-symbol).expand() - - chareq = Poly(chareq, symbol) - chareqroots = [RootOf(chareq, k) for k in range(chareq.degree())] - - # Create a dict root: multiplicity or charroots - charroots = defaultdict(int) - for root in chareqroots: - charroots[root] += 1 - gsol = S(0) - # We need keep track of terms so we can run collect() at the end. - # This is necessary for constantsimp to work properly. - ln = log - for root, multiplicity in list(charroots.items()): - for i in range(multiplicity): - if isinstance(root, RootOf): - gsol += (x**root)*next(constants) - assert multiplicity == 1 - collectterms = [(0, root, 0)] + collectterms - elif root.is_real: - gsol += ln(x)**i*(x**root)*next(constants) - collectterms = [(i, root, 0)] + collectterms - else: - reroot = re(root) - imroot = im(root) - gsol += ln(x)**i*(x**reroot)*(next(constants)*sin(abs(imroot)*ln(x)) - + next(constants)*cos(imroot*ln(x))) - # Preserve ordering (multiplicity, real part, imaginary part) - # It will be assumed implicitly when constructing - # fundamental solution sets. - collectterms = [(i, reroot, imroot)] + collectterms - if returns == 'sol': - return Eq(f(x), gsol) - elif returns in ('list' 'both'): - # HOW TO TEST THIS CODE? (dsolve does not pass 'returns' through) - # Create a list of (hopefully) linearly independent solutions - gensols = [] - # Keep track of when to use sin or cos for nonzero imroot - for i, reroot, imroot in collectterms: - if imroot == 0: - gensols.append(ln(x)**i*x**reroot) - else: - sin_form = ln(x)**i*x**reroot*sin(abs(imroot)*ln(x)) - if sin_form in gensols: - cos_form = ln(x)**i*x**reroot*cos(imroot*ln(x)) - gensols.append(cos_form) - else: - gensols.append(sin_form) - if returns == 'list': - return gensols - else: - return {'sol': Eq(f(x), gsol), 'list': gensols} - else: - raise ValueError('Unknown value for key "returns".') - - -
    [docs]def ode_nth_linear_constant_coeff_homogeneous(eq, func, order, match, returns='sol'): - r""" - Solves an nth order linear homogeneous differential equation with - constant coefficients. - - This is an equation of the form a_n*f(x)^(n) + a_(n-1)*f(x)^(n-1) + - ... + a1*f'(x) + a0*f(x) = 0 - - These equations can be solved in a general manner, by taking the - roots of the characteristic equation a_n*m**n + a_(n-1)*m**(n-1) + - ... + a1*m + a0 = 0. The solution will then be the sum of - Cn*x**i*exp(r*x) terms, for each where Cn is an arbitrary constant, - r is a root of the characteristic equation and i is is one of each - from 0 to the multiplicity of the root - 1 (for example, a root 3 of - multiplicity 2 would create the terms C1*exp(3*x) + C2*x*exp(3*x)). - The exponential is usually expanded for complex roots using Euler's - equation exp(I*x) = cos(x) + I*sin(x). Complex roots always come in - conjugate pars in polynomials with real coefficients, so the two - roots will be represented (after simplifying the constants) as - exp(a*x)*(C1*cos(b*x) + C2*sin(b*x)). - - If SymPy cannot find exact roots to the characteristic equation, a - RootOf instance will be return in its stead. - - >>> from sympy import Function, dsolve, Eq - >>> from sympy.abc import x - >>> f = Function('f') - >>> dsolve(f(x).diff(x, 5) + 10*f(x).diff(x) - 2*f(x), f(x), - ... hint='nth_linear_constant_coeff_homogeneous') - ... # doctest: +NORMALIZE_WHITESPACE - f(x) == C1*exp(x*RootOf(_x**5 + 10*_x - 2, 0)) + - C2*exp(x*RootOf(_x**5 + 10*_x - 2, 1)) + - C3*exp(x*RootOf(_x**5 + 10*_x - 2, 2)) + - C4*exp(x*RootOf(_x**5 + 10*_x - 2, 3)) + - C5*exp(x*RootOf(_x**5 + 10*_x - 2, 4)) - - Note that because this method does not involve integration, there is - no 'nth_linear_constant_coeff_homogeneous_Integral' hint. - - The following is for internal use: - - - returns = 'sol' returns the solution to the ODE. - - returns = 'list' returns a list of linearly independent - solutions, for use with non homogeneous solution methods like - variation of parameters and undetermined coefficients. Note that, - though the solutions should be linearly independent, this function - does not explicitly check that. You can do "assert - simplify(wronskian(sollist)) != 0" to check for linear independence. - Also, "assert len(sollist) == order" will need to pass. - - returns = 'both', return a dictionary {'sol':solution to ODE, - 'list': list of linearly independent solutions}. - - Examples - ======== - - >>> from sympy import Function, dsolve, pprint - >>> from sympy.abc import x - >>> f = Function('f') - >>> pprint(dsolve(f(x).diff(x, 4) + 2*f(x).diff(x, 3) - - ... 2*f(x).diff(x, 2) - 6*f(x).diff(x) + 5*f(x), f(x), - ... hint='nth_linear_constant_coeff_homogeneous')) - x -2*x - f(x) = (C1 + C2*x)*e + (C3*sin(x) + C4*cos(x))*e - - References - ========== - - - http://en.wikipedia.org/wiki/Linear_differential_equation - section: Nonhomogeneous_equation_with_constant_coefficients - - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations", - Dover 1963, pp. 211 - - # indirect doctest - - """ - x = func.args[0] - f = func.func - r = match - - # A generator of constants - constants = numbered_symbols(prefix='C', cls=Symbol, start=1) - - # First, set up characteristic equation. - chareq, symbol = S.Zero, Dummy('x') - - for i in list(r.keys()): - if type(i) == str or i < 0: - pass - else: - chareq += r[i]*symbol**i - - chareq = Poly(chareq, symbol) - chareqroots = [ RootOf(chareq, k) for k in range(chareq.degree()) ] - - # Create a dict root: multiplicity or charroots - charroots = defaultdict(int) - for root in chareqroots: - charroots[root] += 1 - gsol = S(0) - # We need keep track of terms so we can run collect() at the end. - # This is necessary for constantsimp to work properly. - global collectterms - collectterms = [] - for root, multiplicity in list(charroots.items()): - for i in range(multiplicity): - if isinstance(root, RootOf): - gsol += exp(root*x)*next(constants) - assert multiplicity == 1 - collectterms = [(0, root, 0)] + collectterms - else: - reroot = re(root) - imroot = im(root) - gsol += x**i*exp(reroot*x)*(next(constants)*sin(abs(imroot)*x) - + next(constants)*cos(imroot*x)) - # This ordering is important - collectterms = [(i, reroot, imroot)] + collectterms - if returns == 'sol': - return Eq(f(x), gsol) - elif returns in ('list' 'both'): - # Create a list of (hopefully) linearly independent solutions - gensols = [] - # Keep track of when to use sin or cos for nonzero imroot - for i, reroot, imroot in collectterms: - if imroot == 0: - gensols.append(x**i*exp(reroot*x)) - else: - if x**i*exp(reroot*x)*sin(abs(imroot)*x) in gensols: - gensols.append(x**i*exp(reroot*x)*cos(imroot*x)) - else: - gensols.append(x**i*exp(reroot*x)*sin(abs(imroot)*x)) - if returns == 'list': - return gensols - else: - return {'sol': Eq(f(x), gsol), 'list': gensols} - else: - raise ValueError('Unknown value for key "returns".') - -
    -
    [docs]def ode_nth_linear_constant_coeff_undetermined_coefficients(eq, func, order, match): - r""" - Solves an nth order linear differential equation with constant - coefficients using the method of undetermined coefficients. - - This method works on differential equations of the form a_n*f(x)^(n) - + a_(n-1)*f(x)^(n-1) + ... + a1*f'(x) + a0*f(x) = P(x), where P(x) - is a function that has a finite number of linearly independent - derivatives. - - Functions that fit this requirement are finite sums functions of the - form a*x**i*exp(b*x)*sin(c*x + d) or a*x**i*exp(b*x)*cos(c*x + d), - where i is a non-negative integer and a, b, c, and d are constants. - For example any polynomial in x, functions like x**2*exp(2*x), - x*sin(x), and exp(x)*cos(x) can all be used. Products of sin's and - cos's have a finite number of derivatives, because they can be - expanded into sin(a*x) and cos(b*x) terms. However, SymPy currently - cannot do that expansion, so you will need to manually rewrite the - expression in terms of the above to use this method. So, for example, - you will need to manually convert sin(x)**2 into (1 + cos(2*x))/2 to - properly apply the method of undetermined coefficients on it. - - This method works by creating a trial function from the expression - and all of its linear independent derivatives and substituting them - into the original ODE. The coefficients for each term will be a - system of linear equations, which are be solved for and substituted, - giving the solution. If any of the trial functions are linearly - dependent on the solution to the homogeneous equation, they are - multiplied by sufficient x to make them linearly independent. - - Examples - ======== - - >>> from sympy import Function, dsolve, pprint, exp, cos - >>> from sympy.abc import x - >>> f = Function('f') - >>> pprint(dsolve(f(x).diff(x, 2) + 2*f(x).diff(x) + f(x) - - ... 4*exp(-x)*x**2 + cos(2*x), f(x), - ... hint='nth_linear_constant_coeff_undetermined_coefficients')) - / 4\ - | x | -x 4*sin(2*x) 3*cos(2*x) - f(x) = |C1 + C2*x + --|*e - ---------- + ---------- - \ 3 / 25 25 - - References - ========== - - - http://en.wikipedia.org/wiki/Method_of_undetermined_coefficients - - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations", - Dover 1963, pp. 221 - - # indirect doctest - - """ - gensol = ode_nth_linear_constant_coeff_homogeneous(eq, func, order, match, - returns='both') - match.update(gensol) - return _solve_undetermined_coefficients(eq, func, order, match) - -
    -def _solve_undetermined_coefficients(eq, func, order, match): - """ - Helper function for the method of undetermined coefficients. - - See the ode_nth_linear_constant_coeff_undetermined_coefficients() - docstring for more information on this method. - - match should be a dictionary that has the following keys: - 'list' - A list of solutions to the homogeneous equation, such as - the list returned by - ode_nth_linear_constant_coeff_homogeneous(returns='list') - 'sol' - The general solution, such as the solution returned by - ode_nth_linear_constant_coeff_homogeneous(returns='sol') - 'trialset' - The set of trial functions as returned by - _undetermined_coefficients_match()['trialset'] - - """ - x = func.args[0] - f = func.func - r = match - coeffs = numbered_symbols('a', cls=Dummy) - coefflist = [] - gensols = r['list'] - gsol = r['sol'] - trialset = r['trialset'] - notneedset = set([]) - newtrialset = set([]) - global collectterms - if len(gensols) != order: - raise NotImplementedError("Cannot find " + str(order) + - " solutions to the homogeneous equation nessesary to apply " + - "undetermined coefficients to " + str(eq) + " (number of terms != order)") - usedsin = set([]) - mult = 0 # The multiplicity of the root - getmult = True - for i, reroot, imroot in collectterms: - if getmult: - mult = i + 1 - getmult = False - if i == 0: - getmult = True - if imroot: - # Alternate between sin and cos - if (i, reroot) in usedsin: - check = x**i*exp(reroot*x)*cos(imroot*x) - else: - check = x**i*exp(reroot*x)*sin(abs(imroot)*x) - usedsin.add((i, reroot)) - else: - check = x**i*exp(reroot*x) - - if check in trialset: - # If an element of the trial function is already part of the homogeneous - # solution, we need to multiply by sufficient x to make it linearly - # independent. We also don't need to bother checking for the coefficients - # on those elements, since we already know it will be 0. - while True: - if check*x**mult in trialset: - mult += 1 - else: - break - trialset.add(check*x**mult) - notneedset.add(check) - - newtrialset = trialset - notneedset - - trialfunc = 0 - for i in newtrialset: - c = next(coeffs) - coefflist.append(c) - trialfunc += c*i - - eqs = sub_func_doit(eq, f(x), trialfunc) - - coeffsdict = dict(list(zip(trialset, [0]*(len(trialset) + 1)))) - - eqs = expand_mul(eqs) - - for i in Add.make_args(eqs): - s = separatevars(i, dict=True, symbols=[x]) - coeffsdict[s[x]] += s['coeff'] - - coeffvals = solve(list(coeffsdict.values()), coefflist) - - if not coeffvals: - raise NotImplementedError("Could not solve " + str(eq) + " using the " + - " method of undetermined coefficients (unable to solve for coefficients).") - - psol = trialfunc.subs(coeffvals) - - return Eq(f(x), gsol.rhs + psol) - - -def _undetermined_coefficients_match(expr, x): - """ - Returns a trial function match if undetermined coefficients can be - applied to expr, and None otherwise. - - A trial expression can be found for an expression for use with the - method of undetermined coefficients if the expression is an - additive/multiplicative combination of constants, polynomials in x - (the independent variable of expr), sin(a*x + b), cos(a*x + b), and - exp(a*x) terms (in other words, it has a finite number of linearly - independent derivatives). - - Note that you may still need to multiply each term returned here by - sufficient x to make it linearly independent with the solutions to - the homogeneous equation. - - This is intended for internal use by undetermined_coefficients - hints. - - SymPy currently has no way to convert sin(x)**n*cos(y)**m into a sum - of only sin(a*x) and cos(b*x) terms, so these are not implemented. - So, for example, you will need to manually convert sin(x)**2 into - (1 + cos(2*x))/2 to properly apply the method of undetermined - coefficients on it. - - Examples - ======== - - >>> from sympy import log, exp - >>> from sympy.solvers.ode import _undetermined_coefficients_match - >>> from sympy.abc import x - >>> _undetermined_coefficients_match(9*x*exp(x) + exp(-x), x) - {'test': True, 'trialset': set([x*exp(x), exp(-x), exp(x)])} - >>> _undetermined_coefficients_match(log(x), x) - {'test': False} - - """ - from sympy import S - a = Wild('a', exclude=[x]) - b = Wild('b', exclude=[x]) - expr = powsimp(expr, combine='exp') # exp(x)*exp(2*x + 1) => exp(3*x + 1) - retdict = {} - - def _test_term(expr, x): - """ - Test if expr fits the proper form for undetermined coefficients. - """ - if expr.is_Add: - return all(_test_term(i, x) for i in expr.args) - elif expr.is_Mul: - if expr.has(sin, cos): - foundtrig = False - # Make sure that there is only one trig function in the args. - # See the docstring. - for i in expr.args: - if i.has(sin, cos): - if foundtrig: - return False - else: - foundtrig = True - return all(_test_term(i, x) for i in expr.args) - elif expr.is_Function: - if expr.func in (sin, cos, exp): - if expr.args[0].match(a*x + b): - return True - else: - return False - else: - return False - elif expr.is_Pow and expr.base.is_Symbol and expr.exp.is_Integer and \ - expr.exp >= 0: - return True - elif expr.is_Pow and expr.base.is_number: - if expr.exp.match(a*x + b): - return True - else: - return False - elif expr.is_Symbol or expr.is_Number: - return True - else: - return False - - def _get_trial_set(expr, x, exprs=set([])): - """ - Returns a set of trial terms for undetermined coefficients. - - The idea behind undetermined coefficients is that the terms - expression repeat themselves after a finite number of - derivatives, except for the coefficients (they are linearly - dependent). So if we collect these, we should have the terms of - our trial function. - """ - def _remove_coefficient(expr, x): - """ - Returns the expression without a coefficient. - - Similar to expr.as_independent(x)[1], except it only works - multiplicatively. - """ - # I was using the below match, but it doesn't always put all of the - # coefficient in c. c.f. 2**x*6*exp(x)*log(2) - # The below code is probably cleaner anyway. -# c = Wild('c', exclude=[x]) -# t = Wild('t') -# r = expr.match(c*t) - term = S.One - if expr.is_Mul: - for i in expr.args: - if i.has(x): - term *= i - elif expr.has(x): - term = expr - return term - - expr = expand_mul(expr) - if expr.is_Add: - for term in expr.args: - if _remove_coefficient(term, x) in exprs: - pass - else: - exprs.add(_remove_coefficient(term, x)) - exprs = exprs.union(_get_trial_set(term, x, exprs)) - else: - term = _remove_coefficient(expr, x) - tmpset = exprs.union(set([term])) - oldset = set([]) - while tmpset != oldset: - # If you get stuck in this loop, then _test_term is probably broken - oldset = tmpset.copy() - expr = expr.diff(x) - term = _remove_coefficient(expr, x) - if term.is_Add: - tmpset = tmpset.union(_get_trial_set(term, x, tmpset)) - else: - tmpset.add(term) - exprs = tmpset - return exprs - - retdict['test'] = _test_term(expr, x) - if retdict['test']: - # Try to generate a list of trial solutions that will have the undetermined - # coefficients. Note that if any of these are not linearly independent - # with any of the solutions to the homogeneous equation, then they will - # need to be multiplied by sufficient x to make them so. This function - # DOES NOT do that (it doesn't even look at the homogeneous equation). - retdict['trialset'] = _get_trial_set(expr, x) - - return retdict - - -
    [docs]def ode_nth_linear_constant_coeff_variation_of_parameters(eq, func, order, match): - r""" - Solves an nth order linear differential equation with constant - coefficients using the method of variation of parameters. - - This method works on any differential equations of the form - f(x)^(n) + a_(n-1)*f(x)^(n-1) + ... + a1*f'(x) + a0*f(x) = P(x). - - This method works by assuming that the particular solution takes the - form Sum(c_i(x)*y_i(x), (x, 1, n)), where y_i is the ith solution to - the homogeneous equation. The solution is then solved using - Wronskian's and Cramer's Rule. The particular solution is given by - Sum(Integral(W_i(x)/W(x), x)*y_i(x), (x, 1, n)), where W(x) is the - Wronskian of the fundamental system (the system of n linearly - independent solutions to the homogeneous equation), and W_i(x) is - the Wronskian of the fundamental system with the ith column replaced - with [0, 0, ..., 0, P(x)]. - - This method is general enough to solve any nth order inhomogeneous - linear differential equation with constant coefficients, but - sometimes SymPy cannot simplify the Wronskian well enough to - integrate it. If this method hangs, try using the - 'nth_linear_constant_coeff_variation_of_parameters_Integral' hint - and simplifying the integrals manually. Also, prefer using - 'nth_linear_constant_coeff_undetermined_coefficients' when it - applies, because it doesn't use integration, making it faster and - more reliable. - - Warning, using simplify=False with - 'nth_linear_constant_coeff_variation_of_parameters' in dsolve() - may cause it to hang, because it will not attempt to simplify - the Wronskian before integrating. It is recommended that you only - use simplify=False with - 'nth_linear_constant_coeff_variation_of_parameters_Integral' for - this method, especially if the solution to the homogeneous - equation has trigonometric functions in it. - - Examples - ======== - - >>> from sympy import Function, dsolve, pprint, exp, log - >>> from sympy.abc import x - >>> f = Function('f') - >>> pprint(dsolve(f(x).diff(x, 3) - 3*f(x).diff(x, 2) + - ... 3*f(x).diff(x) - f(x) - exp(x)*log(x), f(x), - ... hint='nth_linear_constant_coeff_variation_of_parameters')) - / 3 \ - | 2 x *(6*log(x) - 11)| x - f(x) = |C1 + C2*x + C3*x + ------------------|*e - \ 36 / - - References - ========== - - - http://en.wikipedia.org/wiki/Variation_of_parameters - - http://planetmath.org/encyclopedia/VariationOfParameters.html - - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations", - Dover 1963, pp. 233 - - # indirect doctest - - """ - - gensol = ode_nth_linear_constant_coeff_homogeneous(eq, func, order, match, - returns='both') - match.update(gensol) - return _solve_variation_of_parameters(eq, func, order, match) - -
    -def _solve_variation_of_parameters(eq, func, order, match): - """ - Helper function for the method of variation of parameters. - - See the ode_nth_linear_constant_coeff_variation_of_parameters() - docstring for more information on this method. - - match should be a dictionary that has the following keys: - 'list' - A list of solutions to the homogeneous equation, such as - the list returned by - ode_nth_linear_constant_coeff_homogeneous(returns='list') - 'sol' - The general solution, such as the solution returned by - ode_nth_linear_constant_coeff_homogeneous(returns='sol') - - """ - - x = func.args[0] - f = func.func - r = match - psol = 0 - gensols = r['list'] - gsol = r['sol'] - wr = wronskian(gensols, x) - - if r.get('simplify', True): - wr = simplify(wr) # We need much better simplification for some ODEs. - # See issue 1563, for example. - - # To reduce commonly occuring sin(x)**2 + cos(x)**2 to 1 - wr = trigsimp(wr, deep=True, recursive=True) - if not wr: - # The wronskian will be 0 iff the solutions are not linearly independent. - raise NotImplementedError("Cannot find " + str(order) + - " solutions to the homogeneous equation nessesary to apply " + - "variation of parameters to " + str(eq) + " (Wronskian == 0)") - if len(gensols) != order: - raise NotImplementedError("Cannot find " + str(order) + - " solutions to the homogeneous equation nessesary to apply " + - "variation of parameters to " + str(eq) + " (number of terms != order)") - negoneterm = (-1)**(order) - for i in gensols: - psol += negoneterm*C.Integral(wronskian([x for x in gensols if x != i], x)*r[-1]/wr, x)*i/r[order] - negoneterm *= -1 - - if r.get('simplify', True): - psol = simplify(psol) - psol = trigsimp(psol, deep=True) - return Eq(f(x), gsol.rhs + psol) - - -
    [docs]def ode_separable(eq, func, order, match): - r""" - - Solves separable 1st order differential equations. - - This is any differential equation that can be written as - P(y)*dy/dx = Q(x). The solution can then just be found by - rearranging terms and integrating: - Integral(P(y), y) = Integral(Q(x), x). This hint uses separatevars() - as its back end, so if a separable equation is not caught by this - solver, it is most likely the fault of that function. separatevars() - is smart enough to do most expansion and factoring necessary to - convert a separable equation F(x, y) into the proper form P(x)*Q(y). - The general solution is:: - - >>> from sympy import Function, dsolve, Eq, pprint - >>> from sympy.abc import x - >>> a, b, c, d, f = list(map(Function, ['a', 'b', 'c', 'd', 'f'])) - >>> genform = Eq(a(x)*b(f(x))*f(x).diff(x), c(x)*d(f(x))) - >>> pprint(genform) - d - a(x)*b(f(x))*--(f(x)) = c(x)*d(f(x)) - dx - >>> pprint(dsolve(genform, f(x), hint='separable_Integral')) - f(x) - / / - | | - | b(y) | c(x) - | ---- dy = C1 + | ---- dx - | d(y) | a(x) - | | - / / - - Examples - ======== - - >>> from sympy import Function, dsolve, Eq - >>> from sympy.abc import x - >>> f = Function('f') - >>> pprint(dsolve(Eq(f(x)*f(x).diff(x) + x, 3*x*f(x)**2), f(x), - ... hint='separable', simplify=False)) - / 2 \ 2 - log\3*f (x) - 1/ x - ---------------- = C1 + -- - 6 2 - - References - ========== - - - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations", - Dover 1963, pp. 52 - - # indirect doctest - - """ - x = func.args[0] - f = func.func - C1 = Symbol('C1') - r = match # {'m1':m1, 'm2':m2, 'y':y} - return Eq(C.Integral(r['m2']['coeff']*r['m2'][r['y']]/r['m1'][r['y']], - (r['y'], None, f(x))), C.Integral(-r['m1']['coeff']*r['m1'][x]/ - r['m2'][x], x) + C1)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/solvers/pde.html b/dev-py3k/_modules/sympy/solvers/pde.html deleted file mode 100644 index 649b211647a..00000000000 --- a/dev-py3k/_modules/sympy/solvers/pde.html +++ /dev/null @@ -1,327 +0,0 @@ - - - - - - - - - - sympy.solvers.pde — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.solvers.pde

    -"""
    -Analytical methods for solving Partial Differential Equations
    -Currently implemented methods:
    -    - separation of variables - pde_separate
    -
    -"""
    -
    -from sympy import Eq, Equality
    -from sympy.simplify import simplify
    -from sympy.core.compatibility import reduce
    -from sympy.utilities.iterables import has_dups
    -
    -import operator
    -from functools import reduce
    -
    -
    -
    [docs]def pde_separate(eq, fun, sep, strategy='mul'): - """Separate variables in partial differential equation either by additive - or multiplicative separation approach. It tries to rewrite an equation so - that one of the specified variables occurs on a different side of the - equation than the others. - - :param eq: Partial differential equation - - :param fun: Original function F(x, y, z) - - :param sep: List of separated functions [X(x), u(y, z)] - - :param strategy: Separation strategy. You can choose between additive - separation ('add') and multiplicative separation ('mul') which is - default. - - Examples - ======== - - >>> from sympy import E, Eq, Function, pde_separate, Derivative as D - >>> from sympy.abc import x, t - >>> u, X, T = list(map(Function, 'uXT')) - - >>> eq = Eq(D(u(x, t), x), E**(u(x, t))*D(u(x, t), t)) - >>> pde_separate(eq, u(x, t), [X(x), T(t)], strategy='add') - [exp(-X(x))*Derivative(X(x), x), exp(T(t))*Derivative(T(t), t)] - - >>> eq = Eq(D(u(x, t), x, 2), D(u(x, t), t, 2)) - >>> pde_separate(eq, u(x, t), [X(x), T(t)], strategy='mul') - [Derivative(X(x), x, x)/X(x), Derivative(T(t), t, t)/T(t)] - - See Also - ======== - pde_separate_add, pde_separate_mul - """ - - do_add = False - if strategy == 'add': - do_add = True - elif strategy == 'mul': - do_add = False - else: - assert ValueError('Unknown strategy: %s' % strategy) - - if isinstance(eq, Equality): - if eq.rhs != 0: - return pde_separate(Eq(eq.lhs - eq.rhs), fun, sep, strategy) - assert eq.rhs == 0 - - # Handle arguments - orig_args = list(fun.args) - subs_args = [] - for s in sep: - for j in range(0, len(s.args)): - subs_args.append(s.args[j]) - - if do_add: - functions = reduce(operator.add, sep) - else: - functions = reduce(operator.mul, sep) - - # Check whether variables match - if len(subs_args) != len(orig_args): - raise ValueError("Variable counts do not match") - # Check for duplicate arguments like [X(x), u(x, y)] - if has_dups(subs_args): - raise ValueError("Duplicate substitution arguments detected") - # Check whether the variables match - if set(orig_args) != set(subs_args): - raise ValueError("Arguments do not match") - - # Substitute original function with separated... - result = eq.lhs.subs(fun, functions).doit() - - # Divide by terms when doing multiplicative separation - if not do_add: - eq = 0 - for i in result.args: - eq += i/functions - result = eq - - svar = subs_args[0] - dvar = subs_args[1:] - return _separate(result, svar, dvar) - -
    -
    [docs]def pde_separate_add(eq, fun, sep): - """ - Helper function for searching additive separable solutions. - - Consider an equation of two independent variables x, y and a dependent - variable w, we look for the product of two functions depending on different - arguments: - - `w(x, y, z) = X(x) + y(y, z)` - - Examples - ======== - - >>> from sympy import E, Eq, Function, pde_separate_add, Derivative as D - >>> from sympy.abc import x, t - >>> u, X, T = list(map(Function, 'uXT')) - - >>> eq = Eq(D(u(x, t), x), E**(u(x, t))*D(u(x, t), t)) - >>> pde_separate_add(eq, u(x, t), [X(x), T(t)]) - [exp(-X(x))*Derivative(X(x), x), exp(T(t))*Derivative(T(t), t)] - - """ - return pde_separate(eq, fun, sep, strategy='add') - -
    -
    [docs]def pde_separate_mul(eq, fun, sep): - """ - Helper function for searching multiplicative separable solutions. - - Consider an equation of two independent variables x, y and a dependent - variable w, we look for the product of two functions depending on different - arguments: - - `w(x, y, z) = X(x)*u(y, z)` - - Examples - ======== - - >>> from sympy import Function, Eq, pde_separate_mul, Derivative as D - >>> from sympy.abc import x, y - >>> u, X, Y = list(map(Function, 'uXY')) - - >>> eq = Eq(D(u(x, y), x, 2), D(u(x, y), y, 2)) - >>> pde_separate_mul(eq, u(x, y), [X(x), Y(y)]) - [Derivative(X(x), x, x)/X(x), Derivative(Y(y), y, y)/Y(y)] - - """ - return pde_separate(eq, fun, sep, strategy='mul') - -
    -def _separate(eq, dep, others): - """Separate expression into two parts based on dependencies of variables.""" - - # FIRST PASS - # Extract derivatives depending our separable variable... - terms = set() - for term in eq.args: - if term.is_Mul: - for i in term.args: - if i.is_Derivative and not i.has(*others): - terms.add(term) - continue - elif term.is_Derivative and not term.has(*others): - terms.add(term) - # Find the factor that we need to divide by - div = set() - for term in terms: - ext, sep = term.expand().as_independent(dep) - # Failed? - if sep.has(*others): - return None - div.add(ext) - # FIXME: Find lcm() of all the divisors and divide with it, instead of - # current hack :( - # http://code.google.com/p/sympy/issues/detail?id=1498 - if len(div) > 0: - final = 0 - for term in eq.args: - eqn = 0 - for i in div: - eqn += term / i - final += simplify(eqn) - eq = final - - # SECOND PASS - separate the derivatives - div = set() - lhs = rhs = 0 - for term in eq.args: - # Check, whether we have already term with independent variable... - if not term.has(*others): - lhs += term - continue - # ...otherwise, try to separate - temp, sep = term.expand().as_independent(dep) - # Failed? - if sep.has(*others): - return None - # Extract the divisors - div.add(sep) - rhs -= term.expand() - # Do the division - fulldiv = reduce(operator.add, div) - lhs = simplify(lhs/fulldiv).expand() - rhs = simplify(rhs/fulldiv).expand() - # ...and check whether we were successful :) - if lhs.has(*others) or rhs.has(dep): - return None - return [lhs, rhs] -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/solvers/polysys.html b/dev-py3k/_modules/sympy/solvers/polysys.html deleted file mode 100644 index f8647b99662..00000000000 --- a/dev-py3k/_modules/sympy/solvers/polysys.html +++ /dev/null @@ -1,434 +0,0 @@ - - - - - - - - - - sympy.solvers.polysys — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.solvers.polysys

    -"""Solvers of systems of polynomial equations. """
    -
    -from sympy.polys import Poly, groebner, roots
    -from sympy.polys.polytools import parallel_poly_from_expr
    -from sympy.polys.polyerrors import (ComputationFailed,
    -    PolificationFailed, CoercionFailed)
    -from sympy.utilities import postfixes
    -from sympy.simplify import rcollect
    -from sympy.core import S
    -
    -
    -class SolveFailed(Exception):
    -    """Raised when solver's conditions weren't met. """
    -
    -
    -
    [docs]def solve_poly_system(seq, *gens, **args): - """ - Solve a system of polynomial equations. - - Examples - ======== - - >>> from sympy import solve_poly_system - >>> from sympy.abc import x, y - - >>> solve_poly_system([x*y - 2*y, 2*y**2 - x**2], x, y) - [(0, 0), (2, -sqrt(2)), (2, sqrt(2))] - - """ - try: - polys, opt = parallel_poly_from_expr(seq, *gens, **args) - except PolificationFailed as exc: - raise ComputationFailed('solve_poly_system', len(seq), exc) - - if len(polys) == len(opt.gens) == 2: - f, g = polys - - a, b = f.degree_list() - c, d = g.degree_list() - - if a <= 2 and b <= 2 and c <= 2 and d <= 2: - try: - return solve_biquadratic(f, g, opt) - except SolveFailed: - pass - - return solve_generic(polys, opt) - -
    -def solve_biquadratic(f, g, opt): - """Solve a system of two bivariate quadratic polynomial equations. - - Examples - ======== - - >>> from sympy.polys import Options, Poly - >>> from sympy.abc import x, y - >>> from sympy.solvers.polysys import solve_biquadratic - >>> NewOption = Options((x, y), {'domain': 'ZZ'}) - - >>> a = Poly(y**2 - 4 + x, y, x, domain='ZZ') - >>> b = Poly(y*2 + 3*x - 7, y, x, domain='ZZ') - >>> solve_biquadratic(a, b, NewOption) - [(1/3, 3), (41/27, 11/9)] - - >>> a = Poly(y + x**2 - 3, y, x, domain='ZZ') - >>> b = Poly(-y + x - 4, y, x, domain='ZZ') - >>> solve_biquadratic(a, b, NewOption) - [(-sqrt(29)/2 + 7/2, -sqrt(29)/2 - 1/2), (sqrt(29)/2 + 7/2, -1/2 + \ - sqrt(29)/2)] - """ - G = groebner([f, g]) - - if len(G) == 1 and G[0].is_ground: - return None - - if len(G) != 2: - raise SolveFailed - - p, q = G - x, y = opt.gens - - p = Poly(p, x, expand=False) - q = q.ltrim(-1) - - p_roots = [ rcollect(expr, y) for expr in list(roots(p).keys()) ] - q_roots = list(roots(q).keys()) - - solutions = [] - - for q_root in q_roots: - for p_root in p_roots: - solution = (p_root.subs(y, q_root), q_root) - solutions.append(solution) - - return sorted(solutions) - - -def solve_generic(polys, opt): - """ - Solve a generic system of polynomial equations. - - Returns all possible solutions over C[x_1, x_2, ..., x_m] of a - set F = { f_1, f_2, ..., f_n } of polynomial equations, using - Groebner basis approach. For now only zero-dimensional systems - are supported, which means F can have at most a finite number - of solutions. - - The algorithm works by the fact that, supposing G is the basis - of F with respect to an elimination order (here lexicographic - order is used), G and F generate the same ideal, they have the - same set of solutions. By the elimination property, if G is a - reduced, zero-dimensional Groebner basis, then there exists an - univariate polynomial in G (in its last variable). This can be - solved by computing its roots. Substituting all computed roots - for the last (eliminated) variable in other elements of G, new - polynomial system is generated. Applying the above procedure - recursively, a finite number of solutions can be found. - - The ability of finding all solutions by this procedure depends - on the root finding algorithms. If no solutions were found, it - means only that roots() failed, but the system is solvable. To - overcome this difficulty use numerical algorithms instead. - - References - ========== - - .. [Buchberger01] B. Buchberger, Groebner Bases: A Short - Introduction for Systems Theorists, In: R. Moreno-Diaz, - B. Buchberger, J.L. Freire, Proceedings of EUROCAST'01, - February, 2001 - - .. [Cox97] D. Cox, J. Little, D. O'Shea, Ideals, Varieties - and Algorithms, Springer, Second Edition, 1997, pp. 112 - - Examples - ======== - - >>> from sympy.polys import Poly, Options - >>> from sympy.solvers.polysys import solve_generic - >>> from sympy.abc import x, y - >>> NewOption = Options((x, y), {'domain': 'ZZ'}) - - >>> a = Poly(x - y + 5, x, y, domain='ZZ') - >>> b = Poly(x + y - 3, x, y, domain='ZZ') - >>> solve_generic([a, b], NewOption) - [(-1, 4)] - - >>> a = Poly(x - 2*y + 5, x, y, domain='ZZ') - >>> b = Poly(2*x - y - 3, x, y, domain='ZZ') - >>> solve_generic([a, b], NewOption) - [(11/3, 13/3)] - - >>> a = Poly(x**2 + y, x, y, domain='ZZ') - >>> b = Poly(x + y*4, x, y, domain='ZZ') - >>> solve_generic([a, b], NewOption) - [(0, 0), (1/4, -1/16)] - """ - def _is_univariate(f): - """Returns True if 'f' is univariate in its last variable. """ - for monom in f.monoms(): - if any(m > 0 for m in monom[:-1]): - return False - - return True - - def _subs_root(f, gen, zero): - """Replace generator with a root so that the result is nice. """ - p = f.as_expr({gen: zero}) - - if f.degree(gen) >= 2: - p = p.expand(deep=False) - - return p - - def _solve_reduced_system(system, gens, entry=False): - """Recursively solves reduced polynomial systems. """ - if len(system) == len(gens) == 1: - zeros = list(roots(system[0], gens[-1]).keys()) - return [ (zero,) for zero in zeros ] - - basis = groebner(system, gens, polys=True) - - if len(basis) == 1 and basis[0].is_ground: - if not entry: - return [] - else: - return None - - univariate = list(filter(_is_univariate, basis)) - - if len(univariate) == 1: - f = univariate.pop() - else: - raise NotImplementedError("only zero-dimensional systems supported (finite number of solutions)") - - gens = f.gens - gen = gens[-1] - - zeros = list(roots(f.ltrim(gen)).keys()) - - if not zeros: - return [] - - if len(basis) == 1: - return [ (zero,) for zero in zeros ] - - solutions = [] - - for zero in zeros: - new_system = [] - new_gens = gens[:-1] - - for b in basis[:-1]: - eq = _subs_root(b, gen, zero) - - if eq is not S.Zero: - new_system.append(eq) - - for solution in _solve_reduced_system(new_system, new_gens): - solutions.append(solution + (zero,)) - - return solutions - - try: - result = _solve_reduced_system(polys, opt.gens, entry=True) - except CoercionFailed: - raise NotImplementedError - - if result is not None: - return sorted(result) - else: - return None - - -
    [docs]def solve_triangulated(polys, *gens, **args): - """ - Solve a polynomial system using Gianni-Kalkbrenner algorithm. - - The algorithm proceeds by computing one Groebner basis in the ground - domain and then by iteratively computing polynomial factorizations in - appropriately constructed algebraic extensions of the ground domain. - - Examples - ======== - - >>> from sympy.solvers.polysys import solve_triangulated - >>> from sympy.abc import x, y, z - - >>> F = [x**2 + y + z - 1, x + y**2 + z - 1, x + y + z**2 - 1] - - >>> solve_triangulated(F, x, y, z) - [(0, 0, 1), (0, 1, 0), (1, 0, 0)] - - References - ========== - - 1. Patrizia Gianni, Teo Mora, Algebraic Solution of System of - Polynomial Equations using Groebner Bases, AAECC-5 on Applied Algebra, - Algebraic Algorithms and Error-Correcting Codes, LNCS 356 247--257, 1989 - - """ - G = groebner(polys, gens, polys=True) - G = list(reversed(G)) - - domain = args.get('domain') - - if domain is not None: - for i, g in enumerate(G): - G[i] = g.set_domain(domain) - - f, G = G[0].ltrim(-1), G[1:] - dom = f.get_domain() - - zeros = f.ground_roots() - solutions = set([]) - - for zero in zeros: - solutions.add(((zero,), dom)) - - var_seq = reversed(gens[:-1]) - vars_seq = postfixes(gens[1:]) - - for var, vars in zip(var_seq, vars_seq): - _solutions = set([]) - - for values, dom in solutions: - H, mapping = [], list(zip(vars, values)) - - for g in G: - _vars = (var,) + vars - - if g.has_only_gens(*_vars) and g.degree(var) != 0: - h = g.ltrim(var).eval(dict(mapping)) - - if g.degree(var) == h.degree(): - H.append(h) - - p = min(H, key=lambda h: h.degree()) - zeros = p.ground_roots() - - for zero in zeros: - if not zero.is_Rational: - dom_zero = dom.algebraic_field(zero) - else: - dom_zero = dom - - _solutions.add(((zero,) + values, dom_zero)) - - solutions = _solutions - - solutions = list(solutions) - - for i, (solution, _) in enumerate(solutions): - solutions[i] = solution - - return sorted(solutions)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/solvers/recurr.html b/dev-py3k/_modules/sympy/solvers/recurr.html deleted file mode 100644 index 8da12194e1a..00000000000 --- a/dev-py3k/_modules/sympy/solvers/recurr.html +++ /dev/null @@ -1,904 +0,0 @@ - - - - - - - - - - sympy.solvers.recurr — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.solvers.recurr

    -"""This module is intended for solving recurrences or, in other words,
    -   difference equations. Currently supported are linear, inhomogeneous
    -   equations with polynomial or rational coefficients.
    -
    -   The solutions are obtained among polynomials, rational functions,
    -   hypergeometric terms, or combinations of hypergeometric term which
    -   are pairwise dissimilar.
    -
    -   rsolve_X functions were meant as a low level interface for rsolve()
    -   which would use Mathematica's syntax.
    -
    -   Given a recurrence relation:
    -
    -      a_{k}(n) y(n+k) + a_{k-1}(n) y(n+k-1) + ... + a_{0}(n) y(n) = f(n)
    -
    -   where k > 0 and a_{i}(n) are polynomials in n. To use rsolve_X we need
    -   to put all coefficients in to a list L of k+1 elements the following
    -   way:
    -
    -      L = [ a_{0}(n), ..., a_{k-1}(n), a_{k}(n) ]
    -
    -   where L[i], for i=0..k, maps to a_{i}(n) y(n+i) (y(n+i) is implicit).
    -
    -   For example if we would like to compute m-th Bernoulli polynomial up to
    -   a constant (example was taken from rsolve_poly docstring), then we would
    -   use b(n+1) - b(n) == m*n**(m-1) recurrence, which has solution b(n) =
    -   B_m + C.
    -
    -   Then L = [-1, 1] and f(n) = m*n**(m-1) and finally for m=4:
    -
    -    >>> from sympy import Symbol, bernoulli, rsolve_poly
    -    >>> n = Symbol('n', integer=True)
    -
    -    >>> rsolve_poly([-1, 1], 4*n**3, n)
    -    C0 + n**4 - 2*n**3 + n**2
    -
    -    >>> bernoulli(4, n)
    -    n**4 - 2*n**3 + n**2 - 1/30
    -
    -   For the sake of completeness, f(n) can be:
    -
    -    [1] a polynomial              -> rsolve_poly
    -    [2] a rational function       -> rsolve_ratio
    -    [3] a hypergeometric function  -> rsolve_hyper
    -"""
    -from collections import defaultdict
    -
    -from sympy.core.singleton import S
    -from sympy.core.numbers import Rational
    -from sympy.core.symbol import Symbol, Wild, Dummy
    -from sympy.core.relational import Equality
    -from sympy.core.add import Add
    -from sympy.core.mul import Mul
    -from sympy.core import sympify
    -
    -from sympy.simplify import simplify, hypersimp, hypersimilar
    -from sympy.solvers import solve, solve_undetermined_coeffs
    -from sympy.polys import Poly, quo, gcd, lcm, roots, resultant
    -from sympy.functions import binomial, factorial, FallingFactorial, RisingFactorial
    -from sympy.matrices import Matrix, casoratian
    -from sympy.concrete import product
    -from sympy.core.compatibility import default_sort_key
    -from sympy.utilities.iterables import numbered_symbols
    -
    -
    -
    [docs]def rsolve_poly(coeffs, f, n, **hints): - """Given linear recurrence operator L of order 'k' with polynomial - coefficients and inhomogeneous equation Ly = f, where 'f' is a - polynomial, we seek for all polynomial solutions over field K - of characteristic zero. - - The algorithm performs two basic steps: - - (1) Compute degree N of the general polynomial solution. - (2) Find all polynomials of degree N or less of Ly = f. - - There are two methods for computing the polynomial solutions. - If the degree bound is relatively small, i.e. it's smaller than - or equal to the order of the recurrence, then naive method of - undetermined coefficients is being used. This gives system - of algebraic equations with N+1 unknowns. - - In the other case, the algorithm performs transformation of the - initial equation to an equivalent one, for which the system of - algebraic equations has only 'r' indeterminates. This method is - quite sophisticated (in comparison with the naive one) and was - invented together by Abramov, Bronstein and Petkovsek. - - It is possible to generalize the algorithm implemented here to - the case of linear q-difference and differential equations. - - Lets say that we would like to compute m-th Bernoulli polynomial - up to a constant. For this we can use b(n+1) - b(n) == m*n**(m-1) - recurrence, which has solution b(n) = B_m + C. For example: - - >>> from sympy import Symbol, rsolve_poly - >>> n = Symbol('n', integer=True) - - >>> rsolve_poly([-1, 1], 4*n**3, n) - C0 + n**4 - 2*n**3 + n**2 - - For more information on implemented algorithms refer to: - - [1] S. A. Abramov, M. Bronstein and M. Petkovsek, On polynomial - solutions of linear operator equations, in: T. Levelt, ed., - Proc. ISSAC '95, ACM Press, New York, 1995, 290-296. - - [2] M. Petkovsek, Hypergeometric solutions of linear recurrences - with polynomial coefficients, J. Symbolic Computation, - 14 (1992), 243-264. - - [3] M. Petkovsek, H. S. Wilf, D. Zeilberger, A = B, 1996. - - """ - f = sympify(f) - - if not f.is_polynomial(n): - return None - - homogeneous = f.is_zero - - r = len(coeffs) - 1 - - coeffs = [ Poly(coeff, n) for coeff in coeffs ] - - polys = [ Poly(0, n) ] * (r + 1) - terms = [ (S.Zero, S.NegativeInfinity) ] *(r + 1) - - for i in range(0, r + 1): - for j in range(i, r + 1): - polys[i] += coeffs[j]*binomial(j, i) - - if not polys[i].is_zero: - (exp,), coeff = polys[i].LT() - terms[i] = (coeff, exp) - - d = b = terms[0][1] - - for i in range(1, r + 1): - if terms[i][1] > d: - d = terms[i][1] - - if terms[i][1] - i > b: - b = terms[i][1] - i - - d, b = int(d), int(b) - - x = Dummy('x') - - degree_poly = S.Zero - - for i in range(0, r + 1): - if terms[i][1] - i == b: - degree_poly += terms[i][0]*FallingFactorial(x, i) - - nni_roots = list(roots(degree_poly, x, filter='Z', - predicate=lambda r: r >= 0).keys()) - - if nni_roots: - N = [max(nni_roots)] - else: - N = [] - - if homogeneous: - N += [-b - 1] - else: - N += [f.as_poly(n).degree() - b, -b - 1] - - N = int(max(N)) - - if N < 0: - if homogeneous: - if hints.get('symbols', False): - return (S.Zero, []) - else: - return S.Zero - else: - return None - - if N <= r: - C = [] - y = E = S.Zero - - for i in range(0, N + 1): - C.append(Symbol('C' + str(i))) - y += C[i] * n**i - - for i in range(0, r + 1): - E += coeffs[i].as_expr()*y.subs(n, n + i) - - solutions = solve_undetermined_coeffs(E - f, C, n) - - if solutions is not None: - C = [ c for c in C if (c not in solutions) ] - result = y.subs(solutions) - else: - return None # TBD - else: - A = r - U = N + A + b + 1 - - nni_roots = list(roots(polys[r], filter='Z', - predicate=lambda r: r >= 0).keys()) - - if nni_roots != []: - a = max(nni_roots) + 1 - else: - a = S.Zero - - def _zero_vector(k): - return [S.Zero] * k - - def _one_vector(k): - return [S.One] * k - - def _delta(p, k): - B = S.One - D = p.subs(n, a + k) - - for i in range(1, k + 1): - B *= -Rational(k - i + 1, i) - D += B * p.subs(n, a + k - i) - - return D - - alpha = {} - - for i in range(-A, d + 1): - I = _one_vector(d + 1) - - for k in range(1, d + 1): - I[k] = I[k - 1] * (x + i - k + 1)/k - - alpha[i] = S.Zero - - for j in range(0, A + 1): - for k in range(0, d + 1): - B = binomial(k, i + j) - D = _delta(polys[j].as_expr(), k) - - alpha[i] += I[k]*B*D - - V = Matrix(U, A, lambda i, j: int(i == j)) - - if homogeneous: - for i in range(A, U): - v = _zero_vector(A) - - for k in range(1, A + b + 1): - if i - k < 0: - break - - B = alpha[k - A].subs(x, i - k) - - for j in range(0, A): - v[j] += B * V[i - k, j] - - denom = alpha[-A].subs(x, i) - - for j in range(0, A): - V[i, j] = -v[j] / denom - else: - G = _zero_vector(U) - - for i in range(A, U): - v = _zero_vector(A) - g = S.Zero - - for k in range(1, A + b + 1): - if i - k < 0: - break - - B = alpha[k - A].subs(x, i - k) - - for j in range(0, A): - v[j] += B * V[i - k, j] - - g += B * G[i - k] - - denom = alpha[-A].subs(x, i) - - for j in range(0, A): - V[i, j] = -v[j] / denom - - G[i] = (_delta(f, i - A) - g) / denom - - P, Q = _one_vector(U), _zero_vector(A) - - for i in range(1, U): - P[i] = (P[i - 1] * (n - a - i + 1)/i).expand() - - for i in range(0, A): - Q[i] = Add(*[ (v*p).expand() for v, p in zip(V[:, i], P) ]) - - if not homogeneous: - h = Add(*[ (g*p).expand() for g, p in zip(G, P) ]) - - C = [ Symbol('C' + str(i)) for i in range(0, A) ] - - g = lambda i: Add(*[ c*_delta(q, i) for c, q in zip(C, Q) ]) - - if homogeneous: - E = [ g(i) for i in range(N + 1, U) ] - else: - E = [ g(i) + _delta(h, i) for i in range(N + 1, U) ] - - if E != []: - solutions = solve(E, *C) - - if not solutions: - if homogeneous: - if hints.get('symbols', False): - return (S.Zero, []) - else: - return S.Zero - else: - return None - else: - solutions = {} - - if homogeneous: - result = S.Zero - else: - result = h - - for c, q in list(zip(C, Q)): - if c in solutions: - s = solutions[c]*q - C.remove(c) - else: - s = c*q - - result += s.expand() - - if hints.get('symbols', False): - return (result, C) - else: - return result - -
    -
    [docs]def rsolve_ratio(coeffs, f, n, **hints): - """Given linear recurrence operator L of order 'k' with polynomial - coefficients and inhomogeneous equation Ly = f, where 'f' is a - polynomial, we seek for all rational solutions over field K of - characteristic zero. - - This procedure accepts only polynomials, however if you are - interested in solving recurrence with rational coefficients - then use rsolve() which will pre-process the given equation - and run this procedure with polynomial arguments. - - The algorithm performs two basic steps: - - (1) Compute polynomial v(n) which can be used as universal - denominator of any rational solution of equation Ly = f. - - (2) Construct new linear difference equation by substitution - y(n) = u(n)/v(n) and solve it for u(n) finding all its - polynomial solutions. Return None if none were found. - - Algorithm implemented here is a revised version of the original - Abramov's algorithm, developed in 1989. The new approach is much - simpler to implement and has better overall efficiency. This - method can be easily adapted to q-difference equations case. - - Besides finding rational solutions alone, this functions is - an important part of Hyper algorithm were it is used to find - particular solution of inhomogeneous part of a recurrence. - - For more information on the implemented algorithm refer to: - - [1] S. A. Abramov, Rational solutions of linear difference - and q-difference equations with polynomial coefficients, - in: T. Levelt, ed., Proc. ISSAC '95, ACM Press, New York, - 1995, 285-289 - - Examples - ======== - - >>> from sympy.abc import x - >>> from sympy.solvers.recurr import rsolve_ratio - >>> rsolve_ratio([ - 2*x**3 + x**2 + 2*x - 1, 2*x**3 + x**2 - 6*x, - ... - 2*x**3 - 11*x**2 - 18*x - 9, 2*x**3 + 13*x**2 + 22*x + 8], 0, x) - C2*(2*x - 3)/(2*(x**2 - 1)) - - """ - f = sympify(f) - - if not f.is_polynomial(n): - return None - - coeffs = list(map(sympify, coeffs)) - - r = len(coeffs) - 1 - - A, B = coeffs[r], coeffs[0] - A = A.subs(n, n - r).expand() - - h = Dummy('h') - - res = resultant(A, B.subs(n, n + h), n) - - if not res.is_polynomial(h): - p, q = res.as_numer_denom() - res = quo(p, q, h) - - nni_roots = list(roots(res, h, filter='Z', - predicate=lambda r: r >= 0).keys()) - - if not nni_roots: - return rsolve_poly(coeffs, f, n, **hints) - else: - C, numers = S.One, [S.Zero]*(r + 1) - - for i in range(int(max(nni_roots)), -1, -1): - d = gcd(A, B.subs(n, n + i), n) - - A = quo(A, d, n) - B = quo(B, d.subs(n, n - i), n) - - C *= Mul(*[ d.subs(n, n - j) for j in range(0, i + 1) ]) - - denoms = [ C.subs(n, n + i) for i in range(0, r + 1) ] - - for i in range(0, r + 1): - g = gcd(coeffs[i], denoms[i], n) - - numers[i] = quo(coeffs[i], g, n) - denoms[i] = quo(denoms[i], g, n) - - for i in range(0, r + 1): - numers[i] *= Mul(*(denoms[:i] + denoms[i + 1:])) - - result = rsolve_poly(numers, f * Mul(*denoms), n, **hints) - - if result is not None: - if hints.get('symbols', False): - return (simplify(result[0] / C), result[1]) - else: - return simplify(result / C) - else: - return None - -
    -
    [docs]def rsolve_hyper(coeffs, f, n, **hints): - """Given linear recurrence operator L of order 'k' with polynomial - coefficients and inhomogeneous equation Ly = f we seek for all - hypergeometric solutions over field K of characteristic zero. - - The inhomogeneous part can be either hypergeometric or a sum - of a fixed number of pairwise dissimilar hypergeometric terms. - - The algorithm performs three basic steps: - - (1) Group together similar hypergeometric terms in the - inhomogeneous part of Ly = f, and find particular - solution using Abramov's algorithm. - - (2) Compute generating set of L and find basis in it, - so that all solutions are linearly independent. - - (3) Form final solution with the number of arbitrary - constants equal to dimension of basis of L. - - Term a(n) is hypergeometric if it is annihilated by first order - linear difference equations with polynomial coefficients or, in - simpler words, if consecutive term ratio is a rational function. - - The output of this procedure is a linear combination of fixed - number of hypergeometric terms. However the underlying method - can generate larger class of solutions - D'Alembertian terms. - - Note also that this method not only computes the kernel of the - inhomogeneous equation, but also reduces in to a basis so that - solutions generated by this procedure are linearly independent - - For more information on the implemented algorithm refer to: - - [1] M. Petkovsek, Hypergeometric solutions of linear recurrences - with polynomial coefficients, J. Symbolic Computation, - 14 (1992), 243-264. - - [2] M. Petkovsek, H. S. Wilf, D. Zeilberger, A = B, 1996. - - Examples - ======== - >>> from sympy.solvers import rsolve_hyper - >>> from sympy.abc import x - - >>> rsolve_hyper([-1, -1, 1], 0, x) - C0*(1/2 + sqrt(5)/2)**x + C1*(-sqrt(5)/2 + 1/2)**x - - >>> rsolve_hyper([-1, 1], 1+x, x) - C0 + x*(x + 1)/2 - - """ - coeffs = list(map(sympify, coeffs)) - - f = sympify(f) - - r, kernel = len(coeffs) - 1, [] - - if not f.is_zero: - if f.is_Add: - similar = {} - - for g in f.expand().args: - if not g.is_hypergeometric(n): - return None - - for h in similar.keys(): - if hypersimilar(g, h, n): - similar[h] += g - break - else: - similar[g] = S.Zero - - inhomogeneous = [] - - for g, h in similar.items(): - inhomogeneous.append(g + h) - elif f.is_hypergeometric(n): - inhomogeneous = [f] - else: - return None - - for i, g in enumerate(inhomogeneous): - coeff, polys = S.One, coeffs[:] - denoms = [ S.One ] * (r + 1) - - s = hypersimp(g, n) - - for j in range(1, r + 1): - coeff *= s.subs(n, n + j - 1) - - p, q = coeff.as_numer_denom() - - polys[j] *= p - denoms[j] = q - - for j in range(0, r + 1): - polys[j] *= Mul(*(denoms[:j] + denoms[j + 1:])) - - R = rsolve_poly(polys, Mul(*denoms), n) - - if not (R is None or R is S.Zero): - inhomogeneous[i] *= R - else: - return None - - result = Add(*inhomogeneous) - else: - result = S.Zero - - Z = Dummy('Z') - - p, q = coeffs[0], coeffs[r].subs(n, n - r + 1) - - p_factors = [ z for z in roots(p, n).keys() ] - q_factors = [ z for z in roots(q, n).keys() ] - - factors = [ (S.One, S.One) ] - - for p in p_factors: - for q in q_factors: - if p.is_integer and q.is_integer and p <= q: - continue - else: - factors += [(n - p, n - q)] - - p = [ (n - p, S.One) for p in p_factors ] - q = [ (S.One, n - q) for q in q_factors ] - - factors = p + factors + q - - for A, B in factors: - polys, degrees = [], [] - D = A*B.subs(n, n + r - 1) - - for i in range(0, r + 1): - a = Mul(*[ A.subs(n, n + j) for j in range(0, i) ]) - b = Mul(*[ B.subs(n, n + j) for j in range(i, r) ]) - - poly = quo(coeffs[i]*a*b, D, n) - polys.append(poly.as_poly(n)) - - if not poly.is_zero: - degrees.append(polys[i].degree()) - - d, poly = max(degrees), S.Zero - - for i in range(0, r + 1): - coeff = polys[i].nth(d) - - if coeff is not S.Zero: - poly += coeff * Z**i - - for z in roots(poly, Z).keys(): - if not z.is_real or z.is_zero: - continue - - C = rsolve_poly([ polys[i]*z**i for i in range(r + 1) ], 0, n) - - if C is not None and C is not S.Zero: - ratio = z * A * C.subs(n, n + 1) / B / C - ratio = simplify(ratio) - # If there is a nonnegative root in the denominator of the ratio, - # this indicates that the term y(n_root) is zero, and one should - # start the product with the term y(n_root + 1). - n0 = 0 - for n_root in list(roots(ratio.as_numer_denom()[1], n).keys()): - n0 = max(n0, n_root + 1) - K = product(ratio, (n, n0, n - 1)) - if K.has(factorial, FallingFactorial, RisingFactorial): - K = simplify(K) - - if casoratian(kernel + [K], n, zero=False) != 0: - kernel.append(K) - - symbols = numbered_symbols('C') - kernel.sort(key=default_sort_key) - sk = list(zip(symbols, kernel)) - - for C, ker in sk: - result += C * ker - - if hints.get('symbols', False): - return (result, [s for s, k in sk]) - else: - return result - -
    -
    [docs]def rsolve(f, y, init=None): - """ - Solve univariate recurrence with rational coefficients. - - Given k-th order linear recurrence Ly = f, or equivalently: - - a_{k}(n) y(n+k) + a_{k-1}(n) y(n+k-1) + ... + a_{0}(n) y(n) = f - - where a_{i}(n), for i=0..k, are polynomials or rational functions - in n, and f is a hypergeometric function or a sum of a fixed number - of pairwise dissimilar hypergeometric terms in n, finds all solutions - or returns None, if none were found. - - Initial conditions can be given as a dictionary in two forms: - - [1] { n_0 : v_0, n_1 : v_1, ..., n_m : v_m } - [2] { y(n_0) : v_0, y(n_1) : v_1, ..., y(n_m) : v_m } - - or as a list L of values: - - L = [ v_0, v_1, ..., v_m ] - - where L[i] = v_i, for i=0..m, maps to y(n_i). - - As an example lets consider the following recurrence: - - (n - 1) y(n + 2) - (n**2 + 3 n - 2) y(n + 1) + 2 n (n + 1) y(n) == 0 - - >>> from sympy import Function, rsolve - >>> from sympy.abc import n - >>> y = Function('y') - - >>> f = (n-1)*y(n+2) - (n**2+3*n-2)*y(n+1) + 2*n*(n+1)*y(n) - - >>> rsolve(f, y(n)) - 2**n*C0 + C1*n! - - >>> rsolve(f, y(n), { y(0):0, y(1):3 }) - 3*2**n - 3*n! - - """ - if isinstance(f, Equality): - f = f.lhs - f.rhs - - n = y.args[0] - k = Wild('k', exclude=(n,)) - - h_part = defaultdict(lambda: S.Zero) - i_part = S.Zero - for g in Add.make_args(f): - coeff = S.One - kspec = None - for h in Mul.make_args(g): - if h.is_Function: - if h.func == y.func: - result = h.args[0].match(n + k) - - if result is not None: - kspec = int(result[k]) - else: - raise ValueError( - "'%s(%s+k)' expected, got '%s'" % (y.func, n, h)) - else: - raise ValueError( - "'%s' expected, got '%s'" % (y.func, h.func)) - else: - coeff *= h - - if kspec is not None: - h_part[kspec] += coeff - else: - i_part += coeff - - for k, coeff in h_part.items(): - h_part[k] = simplify(coeff) - - common = S.One - - for coeff in h_part.values(): - if coeff.is_rational_function(n): - if not coeff.is_polynomial(n): - common = lcm(common, coeff.as_numer_denom()[1], n) - else: - raise ValueError( - "Polynomial or rational function expected, got '%s'" % coeff) - - i_numer, i_denom = i_part.as_numer_denom() - - if i_denom.is_polynomial(n): - common = lcm(common, i_denom, n) - - if common is not S.One: - for k, coeff in h_part.items(): - numer, denom = coeff.as_numer_denom() - h_part[k] = numer*quo(common, denom, n) - - i_part = i_numer*quo(common, i_denom, n) - - K_min = min(h_part.keys()) - - if K_min < 0: - K = abs(K_min) - - H_part = defaultdict(lambda: S.Zero) - i_part = i_part.subs(n, n + K).expand() - common = common.subs(n, n + K).expand() - - for k, coeff in h_part.items(): - H_part[k + K] = coeff.subs(n, n + K).expand() - else: - H_part = h_part - - K_max = max(H_part.keys()) - coeffs = [H_part[i] for i in range(K_max + 1)] - - result = rsolve_hyper(coeffs, -i_part, n, symbols=True) - - if result is None: - return None - - solution, symbols = result - - if init == {} or init == []: - init = None - - if symbols and init is not None: - equations = [] - - if type(init) is list: - for i in range(0, len(init)): - eq = solution.subs(n, i) - init[i] - equations.append(eq) - else: - for k, v in init.items(): - try: - i = int(k) - except TypeError: - if k.is_Function and k.func == y.func: - i = int(k.args[0]) - else: - raise ValueError( - "Integer or term expected, got '%s'" % k) - - eq = solution.subs(n, i) - v - equations.append(eq) - - result = solve(equations, *symbols) - - if not result: - return None - else: - for k, v in result.items(): - solution = solution.subs(k, v) - - return solution
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/solvers/solvers.html b/dev-py3k/_modules/sympy/solvers/solvers.html deleted file mode 100644 index 32f10ca6fca..00000000000 --- a/dev-py3k/_modules/sympy/solvers/solvers.html +++ /dev/null @@ -1,2572 +0,0 @@ - - - - - - - - - - sympy.solvers.solvers — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.solvers.solvers

    -"""
    -This module contain solvers for all kinds of equations:
    -
    -    - algebraic or transcendental, use solve()
    -
    -    - recurrence, use rsolve()
    -
    -    - differential, use dsolve()
    -
    -    - nonlinear (numerically), use nsolve()
    -      (you will need a good starting point)
    -
    -"""
    -
    -from sympy.core.compatibility import (iterable, is_sequence, ordered,
    -    default_sort_key, reduce)
    -from sympy.utilities.exceptions import SymPyDeprecationWarning
    -from sympy.core.sympify import sympify
    -from sympy.core import (C, S, Add, Symbol, Wild, Equality, Dummy, Basic,
    -    Expr, Mul)
    -from sympy.core.function import (expand_mul, expand_multinomial, expand_log,
    -                          Derivative, AppliedUndef, UndefinedFunction, nfloat,
    -                          count_ops)
    -from sympy.core.numbers import ilcm, Float
    -from sympy.core.relational import Relational
    -from sympy.logic.boolalg import And, Or
    -from sympy.core.basic import preorder_traversal
    -
    -from sympy.functions import (log, exp, LambertW, cos, sin, tan, cot, cosh,
    -                             sinh, tanh, coth, acos, asin, atan, acot, acosh,
    -                             asinh, atanh, acoth, Abs, sign)
    -from sympy.functions.elementary.miscellaneous import real_root
    -from sympy.simplify import (simplify, collect, powsimp, posify, powdenest,
    -                            nsimplify)
    -from sympy.simplify.sqrtdenest import sqrt_depth, _mexpand
    -from sympy.matrices import Matrix, zeros
    -from sympy.polys import roots, cancel, Poly, together, factor
    -from sympy.functions.elementary.piecewise import piecewise_fold, Piecewise
    -
    -from sympy.utilities.lambdify import lambdify
    -from sympy.utilities.misc import filldedent
    -from sympy.mpmath import findroot
    -
    -from sympy.solvers.polysys import solve_poly_system
    -from sympy.solvers.inequalities import reduce_inequalities
    -
    -from sympy.assumptions import Q, ask
    -
    -from types import GeneratorType
    -from collections import defaultdict
    -from functools import reduce
    -
    -
    -def _ispow(e):
    -    """Return True if e is a Pow or is exp."""
    -    return e.is_Pow or e.func is exp
    -
    -
    -def denoms(eq, symbols=None):
    -    """Return (recursively) set of all denominators that appear in eq
    -    that contain any symbol in iterable ``symbols``; if ``symbols`` is
    -    None (default) then all denominators with symbols will be returned.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.solvers.solvers import denoms
    -    >>> from sympy.abc import x, y, z
    -
    -    >>> denoms(x/y)
    -    set([y])
    -
    -    >>> denoms(x/(y*z))
    -    set([y, z])
    -
    -    >>> denoms(3/x + y/z)
    -    set([x, z])
    -    """
    -
    -    symbols = symbols or eq.free_symbols
    -    dens = set()
    -    if not symbols or not eq.has(*symbols):
    -        return dens
    -    pt = preorder_traversal(eq)
    -    for e in pt:
    -        if _ispow(e):
    -            n, d = e.as_numer_denom()
    -            if d in dens:
    -                pt.skip()
    -            elif d.has(*symbols):
    -                dens.add(d.as_base_exp()[0])
    -    return dens
    -
    -
    -
    [docs]def checksol(f, symbol, sol=None, **flags): - """Checks whether sol is a solution of equation f == 0. - - Input can be either a single symbol and corresponding value - or a dictionary of symbols and values. ``f`` can be a single - equation or an iterable of equations. A solution must satisfy - all equations in ``f`` to be considered valid; if a solution - does not satisfy any equation, False is returned; if one or - more checks are inconclusive (and none are False) then None - is returned. - - Examples - ======== - - >>> from sympy import symbols - >>> from sympy.solvers import checksol - >>> x, y = symbols('x,y') - >>> checksol(x**4 - 1, x, 1) - True - >>> checksol(x**4 - 1, x, 0) - False - >>> checksol(x**2 + y**2 - 5**2, {x: 3, y: 4}) - True - - To check if an expression is zero using checksol, pass it - as ``f`` and send an empty dictionary for ``symbol``: - - >>> checksol(x**2 + x - x*(x + 1), {}) - True - - None is returned if checksol() could not conclude. - - flags: - 'numerical=True (default)' - do a fast numerical check if ``f`` has only one symbol. - 'minimal=True (default is False)' - a very fast, minimal testing. - 'warn=True (default is False)' - print a warning if checksol() could not conclude. - 'simplify=True (default)' - simplify solution before substituting into function and - simplify the function before trying specific simplifications - 'force=True (default is False)' - make positive all symbols without assumptions regarding sign. - - """ - - if sol is not None: - sol = {symbol: sol} - elif isinstance(symbol, dict): - sol = symbol - else: - msg = 'Expecting sym, val or {sym: val}, None but got %s, %s' - raise ValueError(msg % (symbol, sol)) - - if iterable(f): - if not f: - raise ValueError('no functions to check') - rv = True - for fi in f: - check = checksol(fi, sol, **flags) - if check: - continue - if check is False: - return False - rv = None # don't return, wait to see if there's a False - return rv - - if isinstance(f, Poly): - f = f.as_expr() - elif isinstance(f, Equality): - f = f.lhs - f.rhs - - if not f: - return True - - if sol and not f.has(*list(sol.keys())): - # if f(y) == 0, x=3 does not set f(y) to zero...nor does it not - return None - - illegal = set([S.NaN, - S.ComplexInfinity, - S.Infinity, - S.NegativeInfinity]) - if any(sympify(v).atoms() & illegal for k, v in sol.items()): - return False - - was = f - attempt = -1 - numerical = flags.get('numerical', True) - while 1: - attempt += 1 - if attempt == 0: - val = f.subs(sol) - if val.atoms() & illegal: - return False - elif attempt == 1: - if val.free_symbols: - if not val.is_constant(*list(sol.keys())): - return False - # there are free symbols -- simple expansion might work - _, val = val.as_content_primitive() - val = expand_mul(expand_multinomial(val)) - elif attempt == 2: - if flags.get('minimal', False): - return - if flags.get('simplify', True): - for k in sol: - sol[k] = simplify(sol[k]) - # start over without the failed expanded form, possibly - # with a simplified solution - val = f.subs(sol) - if flags.get('force', True): - val, reps = posify(val) - # expansion may work now, so try again and check - exval = expand_mul(expand_multinomial(val)) - if exval.is_number or not exval.free_symbols: - # we can decide now - val = exval - elif attempt == 3: - val = powsimp(val) - elif attempt == 4: - val = cancel(val) - elif attempt == 5: - val = val.expand() - elif attempt == 6: - val = together(val) - elif attempt == 7: - val = powsimp(val) - else: - # if there are no radicals and no functions then this can't be - # zero anymore -- can it? - pot = preorder_traversal(expand_mul(val)) - seen = set() - saw_pow_func = False - for p in pot: - if p in seen: - continue - seen.add(p) - if p.is_Pow and not p.exp.is_Integer: - saw_pow_func = True - elif p.is_Function: - saw_pow_func = True - elif isinstance(p, UndefinedFunction): - saw_pow_func = True - if saw_pow_func: - break - if saw_pow_func is False: - return False - if flags.get('force', True): - # don't do a zero check with the positive assumptions in place - val = val.subs(reps) - nz = val.is_nonzero - if nz is not None: - # issue 2574: nz may be True even when False - # so these are just hacks to keep a false positive - # from being returned - - # HACK 1: LambertW (issue 2574) - if val.is_number and val.has(LambertW): - # don't eval this to verify solution since if we got here, - # numerical must be False - return None - - # add other HACKs here if necessary, otherwise we assume - # the nz value is correct - return not nz - break - - if val == was: - continue - elif val.is_Rational: - return val == 0 - if numerical and not val.free_symbols: - return abs(val.n(18).n(12, chop=True)) < 1e-9 - was = val - - if flags.get('warn', False): - print(("\n\tWarning: could not verify solution %s." % sol)) - # returns None if it can't conclude - # TODO: improve solution testing - -
    -
    [docs]def check_assumptions(expr, **assumptions): - """Checks whether expression `expr` satisfies all assumptions. - - `assumptions` is a dict of assumptions: {'assumption': True|False, ...}. - - Examples - ======== - - >>> from sympy import Symbol, pi, I, exp - >>> from sympy.solvers.solvers import check_assumptions - - >>> check_assumptions(-5, integer=True) - True - >>> check_assumptions(pi, real=True, integer=False) - True - >>> check_assumptions(pi, real=True, negative=True) - False - >>> check_assumptions(exp(I*pi/7), real=False) - True - - >>> x = Symbol('x', real=True, positive=True) - >>> check_assumptions(2*x + 1, real=True, positive=True) - True - >>> check_assumptions(-2*x - 5, real=True, positive=True) - False - - `None` is returned if check_assumptions() could not conclude. - - >>> check_assumptions(2*x - 1, real=True, positive=True) - >>> z = Symbol('z') - >>> check_assumptions(z, real=True) - """ - expr = sympify(expr) - - result = True - for key, expected in assumptions.items(): - if expected is None: - continue - test = getattr(expr, 'is_' + key, None) - if test is expected: - continue - elif test is not None: - return False - result = None # Can't conclude, unless an other test fails. - return result - -
    -
    [docs]def solve(f, *symbols, **flags): - """ - Algebraically solves equations and systems of equations. - - Currently supported are: - - univariate polynomial, - - transcendental - - piecewise combinations of the above - - systems of linear and polynomial equations - - sytems containing relational expressions. - - Input is formed as: - - * f - - a single Expr or Poly that must be zero, - - an Equality - - a Relational expression or boolean - - iterable of one or more of the above - - * symbols (object(s) to solve for) specified as - - none given (other non-numeric objects will be used) - - single symbol - - denested list of symbols - e.g. solve(f, x, y) - - ordered iterable of symbols - e.g. solve(f, [x, y]) - - * flags - 'dict'=True (default is False) - return list (perhaps empty) of solution mappings - 'set'=True (default is False) - return list of symbols and set of tuple(s) of solution(s) - 'exclude=[] (default)' - don't try to solve for any of the free symbols in exclude; - if expressions are given, the free symbols in them will - be extracted automatically. - 'check=True (default)' - If False, don't do any testing of solutions. This can be - useful if one wants to include solutions that make any - denominator zero. - 'numerical=True (default)' - do a fast numerical check if ``f`` has only one symbol. - 'minimal=True (default is False)' - a very fast, minimal testing. - 'warning=True (default is False)' - print a warning if checksol() could not conclude. - 'simplify=True (default)' - simplify all but cubic and quartic solutions before - returning them and (if check is not False) use the - general simplify function on the solutions and the - expression obtained when they are substituted into the - function which should be zero - 'force=True (default is False)' - make positive all symbols without assumptions regarding sign. - 'rational=True (default)' - recast Floats as Rational; if this option is not used, the - system containing floats may fail to solve because of issues - with polys. If rational=None, Floats will be recast as - rationals but the answer will be recast as Floats. If the - flag is False then nothing will be done to the Floats. - 'manual=True (default is False)' - do not use the polys/matrix method to solve a system of - equations, solve them one at a time as you might "manually". - 'implicit=True (default is False)' - allows solve to return a solution for a pattern in terms of - other functions that contain that pattern; this is only - needed if the pattern is inside of some invertible function - like cos, exp, .... - - Examples - ======== - - The output varies according to the input and can be seen by example:: - - >>> from sympy import solve, Poly, Eq, Function, exp - >>> from sympy.abc import x, y, z, a, b - >>> f = Function('f') - - * boolean or univariate Relational - - >>> solve(x < 3) - And(im(x) == 0, re(x) < 3) - - * to always get a list of solution mappings, use flag dict=True - - >>> solve(x - 3, dict=True) - [{x: 3}] - >>> solve([x - 3, y - 1], dict=True) - [{x: 3, y: 1}] - - * to get a list of symbols and set of solution(s) use flag set=True - - >>> solve([x**2 - 3, y - 1], set=True) - ([x, y], set([(-sqrt(3), 1), (sqrt(3), 1)])) - - * single expression and single symbol that is in the expression - - >>> solve(x - y, x) - [y] - >>> solve(x - 3, x) - [3] - >>> solve(Eq(x, 3), x) - [3] - >>> solve(Poly(x - 3), x) - [3] - >>> set(solve(x**2 - y**2, x)) - set([-y, y]) - >>> set(solve(x**4 - 1, x)) - set([-1, 1, -I, I]) - - * single expression with no symbol that is in the expression - - >>> solve(3, x) - [] - >>> solve(x - 3, y) - [] - - * single expression with no symbol given - - In this case, all free symbols will be selected as potential - symbols to solve for. If the equation is univariate then a list - of solutionsis returned; otherwise -- as is the case when symbols are - given as an iterable of length > 1 -- a list of mappings will be returned. - - >>> solve(x - 3) - [3] - >>> solve(x**2 - y**2) # doctest: +SKIP - [{x: -y}, {x: y}] - >>> solve(z**2*x**2 - z**2*y**2) # doctest: +SKIP - [{x: -y}, {x: y}] - >>> solve(z**2*x - z**2*y**2) - [{x: y**2}] - - * when an object other than a Symbol is given as a symbol, it is - isolated algebraically and an implicit solution may be obtained. - This is mostly provided as a convenience to save one from replacing - the object with a Symbol and solving for that Symbol. It will only - work if the specified object can be replaced with a Symbol using the - subs method. - - >>> solve(f(x) - x, f(x)) - [x] - >>> solve(f(x).diff(x) - f(x) - x, f(x).diff(x)) - [x + f(x)] - >>> solve(f(x).diff(x) - f(x) - x, f(x)) - [-x + Derivative(f(x), x)] - >>> set(solve(x + exp(x)**2, exp(x))) - set([-sqrt(-x), sqrt(-x)]) - - >>> from sympy import Indexed, IndexedBase, Tuple, sqrt - >>> A = IndexedBase('A') - >>> eqs = Tuple(A[1] + A[2] - 3, A[1] - A[2] + 1) - >>> solve(eqs, eqs.atoms(Indexed)) - {A[1]: 1, A[2]: 2} - - * To solve for a *symbol* implicitly, use 'implicit=True': - - >>> solve(x + exp(x), x) - [-LambertW(1)] - >>> solve(x + exp(x), x, implicit=True) - [-exp(x)] - - * It is possible to solve for anything that can be targeted with - subs: - - >>> solve(x + 2 + sqrt(3), x + 2) - [-sqrt(3)] - >>> solve((x + 2 + sqrt(3), x + 4 + y), y, x + 2) - {y: -2 + sqrt(3), x + 2: -sqrt(3)} - - * Nothing heroic is done in this implicit solving so you may end up - with a symbol still in the solution: - - >>> eqs = (x*y + 3*y + sqrt(3), x + 4 + y) - >>> solve(eqs, y, x + 2) - {y: sqrt(3)/(-x - 3), x + 2: (-2*x - 6 + sqrt(3))/(x + 3)} - >>> solve(eqs, y*x, x) - {x: -y - 4, x*y: -3*y - sqrt(3)} - - * if you attempt to solve for a number remember that the number - you have obtained does not necessarily mean that the value is - equivalent to the expression obtained: - - >>> solve(sqrt(2) - 1, 1) - [sqrt(2)] - >>> solve(x - y + 1, 1) # /!\ -1 is targeted, too - [x/(y - 1)] - >>> [_.subs(z, -1) for _ in solve((x - y + 1).subs(-1, z), 1)] - [-x + y] - - * To solve for a function within a derivative, use dsolve. - - * single expression and more than 1 symbol - - * when there is a linear solution - - >>> solve(x - y**2, x, y) - [{x: y**2}] - >>> solve(x**2 - y, x, y) - [{y: x**2}] - - * when undetermined coefficients are identified - - * that are linear - - >>> solve((a + b)*x - b + 2, a, b) - {a: -2, b: 2} - - * that are nonlinear - - >>> set(solve((a + b)*x - b**2 + 2, a, b)) - set([(-sqrt(2), sqrt(2)), (sqrt(2), -sqrt(2))]) - - * if there is no linear solution then the first successful - attempt for a nonlinear solution will be returned - - >>> solve(x**2 - y**2, x, y) # doctest: +SKIP - [{x: -y}, {x: y}] - >>> solve(x**2 - y**2/exp(x), x, y) - [{x: 2*LambertW(y/2)}] - >>> solve(x**2 - y**2/exp(x), y, x) # doctest: +SKIP - [{y: -x*exp(x/2)}, {y: x*exp(x/2)}] - - * iterable of one or more of the above - - * involving relationals or bools - - >>> solve([x < 3, x - 2]) - And(im(x) == 0, re(x) == 2) - >>> solve([x > 3, x - 2]) - False - - * when the system is linear - - * with a solution - - >>> solve([x - 3], x) - {x: 3} - >>> solve((x + 5*y - 2, -3*x + 6*y - 15), x, y) - {x: -3, y: 1} - >>> solve((x + 5*y - 2, -3*x + 6*y - 15), x, y, z) - {x: -3, y: 1} - >>> solve((x + 5*y - 2, -3*x + 6*y - z), z, x, y) - {x: -5*y + 2, z: 21*y - 6} - - * without a solution - - >>> solve([x + 3, x - 3]) - [] - - * when the system is not linear - - >>> set(solve([x**2 + y -2, y**2 - 4], x, y)) - set([(-2, -2), (0, 2), (2, -2)]) - - * if no symbols are given, all free symbols will be selected and a list - of mappings returned - - >>> solve([x - 2, x**2 + y]) - [{x: 2, y: -4}] - >>> solve([x - 2, x**2 + f(x)], set([f(x), x])) - [{x: 2, f(x): -4}] - - * if any equation doesn't depend on the symbol(s) given it will be - eliminated from the equation set and an answer may be given - implicitly in terms of variables that were not of interest - - >>> solve([x - y, y - 3], x) - {x: y} - - Notes - ===== - - assumptions aren't checked when `solve()` input involves - relationals or bools. - - When the solutions are checked, those that make any denominator zero - are automatically excluded. If you do not want to exclude such solutions - then use the check=False option: - - >>> from sympy import sin, limit - >>> solve(sin(x)/x) - [] - - If check=False then a solution to the numerator being zero is found: x = 0. - In this case, this is a spurious solution since sin(x)/x has the well known - limit (without dicontinuity) of 1 at x = 0: - - >>> solve(sin(x)/x, check=False) - [0] - - In the following case, however, the limit exists and is equal to the the - value of x = 0 that is excluded when check=True: - - >>> eq = x**2*(1/x - z**2/x) - >>> solve(eq, x) - [] - >>> solve(eq, x, check=False) - [0] - >>> limit(eq, x, 0, '-') - 0 - >>> limit(eq, x, 0, '+') - 0 - - - See Also - ======== - - - rsolve() for solving recurrence relationships - - dsolve() for solving differential equations - - """ - # make f and symbols into lists of sympified quantities - # keeping track of how f was passed since if it is a list - # a dictionary of results will be returned. - ########################################################################### - - def _sympified_list(w): - return list(map(sympify, w if iterable(w) else [w])) - bare_f = not iterable(f) - ordered_symbols = (symbols and - symbols[0] and - (isinstance(symbols[0], Symbol) or - is_sequence(symbols[0], - include=GeneratorType) - ) - ) - f, symbols = (_sympified_list(w) for w in [f, symbols]) - - implicit = flags.get('implicit', False) - - # preprocess equation(s) - ########################################################################### - for i, fi in enumerate(f): - if isinstance(fi, Equality): - f[i] = fi.lhs - fi.rhs - elif isinstance(fi, Poly): - f[i] = fi.as_expr() - elif isinstance(fi, bool) or fi.is_Relational: - return reduce_inequalities(f, assume=flags.get('assume'), - symbols=symbols) - # Any embedded piecewise functions need to be brought out to the - # top level so that the appropriate strategy gets selected. - f[i] = piecewise_fold(f[i]) - - # preprocess symbol(s) - ########################################################################### - if not symbols: - # get symbols from equations - symbols = reduce(set.union, [fi.free_symbols - for fi in f], set()) - if len(symbols) < len(f): - for fi in f: - pot = preorder_traversal(fi) - for p in pot: - if not (p.is_number or p.is_Add or p.is_Mul) or \ - isinstance(p, AppliedUndef): - flags['dict'] = True # better show symbols - symbols.add(p) - pot.skip() # don't go any deeper - symbols = list(symbols) - # supply dummy symbols so solve(3) behaves like solve(3, x) - for i in range(len(f) - len(symbols)): - symbols.append(Dummy()) - - ordered_symbols = False - elif len(symbols) == 1 and iterable(symbols[0]): - symbols = symbols[0] - - # remove symbols the user is not interested in - exclude = flags.pop('exclude', set()) - if exclude: - if isinstance(exclude, Expr): - exclude = [exclude] - exclude = reduce(set.union, [e.free_symbols for e in sympify(exclude)]) - symbols = [s for s in symbols if s not in exclude] - - if not ordered_symbols: - # we do this to make the results returned canonical in case f - # contains a system of nonlinear equations; all other cases should - # be unambiguous - symbols = sorted(symbols, key=default_sort_key) - - # we can solve for non-symbol entities by replacing them with Dummy symbols - symbols_new = [] - symbol_swapped = False - for i, s in enumerate(symbols): - if s.is_Symbol: - s_new = s - else: - symbol_swapped = True - s_new = Dummy('X%d' % i) - symbols_new.append(s_new) - - if symbol_swapped: - swap_sym = list(zip(symbols, symbols_new)) - f = [fi.subs(swap_sym) for fi in f] - symbols = symbols_new - swap_sym = dict([(v, k) for k, v in swap_sym]) - else: - swap_sym = {} - - # this is needed in the next two events - symset = set(symbols) - - # get rid of equations that have no symbols of interest; we don't - # try to solve them because the user didn't ask and they might be - # hard to solve; this means that solutions may be given in terms - # of the eliminated equations e.g. solve((x-y, y-3), x) -> {x: y} - newf = [] - for fi in f: - # let the solver handle equations that.. - # - have no symbols but are expressions - # - have symbols of interest - # - have no symbols of interest but are constant - # but when an expression is not constant and has no symbols of - # interest, it can't change what we obtain for a solution from - # the remaining equations so we don't include it; and if it's - # zero it can be removed and if it's not zero, there is no - # solution for the equation set as a whole - # - # The reason for doing this filtering is to allow an answer - # to be obtained to queries like solve((x - y, y), x); without - # this mod the return value is [] - ok = False - if fi.has(*symset): - ok = True - else: - free = fi.free_symbols - if not free: - if fi.is_Number: - if fi.is_zero: - continue - return [] - ok = True - else: - if fi.is_constant(): - ok = True - if ok: - newf.append(fi) - if not newf: - return [] - f = newf - del newf - - # mask off any Object that we aren't going to invert: Derivative, - # Integral, etc... so that solving for anything that they contain will - # give an implicit solution - seen = set() - non_inverts = set() - for fi in f: - pot = preorder_traversal(fi) - for p in pot: - if isinstance(p, bool) or isinstance(p, Piecewise): - pass - elif (isinstance(p, bool) or - not p.args or - p in symset or - p.is_Add or p.is_Mul or - p.is_Pow and not implicit or - p.is_Function and not isinstance(p, AppliedUndef) and - not implicit): - continue - elif not p in seen: - seen.add(p) - if p.free_symbols & symset: - non_inverts.add(p) - else: - continue - pot.skip() - del seen - non_inverts = dict(list(zip(non_inverts, [Dummy() for d in non_inverts]))) - f = [fi.subs(non_inverts) for fi in f] - non_inverts = [(v, k.subs(swap_sym)) for k, v in non_inverts.items()] - - # rationalize Floats - floats = False - if flags.get('rational', True) is not False: - for i, fi in enumerate(f): - if fi.has(Float): - floats = True - f[i] = nsimplify(fi, rational=True) - - # - # try to get a solution - ########################################################################### - if bare_f: - solution = _solve(f[0], *symbols, **flags) - else: - solution = _solve_system(f, symbols, **flags) - - # - # postprocessing - ########################################################################### - # Restore masked-off objects - if non_inverts: - - def _do_dict(solution): - return dict([(k, v.subs(non_inverts)) for k, v in - solution.items()]) - for i in range(1): - if type(solution) is dict: - solution = _do_dict(solution) - break - elif solution and type(solution) is list: - if type(solution[0]) is dict: - solution = [_do_dict(s) for s in solution] - break - elif type(solution[0]) is tuple: - solution = [tuple([v.subs(non_inverts) for v in s]) for s - in solution] - break - else: - solution = [v.subs(non_inverts) for v in solution] - break - elif not solution: - break - else: - raise NotImplementedError(filldedent(''' - no handling of %s was implemented''' % solution)) - - # Restore original "symbols" if a dictionary is returned. - # This is not necessary for - # - the single univariate equation case - # since the symbol will have been removed from the solution; - # - the nonlinear poly_system since that only supports zero-dimensional - # systems and those results come back as a list - # - # ** unless there were Derivatives with the symbols, but those were handled - # above. - if symbol_swapped: - symbols = [swap_sym[k] for k in symbols] - if type(solution) is dict: - solution = dict([(swap_sym[k], v.subs(swap_sym)) - for k, v in solution.items()]) - elif solution and type(solution) is list and type(solution[0]) is dict: - for i, sol in enumerate(solution): - solution[i] = dict([(swap_sym[k], v.subs(swap_sym)) - for k, v in sol.items()]) - - # undo the dictionary solutions returned when the system was only partially - # solved with poly-system if all symbols are present - if ( - solution and - ordered_symbols and - type(solution) is not dict and - type(solution[0]) is dict and - all(s in solution[0] for s in symbols) - ): - solution = [tuple([r[s].subs(r) for s in symbols]) for r in solution] - - # Get assumptions about symbols, to filter solutions. - # Note that if assumptions about a solution can't be verified, it is still - # returned. - check = flags.get('check', True) - - # restore floats - if floats and solution and flags.get('rational', None) is None: - solution = nfloat(solution, exponent=False) - - if check and solution: - - warning = flags.get('warn', False) - got_None = [] # solutions for which one or more symbols gave None - no_False = [] # solutions for which no symbols gave False - if type(solution) is list: - if type(solution[0]) is tuple: - for sol in solution: - for symb, val in zip(symbols, sol): - test = check_assumptions(val, **symb.assumptions0) - if test is False: - break - if test is None: - got_None.append(sol) - else: - no_False.append(sol) - elif type(solution[0]) is dict: - for sol in solution: - a_None = False - for symb, val in sol.items(): - test = check_assumptions(val, **symb.assumptions0) - if test: - continue - if test is False: - break - a_None = True - else: - no_False.append(sol) - if a_None: - got_None.append(sol) - else: # list of expressions - for sol in solution: - test = check_assumptions(sol, **symbols[0].assumptions0) - if test is False: - continue - no_False.append(sol) - if test is None: - got_None.append(sol) - - elif type(solution) is dict: - a_None = False - for symb, val in solution.items(): - test = check_assumptions(val, **symb.assumptions0) - if test: - continue - if test is False: - no_False = None - break - a_None = True - else: - no_False = solution - if a_None: - got_None.append(solution) - - elif isinstance(solution, (Relational, And, Or)): - assert len(symbols) == 1 - if warning and symbols[0].assumptions0: - print((filldedent(""" - \tWarning: assumptions about variable '%s' are - not handled currently.""" % symbols[0]))) - # TODO: check also variable assumptions for inequalities - - else: - raise TypeError('Unrecognized solution') # improve the checker - - solution = no_False - if warning and got_None: - print((filldedent(""" - \tWarning: assumptions concerning following solution(s) - can't be checked:""" + '\n\t' + - ', '.join(str(s) for s in got_None)))) - - # - # done - ########################################################################### - - as_dict = flags.get('dict', False) - as_set = flags.get('set', False) - - if not as_set and isinstance(solution, list): - # Make sure that a list of solutions is ordered in a canonical way. - solution.sort(key=default_sort_key) - - if not as_dict and not as_set: - return solution or [] - - # make return a list of mappings or [] - if not solution: - solution = [] - else: - if isinstance(solution, dict): - solution = [solution] - elif iterable(solution[0]): - solution = [dict(list(zip(symbols, s))) for s in solution] - elif isinstance(solution[0], dict): - pass - else: - assert len(symbols) == 1 - solution = [{symbols[0]: s} for s in solution] - if as_dict: - return solution - assert as_set - if not solution: - return [], set() - k = sorted(list(solution[0].keys()), key=lambda i: i.sort_key()) - return k, set([tuple([s[ki] for ki in k]) for s in solution]) - -
    -def _solve(f, *symbols, **flags): - """Return a checked solution for f in terms of one or more of the - symbols.""" - - if len(symbols) != 1: - soln = None - free = f.free_symbols - ex = free - set(symbols) - if len(ex) != 1: - ind, dep = f.as_independent(*symbols) - ex = ind.free_symbols & dep.free_symbols - if len(ex) == 1: - ex = ex.pop() - try: - # may come back as dict or list (if non-linear) - soln = solve_undetermined_coeffs(f, symbols, ex) - except NotImplementedError: - pass - if soln: - return soln - # find first successful solution - failed = [] - for s in symbols: - n, d = solve_linear(f, symbols=[s]) - if n.is_Symbol: - # no need to check but we should simplify if desired - if flags.get('simplify', True): - d = simplify(d) - return [{n: d}] - elif n and d: # otherwise there was no solution for s - failed.append(s) - if not failed: - return [] - for s in failed: - try: - soln = _solve(f, s, **flags) - return [{s: sol} for sol in soln] - except NotImplementedError: - continue - else: - msg = "No algorithms are implemented to solve equation %s" - raise NotImplementedError(msg % f) - symbol = symbols[0] - - check = flags.get('check', True) - # build up solutions if f is a Mul - if f.is_Mul: - result = set() - dens = denoms(f, symbols) - for m in f.args: - soln = _solve(m, symbol, **flags) - result.update(set(soln)) - result = list(result) - if check: - result = [s for s in result if - all(not checksol(den, {symbol: s}, **flags) for den in dens)] - # set flags for quick exit at end - check = False - flags['simplify'] = False - - elif f.is_Piecewise: - result = set() - for expr, cond in f.args: - candidates = _solve(expr, *symbols) - if cond is True: - # Only include solutions that do not match the condition - # of any of the other pieces. - for candidate in candidates: - matches_other_piece = False - for other_expr, other_cond in f.args: - if other_cond is True: - continue - if bool(other_cond.subs(symbol, candidate)): - matches_other_piece = True - break - if not matches_other_piece: - result.add(candidate) - else: - for candidate in candidates: - if bool(cond.subs(symbol, candidate)): - result.add(candidate) - check = False - else: - # first see if it really depends on symbol and whether there - # is a linear solution - f_num, sol = solve_linear(f, symbols=symbols) - if not symbol in f_num.free_symbols: - return [] - elif f_num.is_Symbol: - # no need to check but simplify if desired - if flags.get('simplify', True): - sol = simplify(sol) - return [sol] - - result = False # no solution was obtained - msg = '' # there is no failure message - dens = denoms(f, symbols) # store these for checking later - - # Poly is generally robust enough to convert anything to - # a polynomial and tell us the different generators that it - # contains, so we will inspect the generators identified by - # polys to figure out what to do. - poly = Poly(f_num) - if poly is None: - raise ValueError('could not convert %s to Poly' % f_num) - gens = [g for g in poly.gens if g.has(symbol)] - - if len(gens) > 1: - # If there is more than one generator, it could be that the - # generators have the same base but different powers, e.g. - # >>> Poly(exp(x)+1/exp(x)) - # Poly(exp(-x) + exp(x), exp(-x), exp(x), domain='ZZ') - # >>> Poly(sqrt(x)+sqrt(sqrt(x))) - # Poly(sqrt(x) + x**(1/4), sqrt(x), x**(1/4), domain='ZZ') - # If the exponents are Rational then a change of variables - # will make this a polynomial equation in a single base. - - def _as_base_q(x): - """Return (b**e, q) for x = b**(p*e/q) where p/q is the leading - Rational of the exponent of x, e.g. exp(-2*x/3) -> (exp(x), 3) - """ - b, e = x.as_base_exp() - if e.is_Rational: - return b, e.q - if not e.is_Mul: - return x, 1 - c, ee = e.as_coeff_Mul() - if c.is_Rational and c is not S.One: # c could be a Float - return b**ee, c.q - return x, 1 - - bases, qs = list(zip(*[_as_base_q(g) for g in gens])) - bases = set(bases) - - if len(bases) > 1: - funcs = set(b.func for b in bases if b.is_Function) - - trig = set([cos, sin, tan, cot]) - other = funcs - trig - if not other and len(funcs.intersection(trig)) > 1: - return _solve(f_num.rewrite(tan), symbol, **flags) - - trigh = set([cosh, sinh, tanh, coth]) - other = funcs - trigh - if not other and len(funcs.intersection(trigh)) > 1: - return _solve(f_num.rewrite(tanh), symbol, **flags) - - # just a simple case - see if replacement of single function - # clears all symbol-dependent functions, e.g. - # log(x) - log(log(x) - 1) - 3 can be solved even though it has - # two generators. - - funcs = [f for f in bases if f.is_Function] - if funcs: - funcs.sort(key=count_ops) # put shallowest function first - f1 = funcs[0] - t = Dummy('t') - # perform the substitution - ftry = f_num.subs(f1, t) - - # if no Functions left, we can proceed with usual solve - if not ftry.has(symbol): - cv_sols = _solve(ftry, t) - cv_inv = _solve(t - f1, symbol)[0] - sols = list() - for sol in cv_sols: - sols.append(cv_inv.subs(t, sol)) - return sols - - msg = 'multiple generators %s' % gens - - elif any(q != 1 for q in qs): - # e.g. for x**(1/2) + x**(1/4) a change of variables - # can be made using p**4 to give p**2 + p - base = bases.pop() - m = reduce(ilcm, qs) - p = Dummy('p', positive=True) - cov = p**m - fnew = f_num.subs(base, cov) - poly = Poly(fnew, p) # we now have a single generator, p - - # for cubics and quartics, if the flag wasn't set, DON'T do it - # by default since the results are quite long. Perhaps one - # could base this decision on a certain critical length of the - # roots. - if poly.degree() > 2: - flags['simplify'] = flags.get('simplify', False) - - soln = list(roots(poly, cubics=True, quartics=True).keys()) - if not soln: - soln = poly.all_roots() - check = False # RootOf instances can not be checked - - # We now know what the values of p are equal to. Now find out - # how they are related to the original x, e.g. if p**2 = cos(x) - # then x = acos(p**2) - # - inversion = _solve(cov - base, symbol, **flags) - result = [i.subs(p, s) for i in inversion for s in soln] - - else: # len(bases) == 1 and all(q == 1 for q in qs): - # e.g. case where gens are exp(x), exp(-x) - u = bases.pop() - t = Dummy('t') - inv = _solve(u - t, symbol) - ftry = f_num.subs(u, t) - if not ftry.has(symbol): - soln = _solve(ftry, t) - sols = list() - for sol in soln: - for i in inv: - sols.append(i.subs(t, sol)) - return sols - - elif len(gens) == 1: - - # There is only one generator that we are interested in, but there - # may have been more than one generator identified by polys (e.g. - # for symbols other than the one we are interested in) so recast - # the poly in terms of our generator of interest. - - if len(poly.gens) > 1: - poly = Poly(poly, gens[0]) - - # if we haven't tried tsolve yet, do so now - if not flags.pop('tsolve', False): - # for cubics and quartics, if the flag wasn't set, DON'T do it - # by default since the results are quite long. Perhaps one - # could base this decision on a certain critical length of the - # roots. - if poly.degree() > 2: - flags['simplify'] = flags.get('simplify', False) - soln = list(roots(poly, cubics=True, quartics=True).keys()) - if not soln: - soln = poly.all_roots() - check = False # RootOf instances can not be checked - gen = poly.gen - if gen != symbol: - u = Dummy() - flags['tsolve'] = True - inversion = _solve(gen - u, symbol, **flags) - soln = list(set([i.subs(u, s) for i in - inversion for s in soln])) - result = soln - - # fallback if above fails - if result is False: - result = _tsolve(f_num, symbol, **flags) - if result is None: - result = False - - if result is False: - raise NotImplementedError(msg + - "\nNo algorithms are implemented to solve equation %s" % f) - - if flags.get('simplify', True): - result = list(map(simplify, result)) - # we just simplified the solution so we now set the flag to - # False so the simplification doesn't happen again in checksol() - flags['simplify'] = False - - if check: - # reject any result that makes any denom. affirmatively 0; - # if in doubt, keep it - result = [s for s in result if - all(not checksol(den, {symbol: s}, **flags) - for den in dens)] - # keep only results if the check is not False - result = [r for r in result if - checksol(f_num, {symbol: r}, **flags) is not False] - return result - - -def _solve_system(exprs, symbols, **flags): - check = flags.get('check', True) - if not exprs: - return [] - - polys = [] - dens = set() - failed = [] - result = False - manual = flags.get('manual', False) - for j, g in enumerate(exprs): - dens.update(denoms(g, symbols)) - i, d = _invert(g, *symbols) - g = d - i - g = exprs[j] = g.as_numer_denom()[0] - if manual: - failed.append(g) - continue - - poly = g.as_poly(*symbols, **{'extension': True}) - - if poly is not None: - polys.append(poly) - else: - failed.append(g) - - if not polys: - solved_syms = [] - else: - if all(p.is_linear for p in polys): - n, m = len(polys), len(symbols) - matrix = zeros(n, m + 1) - - for i, poly in enumerate(polys): - for monom, coeff in poly.terms(): - try: - j = list(monom).index(1) - matrix[i, j] = coeff - except ValueError: - matrix[i, m] = -coeff - - # returns a dictionary ({symbols: values}) or None - result = solve_linear_system(matrix, *symbols, **flags) - if result: - # it doesn't need to be checked but we need to see - # that it didn't set any denominators to 0 - if any(checksol(d, result, **flags) for d in dens): - result = None - if failed: - if result: - solved_syms = list(result.keys()) - else: - solved_syms = [] - - else: - if len(symbols) > len(polys): - from sympy.utilities.iterables import subsets - from sympy.core.compatibility import set_union - - free = set_union(*[p.free_symbols for p in polys]) - free = list(free.intersection(symbols)) - free.sort(key=default_sort_key) - for syms in subsets(free, len(polys)): - try: - # returns [] or list of tuples of solutions for syms - result = solve_poly_system(polys, *syms) - if result: - solved_syms = syms - break - except NotImplementedError: - pass - else: - raise NotImplementedError('no valid subset found') - else: - try: - result = solve_poly_system(polys, *symbols) - solved_syms = symbols - except NotImplementedError: - failed.extend([g.as_expr() for g in polys]) - solved_syms = [] - - if result: - # we don't know here if the symbols provided were given - # or not, so let solve resolve that. A list of dictionaries - # is going to always be returned from here. - # - # We do not check the solution obtained from polys, either. - result = [dict(list(zip(solved_syms, r))) for r in result] - - if failed: - # For each failed equation, see if we can solve for one of the - # remaining symbols from that equation. If so, we update the - # solution set and continue with the next failed equation, - # repeating until we are done or we get an equation that can't - # be solved. - if result: - if type(result) is dict: - result = [result] - else: - result = [{}] - - def _ok_syms(e, sort=False): - rv = (e.free_symbols - solved_syms) & legal - if sort: - rv = list(rv) - rv.sort(key=default_sort_key) - return rv - - solved_syms = set(solved_syms) # set of symbols we have solved for - legal = set(symbols) # what we are interested in - simplify_flag = flags.get('simplify', None) - do_simplify = flags.get('simplify', True) - # sort so equation with the fewest potential symbols is first - for eq in ordered(failed, lambda _: len(_ok_syms(_))): - newresult = [] - bad_results = [] - got_s = None - u = Dummy() - for r in result: - # update eq with everything that is known so far - eq2 = eq.subs(r) - # if check is True then we see if it satisfies this - # equation, otherwise we just accept it - if check and r: - b = checksol(u, u, eq2, minimal=True) - if b is not None: - # this solution is sufficient to know whether - # it is valid or not so we either accept or - # reject it, then continue - if b: - newresult.append(r) - else: - bad_results.append(r) - continue - # search for a symbol amongst those available that - # can be solved for - ok_syms = _ok_syms(eq2, sort=True) - if not ok_syms: - if r: - newresult.append(r) - break # skip as it's independent of desired symbols - for s in ok_syms: - try: - soln = _solve(eq2, s, **flags) - except NotImplementedError: - continue - # put each solution in r and append the now-expanded - # result in the new result list; use copy since the - # solution for s in being added in-place - if do_simplify: - flags['simplify'] = False # for checksol's sake - for sol in soln: - if check: - # check that it satisfies *other* equations - ok = False - for p in polys: - if checksol(p, s, sol, **flags) is False: - break - else: - ok = True - if not ok: - continue - # check that it doesn't set any denominator to 0 - if any(checksol(d, s, sol, **flags) for d in dens): - continue - # update existing solutions with this new one - rnew = r.copy() - for k, v in r.items(): - rnew[k] = v.subs(s, sol) - # and add this new solution - rnew[s] = sol - newresult.append(rnew) - if simplify_flag is not None: - flags['simplify'] = simplify_flag - got_s = s - break - else: - raise NotImplementedError('could not solve %s' % eq2) - if got_s: - result = newresult - solved_syms.add(got_s) - for b in bad_results: - result.remove(b) - # if there is only one result should we return just the dictionary? - return result - - -
    [docs]def solve_linear(lhs, rhs=0, symbols=[], exclude=[]): - r""" Return a tuple derived from f = lhs - rhs that is either: - - (numerator, denominator) of ``f`` - If this comes back as (0, 1) it means - that ``f`` is independent of the symbols in ``symbols``, e.g:: - - y*cos(x)**2 + y*sin(x)**2 - y = y*(0) = 0 - cos(x)**2 + sin(x)**2 = 1 - - If it comes back as (0, 0) there is no solution to the equation - amongst the symbols given. - - If the numerator is not zero then the function is guaranteed - to be dependent on a symbol in ``symbols``. - - or - - (symbol, solution) where symbol appears linearly in the numerator of - ``f``, is in ``symbols`` (if given) and is not in ``exclude`` (if given). - - No simplification is done to ``f`` other than and mul=True expansion, - so the solution will correspond strictly to a unique solution. - - Examples - ======== - - >>> from sympy.solvers.solvers import solve_linear - >>> from sympy.abc import x, y, z - - These are linear in x and 1/x: - - >>> solve_linear(x + y**2) - (x, -y**2) - >>> solve_linear(1/x - y**2) - (x, y**(-2)) - - When not linear in x or y then the numerator and denominator are returned. - - >>> solve_linear(x**2/y**2 - 3) - (x**2 - 3*y**2, y**2) - - If the numerator is a symbol then (0, 0) is returned if the solution for - that symbol would have set any denominator to 0: - - >>> solve_linear(1/(1/x - 2)) - (0, 0) - >>> 1/(1/x) # to SymPy, this looks like x ... - x - >>> solve_linear(1/(1/x)) # so a solution is given - (x, 0) - - If x is allowed to cancel, then this appears linear, but this sort of - cancellation is not done so the solution will always satisfy the original - expression without causing a division by zero error. - - >>> solve_linear(x**2*(1/x - z**2/x)) - (x**2*(-z**2 + 1), x) - - You can give a list of what you prefer for x candidates: - - >>> solve_linear(x + y + z, symbols=[y]) - (y, -x - z) - - You can also indicate what variables you don't want to consider: - - >>> solve_linear(x + y + z, exclude=[x, z]) - (y, -x - z) - - If only x was excluded then a solution for y or z might be obtained. - - """ - from sympy import Equality - if isinstance(lhs, Equality): - if rhs: - raise ValueError(filldedent(''' - If lhs is an Equality, rhs must be 0 but was %s''' % rhs)) - rhs = lhs.rhs - lhs = lhs.lhs - dens = None - eq = lhs - rhs - n, d = eq.as_numer_denom() - if not n: - return S.Zero, S.One - - free = n.free_symbols - if not symbols: - symbols = free - else: - bad = [s for s in symbols if not s.is_Symbol] - if bad: - if len(bad) == 1: - bad = bad[0] - if len(symbols) == 1: - eg = 'solve(%s, %s)' % (eq, symbols[0]) - else: - eg = 'solve(%s, *%s)' % (eq, list(symbols)) - raise ValueError(filldedent(''' - solve_linear only handles symbols, not %s. To isolate - non-symbols use solve, e.g. >>> %s <<<. - ''' % (bad, eg))) - symbols = free.intersection(symbols) - symbols = symbols.difference(exclude) - dfree = d.free_symbols - - # derivatives are easy to do but tricky to analyze to see if they are going - # to disallow a linear solution, so for simplicity we just evaluate the - # ones that have the symbols of interest - derivs = defaultdict(list) - for der in n.atoms(Derivative): - csym = der.free_symbols & symbols - for c in csym: - derivs[c].append(der) - - if symbols: - all_zero = True - for xi in symbols: - # if there are derivatives in this var, calculate them now - if type(derivs[xi]) is list: - derivs[xi] = dict([(der, der.doit()) for der in derivs[xi]]) - nn = n.subs(derivs[xi]) - dn = nn.diff(xi) - if dn: - all_zero = False - if not xi in dn.free_symbols: - vi = -(nn.subs(xi, 0))/dn - if dens is None: - dens = denoms(eq, symbols) - if not any(checksol(di, {xi: vi}, minimal=True) is True - for di in dens): - # simplify any trivial integral - irep = [(i, i.doit()) for i in vi.atoms(C.Integral) if - i.function.is_number] - # do a slight bit of simplification - vi = expand_mul(vi.subs(irep)) - if not d.has(xi) or not (d/xi).has(xi): - return xi, vi - - if all_zero: - return S.Zero, S.One - if n.is_Symbol: # there was no valid solution - n = d = S.Zero - return n, d # should we cancel now? - -
    -
    [docs]def solve_linear_system(system, *symbols, **flags): - r""" - Solve system of N linear equations with M variables, which means - both under- and overdetermined systems are supported. The possible - number of solutions is zero, one or infinite. Respectively, this - procedure will return None or a dictionary with solutions. In the - case of underdetermined systems, all arbitrary parameters are skipped. - This may cause a situation in which an empty dictionary is returned. - In that case, all symbols can be assigned arbitrary values. - - Input to this functions is a Nx(M+1) matrix, which means it has - to be in augmented form. If you prefer to enter N equations and M - unknowns then use `solve(Neqs, *Msymbols)` instead. Note: a local - copy of the matrix is made by this routine so the matrix that is - passed will not be modified. - - The algorithm used here is fraction-free Gaussian elimination, - which results, after elimination, in an upper-triangular matrix. - Then solutions are found using back-substitution. This approach - is more efficient and compact than the Gauss-Jordan method. - - >>> from sympy import Matrix, solve_linear_system - >>> from sympy.abc import x, y - - Solve the following system:: - - x + 4 y == 2 - -2 x + y == 14 - - >>> system = Matrix(( (1, 4, 2), (-2, 1, 14))) - >>> solve_linear_system(system, x, y) - {x: -6, y: 2} - - A degenerate system returns an empty dictionary. - - >>> system = Matrix(( (0,0,0), (0,0,0) )) - >>> solve_linear_system(system, x, y) - {} - - """ - matrix = system[:, :] - syms = list(symbols) - - i, m = 0, matrix.cols - 1 # don't count augmentation - - while i < matrix.rows: - if i == m: - # an overdetermined system - if any(matrix[i:, m]): - return None # no solutions - else: - # remove trailing rows - matrix = matrix[:i, :] - break - - if not matrix[i, i]: - # there is no pivot in current column - # so try to find one in other columns - for k in range(i + 1, m): - if matrix[i, k]: - break - else: - if matrix[i, m]: - # we need to know if this is always zero or not. We - # assume that if there are free symbols that it is not - # identically zero (or that there is more than one way - # to make this zero. Otherwise, if there are none, this - # is a constant and we assume that it does not simplify - # to zero XXX are there better ways to test this? - if not matrix[i, m].free_symbols: - return None # no solution - - # zero row with non-zero rhs can only be accepted - # if there is another equivalent row, so look for - # them and delete them - nrows = matrix.rows - rowi = matrix.row(i) - ip = None - j = i + 1 - while j < matrix.rows: - # do we need to see if the rhs of j - # is a constant multiple of i's rhs? - rowj = matrix.row(j) - if rowj == rowi: - matrix.row_del(j) - elif rowj[:-1] == rowi[:-1]: - if ip is None: - _, ip = rowi[-1].as_content_primitive() - _, jp = rowj[-1].as_content_primitive() - if not (simplify(jp - ip) or simplify(jp + ip)): - matrix.row_del(j) - - j += 1 - - if nrows == matrix.rows: - # no solution - return None - # zero row or was a linear combination of - # other rows or was a row with a symbolic - # expression that matched other rows, e.g. [0, 0, x - y] - # so now we can safely skip it - matrix.row_del(i) - if not matrix: - # every choice of variable values is a solution - # so we return an empty dict instead of None - return dict() - continue - - # we want to change the order of colums so - # the order of variables must also change - syms[i], syms[k] = syms[k], syms[i] - matrix.col_swap(i, k) - - pivot_inv = S.One/matrix[i, i] - - # divide all elements in the current row by the pivot - matrix.row_op(i, lambda x, _: x * pivot_inv) - - for k in range(i + 1, matrix.rows): - if matrix[k, i]: - coeff = matrix[k, i] - - # subtract from the current row the row containing - # pivot and multiplied by extracted coefficient - matrix.row_op(k, lambda x, j: simplify(x - matrix[i, j]*coeff)) - - i += 1 - - # if there weren't any problems, augmented matrix is now - # in row-echelon form so we can check how many solutions - # there are and extract them using back substitution - - do_simplify = flags.get('simplify', True) - - if len(syms) == matrix.rows: - # this system is Cramer equivalent so there is - # exactly one solution to this system of equations - k, solutions = i - 1, {} - - while k >= 0: - content = matrix[k, m] - - # run back-substitution for variables - for j in range(k + 1, m): - content -= matrix[k, j]*solutions[syms[j]] - - if do_simplify: - solutions[syms[k]] = simplify(content) - else: - solutions[syms[k]] = content - - k -= 1 - - return solutions - elif len(syms) > matrix.rows: - # this system will have infinite number of solutions - # dependent on exactly len(syms) - i parameters - k, solutions = i - 1, {} - - while k >= 0: - content = matrix[k, m] - - # run back-substitution for variables - for j in range(k + 1, i): - content -= matrix[k, j]*solutions[syms[j]] - - # run back-substitution for parameters - for j in range(i, m): - content -= matrix[k, j]*syms[j] - - if do_simplify: - solutions[syms[k]] = simplify(content) - else: - solutions[syms[k]] = content - - k -= 1 - - return solutions - else: - return [] # no solutions - -
    -
    [docs]def solve_undetermined_coeffs(equ, coeffs, sym, **flags): - """Solve equation of a type p(x; a_1, ..., a_k) == q(x) where both - p, q are univariate polynomials and f depends on k parameters. - The result of this functions is a dictionary with symbolic - values of those parameters with respect to coefficients in q. - - This functions accepts both Equations class instances and ordinary - SymPy expressions. Specification of parameters and variable is - obligatory for efficiency and simplicity reason. - - >>> from sympy import Eq - >>> from sympy.abc import a, b, c, x - >>> from sympy.solvers import solve_undetermined_coeffs - - >>> solve_undetermined_coeffs(Eq(2*a*x + a+b, x), [a, b], x) - {a: 1/2, b: -1/2} - - >>> solve_undetermined_coeffs(Eq(a*c*x + a+b, x), [a, b], x) - {a: 1/c, b: -1/c} - - """ - if isinstance(equ, Equality): - # got equation, so move all the - # terms to the left hand side - equ = equ.lhs - equ.rhs - - equ = cancel(equ).as_numer_denom()[0] - - system = list(collect(equ.expand(), sym, evaluate=False).values()) - - if not any(equ.has(sym) for equ in system): - # consecutive powers in the input expressions have - # been successfully collected, so solve remaining - # system using Gaussian elimination algorithm - return solve(system, *coeffs, **flags) - else: - return None # no solutions - -
    -
    [docs]def solve_linear_system_LU(matrix, syms): - """ - Solves the augmented matrix system using LUsolve and returns a dictionary - in which solutions are keyed to the symbols of syms *as ordered*. - - The matrix must be invertible. - - Examples - ======== - - >>> from sympy import Matrix - >>> from sympy.abc import x, y, z - >>> from sympy.solvers.solvers import solve_linear_system_LU - - >>> solve_linear_system_LU(Matrix([ - ... [1, 2, 0, 1], - ... [3, 2, 2, 1], - ... [2, 0, 0, 1]]), [x, y, z]) - {x: 1/2, y: 1/4, z: -1/2} - - See Also - ======== - - sympy.matrices.LUsolve - - """ - assert matrix.rows == matrix.cols - 1 - A = matrix[:matrix.rows, :matrix.rows] - b = matrix[:, matrix.cols - 1:] - soln = A.LUsolve(b) - solutions = {} - for i in range(soln.rows): - solutions[syms[i]] = soln[i, 0] - return solutions -
    -_x = Dummy('x') -_a, _b, _c, _d, _e, _f, _g, _h = [Wild(t, exclude=[_x]) for t in 'abcdefgh'] -_patterns = None - - -def _generate_patterns(): - """ - Generates patterns for transcendental equations. - - This is lazily calculated (called) in the tsolve() function and stored in - the patterns global variable. - """ - - tmp1 = _f ** (_h - (_c*_g/_b)) - tmp2 = (-_e*tmp1/_a)**(1/_d) - global _patterns - _patterns = [ - (_a*_x*exp(_b*_x) - _c, - LambertW(_b*_c/_a)/_b), - (_a*_x*log(_b*_x) - _c, - exp(LambertW(_b*_c/_a))/_b), - (_a*(_b*_x + _c)**_d + _e, - ((-(_e/_a))**(1/_d) - _c)/_b), - (_b + _c*exp(_d*_x + _e), - (log(-_b/_c) - _e)/_d), - (_a*_x + _b + _c*exp(_d*_x + _e), - -_b/_a - LambertW(_c*_d*exp(_e - _b*_d/_a)/_a)/_d), - (_b + _c*_f**(_d*_x + _e), - (log(-_b/_c) - _e*log(_f))/_d/log(_f)), - (_a*_x + _b + _c*_f**(_d*_x + _e), - -_b/_a - LambertW(_c*_d*_f**(_e - _b*_d/_a)*log(_f)/_a)/_d/log(_f)), - (_b + _c*log(_d*_x + _e), - (exp(-_b/_c) - _e)/_d), - (_a*_x + _b + _c*log(_d*_x + _e), - -_e/_d + _c/_a*LambertW(_a/_c/_d*exp(-_b/_c + _a*_e/_c/_d))), - (_a*(_b*_x + _c)**_d + _e*_f**(_g*_x + _h), - -_c/_b - _d*LambertW(-tmp2*_g*log(_f)/_b/_d)/_g/log(_f)) - ] - - -
    [docs]def tsolve(eq, sym): - SymPyDeprecationWarning( - feature="tsolve()", - useinstead="solve()", - issue=3385, - deprecated_since_version="0.7.2", - ).warn() - return _tsolve(eq, sym) - -
    -def _tsolve(eq, sym, **flags): - """ - Helper for _solve that solves a transcendental equation with respect - to the given symbol. Various equations containing powers and logarithms, - can be solved. - - Only a single solution is returned. This solution is generally - not unique. In some cases, a complex solution may be returned - even though a real solution exists. - - >>> from sympy import log - >>> from sympy.solvers.solvers import _tsolve as tsolve - >>> from sympy.abc import x - - >>> tsolve(3**(2*x+5)-4, x) - [(-5*log(3) + 2*log(2))/(2*log(3))] - - >>> tsolve(log(x) + 2*x, x) - [LambertW(2)/2] - - """ - if _patterns is None: - _generate_patterns() - eq2 = eq.subs(sym, _x) - for p, sol in _patterns: - m = eq2.match(p) - if m: - soln = sol.subs(m).subs(_x, sym) - if sym not in soln.free_symbols: - return [soln] - - try: - u = unrad(eq, sym) - except ValueError: - raise NotImplementedError('Radicals cannot be cleared from %s' % eq) - if u: - eq, cov, dens = u - if cov: - if len(cov) > 1: - raise NotImplementedError('Not sure how to handle this.') - isym, ieq = cov[0] - # since cov is written in terms of positive symbols, set - # check to False or else 0 would be excluded; _solve will check - # the results - flags['check'] = False - sol = _solve(eq, isym, **flags) - inv = _solve(ieq, sym, **flags) - result = [] - for s in sol: - for i in inv: - result.append(i.subs(isym, s)) - return result - else: - return _solve(eq, sym, **flags) - - rhs, lhs = _invert(eq, sym) - - if lhs.is_Add: - # it's time to try factoring - fac = factor(lhs - rhs) - if fac.is_Mul: - return _solve(fac, sym) - - elif lhs.is_Pow: - if lhs.exp.is_Integer: - if lhs - rhs != eq: - return _solve(lhs - rhs, sym) - elif not rhs: - return _solve(lhs.base, sym) - elif sym not in lhs.exp.free_symbols: - return _solve(lhs.base - rhs**(1/lhs.exp), sym) - elif not rhs and sym in lhs.exp.free_symbols: - # f(x)**g(x) only has solutions where f(x) == 0 and g(x) != 0 at - # the same place - sol_base = _solve(lhs.base, sym) - if not sol_base: - return sol_base - return list(set(sol_base) - set(_solve(lhs.exp, sym))) - elif (rhs is not S.Zero and - lhs.base.is_positive and - lhs.exp.is_real): - return _solve(lhs.exp*log(lhs.base) - log(rhs), sym) - - elif lhs.is_Mul and rhs.is_positive: - llhs = expand_log(log(lhs)) - if llhs.is_Add: - return _solve(llhs - log(rhs), sym) - - rewrite = lhs.rewrite(exp) - if rewrite != lhs: - return _solve(rewrite - rhs, sym) - - if flags.pop('force', True): - flags['force'] = False - pos, reps = posify(lhs - rhs) - for u, s in reps.items(): - if s == sym: - break - else: - u = sym - try: - soln = _solve(pos, u, **flags) - except NotImplementedError: - return - return [s.subs(reps) for s in soln] - -# TODO: option for calculating J numerically - - -
    [docs]def nsolve(*args, **kwargs): - r""" - Solve a nonlinear equation system numerically:: - - nsolve(f, [args,] x0, modules=['mpmath'], **kwargs) - - f is a vector function of symbolic expressions representing the system. - args are the variables. If there is only one variable, this argument can - be omitted. - x0 is a starting vector close to a solution. - - Use the modules keyword to specify which modules should be used to - evaluate the function and the Jacobian matrix. Make sure to use a module - that supports matrices. For more information on the syntax, please see the - docstring of lambdify. - - Overdetermined systems are supported. - - >>> from sympy import Symbol, nsolve - >>> import sympy - >>> sympy.mpmath.mp.dps = 15 - >>> x1 = Symbol('x1') - >>> x2 = Symbol('x2') - >>> f1 = 3 * x1**2 - 2 * x2**2 - 1 - >>> f2 = x1**2 - 2 * x1 + x2**2 + 2 * x2 - 8 - >>> print(nsolve((f1, f2), (x1, x2), (-1, 1))) - [-1.19287309935246] - [ 1.27844411169911] - - For one-dimensional functions the syntax is simplified: - - >>> from sympy import sin, nsolve - >>> from sympy.abc import x - >>> nsolve(sin(x), x, 2) - 3.14159265358979 - >>> nsolve(sin(x), 2) - 3.14159265358979 - - mpmath.findroot is used, you can find there more extensive documentation, - especially concerning keyword parameters and available solvers. - """ - # interpret arguments - if len(args) == 3: - f = args[0] - fargs = args[1] - x0 = args[2] - elif len(args) == 2: - f = args[0] - fargs = None - x0 = args[1] - elif len(args) < 2: - raise TypeError('nsolve expected at least 2 arguments, got %i' - % len(args)) - else: - raise TypeError('nsolve expected at most 3 arguments, got %i' - % len(args)) - modules = kwargs.get('modules', ['mpmath']) - if isinstance(f, (list, tuple)): - f = Matrix(f).T - if not isinstance(f, Matrix): - # assume it's a sympy expression - if isinstance(f, Equality): - f = f.lhs - f.rhs - f = f.evalf() - syms = f.free_symbols - if fargs is None: - fargs = syms.copy().pop() - if not (len(syms) == 1 and (fargs in syms or fargs[0] in syms)): - raise ValueError(filldedent(''' - expected a one-dimensional and numerical function''')) - - # the function is much better behaved if there is no denominator - f = f.as_numer_denom()[0] - - f = lambdify(fargs, f, modules) - return findroot(f, x0, **kwargs) - if len(fargs) > f.cols: - raise NotImplementedError(filldedent(''' - need at least as many equations as variables''')) - verbose = kwargs.get('verbose', False) - if verbose: - print('f(x):') - print(f) - # derive Jacobian - J = f.jacobian(fargs) - if verbose: - print('J(x):') - print(J) - # create functions - f = lambdify(fargs, f.T, modules) - J = lambdify(fargs, J, modules) - # solve the system numerically - x = findroot(f, x0, J=J, **kwargs) - return x - -
    -def _invert(eq, *symbols, **kwargs): - """Return tuple (i, d) where ``i`` is independent of ``symbols`` and ``d`` - contains symbols. ``i`` and ``d`` are obtained after recursively using - algebraic inversion until an uninvertible ``d`` remains. If there are no - free symbols then ``d`` will be zero. Some (but not necessarily all) - solutions to the expression ``i - d`` will be related the solutions of the - original expression. - - Examples - ======== - - >>> from sympy.solvers.solvers import _invert as invert - >>> from sympy import sqrt, cos - >>> from sympy.abc import x, y - >>> invert(x - 3) - (3, x) - >>> invert(3) - (3, 0) - >>> invert(2*cos(x) - 1) - (pi/3, x) - >>> invert(sqrt(x) - 3) - (3, sqrt(x)) - >>> invert(sqrt(x) + y, x) - (-y, sqrt(x)) - >>> invert(sqrt(x) + y, y) - (-sqrt(x), y) - >>> invert(sqrt(x) + y, x, y) - (0, sqrt(x) + y) - - If there is more than one symbol in a power's base and the exponent - is not an Integer, then the principal root will be used for the - inversion: - - >>> invert(sqrt(x + y) - 2) - (4, x + y) - >>> invert(sqrt(x + y) - 2) - (4, x + y) - - If the exponent is an integer, setting ``integer_power`` to True - will force the principal root to be selected: - - >>> invert(x**2 - 4, integer_power=True) - (2, x) - - """ - eq = sympify(eq) - free = eq.free_symbols - if not symbols: - symbols = free - if not free & set(symbols): - return eq, S.Zero - - dointpow = bool(kwargs.get('integer_power', False)) - - inverses = { - asin: sin, - acos: cos, - atan: tan, - acot: cot, - asinh: sinh, - acosh: cosh, - atanh: tanh, - acoth: coth, - } - - lhs = eq - rhs = S.Zero - while True: - was = lhs - while True: - indep, dep = lhs.as_independent(*symbols) - - # dep + indep == rhs - if lhs.is_Add: - # this indicates we have done it all - if indep is S.Zero: - break - - lhs = dep - rhs -= indep - - # dep * indep == rhs - else: - # this indicates we have done it all - if indep is S.One: - break - - lhs = dep - rhs /= indep - - # collect like-terms in symbols - if lhs.is_Add: - terms = {} - for a in lhs.args: - i, d = a.as_independent(*symbols) - terms.setdefault(d, []).append(i) - if any(len(v) > 1 for v in list(terms.values())): - args = [] - for d, i in terms.items(): - if len(i) > 1: - args.append(Add(*i)*d) - else: - args.append(i[0]*d) - lhs = Add(*args) - - # if it's a two-term Add with rhs = 0 and two powers we can get the - # dependent terms together, e.g. 3*f(x) + 2*g(x) -> f(x)/g(x) = -2/3 - if lhs.is_Add and not rhs and len(lhs.args) == 2: - a, b = lhs.as_two_terms() - ai, ad = a.as_independent(*symbols) - bi, bd = b.as_independent(*symbols) - if any(_ispow(i) for i in (ad, bd)): - a_base, a_exp = ad.as_base_exp() - b_base, b_exp = bd.as_base_exp() - if a_base == b_base: - # a = -b - lhs = powsimp(powdenest(ad/bd)) - rhs = -bi/ai - else: - bases = (b_base, a_base) - expos = (b_exp, a_exp) - if (all(i.is_real for i in bases + expos) and - all(i.is_positive for i in bases) and - any(i.has(*symbols) for i in expos) and - not any(i.has(*symbols) for i in bases) and - sign(ai*bi).is_negative): - # ai*a_base**a_exp = -bi*b_base**b_exp - lhs = a_exp*log(a_base) - b_exp*log(b_base) - rhs = -log(ai/-bi) - - elif lhs.is_Mul and any(_ispow(a) for a in lhs.args): - lhs = powsimp(powdenest(lhs)) - - # -1 - # f(x) = g -> x = f (g) - elif lhs.is_Function and (lhs.nargs == 1 or len(lhs.args) == 1) and \ - (hasattr(lhs, 'inverse') or - lhs.func in inverses or - lhs.func is Abs): - if lhs.func in inverses: - inv = inverses[lhs.func] - elif lhs.func is Abs: - inv = lambda w: w**2 - lhs = Basic(lhs.args[0]**2) # get it ready to remove the args - else: - inv = lhs.inverse() - rhs = inv(rhs) - lhs = lhs.args[0] - - if rhs and lhs.is_Pow and lhs.exp.is_Integer and lhs.exp < 0: - lhs = 1/lhs - rhs = 1/rhs - - # base**a = b -> base = b**(1/a) if - # a is an Integer and dointpow=True (this gives real branch of root) - # a is not an Integer and the equation is multivariate and the - # base has more than 1 symbol in it - # The rationale for this is that right now the multi-system solvers - # doesn't try to resolve generators to see, for example, if the whole - # system is written in terms of sqrt(x + y) so it will just fail, so we - # do that step here. - if lhs.is_Pow and ( - lhs.exp.is_Integer and dointpow or not lhs.exp.is_Integer and - len(symbols) > 1 and len(lhs.base.free_symbols & set(symbols)) > 1): - rhs = rhs**(1/lhs.exp) - lhs = lhs.base - - if lhs == was: - break - return rhs, lhs - - -def unrad(eq, *syms, **flags): - """ Remove radicals with symbolic arguments and return (eq, cov, dens), - None or raise an error: - - None is returned if there are no radicals to remove. - - ValueError is raised if there are radicals and they cannot be removed. - - Otherwise - - ``eq``, ``cov`` - equation without radicals, perhaps written in terms of - change variables; the relationship to the original variables - is given by the expressions in list (``cov``) whose tuples, - (``v``, ``expr``) give the change variable introduced (``v``) - and the expression (``expr``) which equates the base of the radical - to the power of the change variable needed to clear the radical. - For example, for sqrt(2 - x) the tuple (_p, -_p**2 - x + 2), would - be obtained. - ``dens`` - A set containing all denominators encountered while removing - radicals. This may be of interest since any solution obtained in - the modified expression should not set any denominator to zero. - ``syms`` - an iterable of symbols which, if provided, will limit the focus of - radical removal: only radicals with one or more of the symbols of - interest will be cleared. - - ``flags`` are used internally for communication during recursive calls. - - Radicals can be removed from an expression if: - * all bases of the radicals are the same; a change of variables is - done in this case. - * if all radicals appear in one term of the expression - * there are only 4 terms with sqrt() factors or there are less than - four terms having sqrt() factors - - Examples - ======== - - >>> from sympy.solvers.solvers import unrad - >>> from sympy.abc import x - >>> from sympy import sqrt, Rational - >>> unrad(sqrt(x)*x**Rational(1,3) + 2) - (x**5 - 64, [], []) - >>> unrad(sqrt(x) + (x + 1)**Rational(1,3)) - (x**3 - x**2 - 2*x - 1, [], []) - >>> unrad(sqrt(x) + x**Rational(1,3) + 2) - (_p**3 + _p**2 + 2, [(_p, -_p**6 + x)], []) - - """ - if eq.is_Atom: - return - cov, dens, nwas = [flags.get(k, v) for k, v in - sorted(dict(dens=None, cov=None, n=None).items())] - - def _take(d): - # see if this is a term that has symbols of interest - # and merits further processing - free = d.free_symbols - if not free: - return False - return not syms or free.intersection(syms) - - if dens is None: - dens = set() - if cov is None: - cov = [] - - eq = powdenest(eq) - eq, d = eq.as_numer_denom() - eq = _mexpand(eq) - if _take(d): - dens.add(d) - - if not eq.free_symbols: - return eq, cov, list(dens) - - poly = eq.as_poly() - - # if all the bases are the same or all the radicals are in one - # term, `lcm` will be the lcm of the radical's exponent - # denominators - lcm = 1 - rads = set() - bases = set() - for g in poly.gens: - if not _take(g) or not g.is_Pow: - continue - ecoeff = g.exp.as_coeff_mul()[0] # a Rational - if ecoeff.q != 1: - rads.add(g) - lcm = ilcm(lcm, ecoeff.q) - bases.add(g.base) - - if not rads: - return - - depth = sqrt_depth(eq) - - # get terms together that have common generators - drad = dict(list(zip(rads, list(range(len(rads)))))) - rterms = {(): []} - args = Add.make_args(poly.as_expr()) - for t in args: - if _take(t): - common = set(t.as_poly().gens).intersection(rads) - key = tuple(sorted([drad[i] for i in common])) - else: - key = () - rterms.setdefault(key, []).append(t) - args = Add(*rterms.pop(())) - rterms = [Add(*rterms[k]) for k in list(rterms.keys())] - # the output will depend on the order terms are processed, so - # make it canonical quickly - rterms.sort(key=default_sort_key) - - # continue handling - ok = True - if len(rterms) == 1: - eq = rterms[0]**lcm - (-args)**lcm - - elif len(rterms) == 2 and not args: - eq = rterms[0]**lcm - rterms[1]**lcm - - elif log(lcm, 2).is_Integer and (not args and len(rterms) == 4 or len(rterms) < 4): - if len(rterms) == 4: - # (r0+r1)**2 - (r2+r3)**2 - t1, t2, t3, t4 = [t**2 for t in rterms] - eq = t1 + t2 + 2*rterms[0]*rterms[1] - \ - (t3 + t4 + 2*rterms[2]*rterms[3]) - elif len(rterms) == 3: - # (r0+r1)**2 - (r2+a)**2 - t1, t2, t3 = [t**2 for t in rterms] - eq = t1 + t2 + 2*rterms[0]*rterms[1] - \ - (t3 + args**2 + 2*args*rterms[2]) - elif len(rterms) == 2: - t1, t2 = [t**2 for t in rterms[:2]] - # r0**2 - (r1+a)**2 - eq = t1 - (t2 + args**2 + 2*args*rterms[1]) - - elif len(bases) == 1: # change of variables may work - ok = False - covwas = len(cov) - b = bases.pop() - for p, bexpr in cov: - pow = (b - bexpr) - if pow.is_Pow: - pb, pe = pow.as_base_exp() - if pe == lcm and pb == p: - p = pb - break - else: - p = Dummy('p', positive=True) - cov.append((p, b - p**lcm)) - eq = poly.subs(b, p**lcm).as_expr() - if not eq.free_symbols.intersection(syms): - ok = True - else: - if len(cov) > covwas: - cov = cov[:-1] - else: - ok = False - - new_depth = sqrt_depth(eq) - if not ok or (nwas is not None and len(rterms) == nwas and new_depth and new_depth == depth): - # XXX: XFAIL tests indicate other cases that should be handled. - raise ValueError('Cannot remove all radicals from %s' % eq) - - neq = unrad(eq, *syms, **dict(cov=cov, dens=dens, n=len(rterms))) - if neq: - eq = neq[0] - if eq.could_extract_minus_sign(): - eq = -eq - return (_mexpand(eq), cov, list(dens)) -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/statistics/distributions.html b/dev-py3k/_modules/sympy/statistics/distributions.html deleted file mode 100644 index 7263a82da87..00000000000 --- a/dev-py3k/_modules/sympy/statistics/distributions.html +++ /dev/null @@ -1,629 +0,0 @@ - - - - - - - - - - sympy.statistics.distributions — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.statistics.distributions

    -from sympy.core import sympify, Lambda, Dummy, Integer, Rational, oo, Float, pi
    -from sympy.functions import sqrt, exp, erf
    -from sympy.printing import sstr
    -import random
    -
    -
    -
    [docs]class Sample(tuple): - """ - Sample([x1, x2, x3, ...]) represents a collection of samples. - Sample parameters like mean, variance and stddev can be accessed as - properties. - The sample will be sorted. - - Examples - ======== - - >>> from sympy.statistics.distributions import Sample - >>> Sample([0, 1, 2, 3]) - Sample([0, 1, 2, 3]) - >>> Sample([8, 3, 2, 4, 1, 6, 9, 2]) - Sample([1, 2, 2, 3, 4, 6, 8, 9]) - >>> s = Sample([1, 2, 3, 4, 5]) - >>> s.mean - 3 - >>> s.stddev - sqrt(2) - >>> s.median - 3 - >>> s.variance - 2 - - """ - def __new__(cls, sample): - s = tuple.__new__(cls, sorted(sample)) - s.mean = mean = sum(s) / Integer(len(s)) - s.variance = sum([(x - mean)**2 for x in s]) / Integer(len(s)) - s.stddev = sqrt(s.variance) - if len(s) % 2: - s.median = s[len(s)//2] - else: - s.median = sum(s[len(s)//2 - 1:len(s)//2 + 1]) / Integer(2) - return s - - def __repr__(self): - return sstr(self) - - def __str__(self): - return sstr(self) - -
    -
    [docs]class ContinuousProbability(object): - """Base class for continuous probability distributions""" - -
    [docs] def probability(s, a, b): - """ - Calculate the probability that a random number x generated - from the distribution satisfies a <= x <= b - - Examples - ======== - - >>> from sympy.statistics import Normal - >>> from sympy.core import oo - >>> Normal(0, 1).probability(-1, 1) - erf(sqrt(2)/2) - >>> Normal(0, 1).probability(1, oo) - -erf(sqrt(2)/2)/2 + 1/2 - - """ - return s.cdf(b) - s.cdf(a) -
    -
    [docs] def random(s, n=None): - """ - random() -- generate a random number from the distribution. - random(n) -- generate a Sample of n random numbers. - - Examples - ======== - - >>> from sympy.statistics import Uniform - >>> x = Uniform(1, 5).random() - >>> x < 5 and x > 1 - True - >>> x = Uniform(-4, 2).random() - >>> x < 2 and x > -4 - True - - """ - if n is None: - return s._random() - else: - return Sample([s._random() for i in range(n)]) -
    - def __repr__(self): - return sstr(self) - - def __str__(self): - return sstr(self) - -
    -
    [docs]class Normal(ContinuousProbability): - """ - Normal(mu, sigma) represents the normal or Gaussian distribution - with mean value mu and standard deviation sigma. - - Examples - ======== - - >>> from sympy.statistics import Normal - >>> from sympy import oo - >>> N = Normal(1, 2) - >>> N.mean - 1 - >>> N.variance - 4 - >>> N.probability(-oo, 1) # probability on an interval - 1/2 - >>> N.probability(1, oo) - 1/2 - >>> N.probability(-oo, oo) - 1 - >>> N.probability(-1, 3) - erf(sqrt(2)/2) - >>> _.evalf() - 0.682689492137086 - - """ - def __init__(self, mu, sigma): - self.mu = sympify(mu) - self.sigma = sympify(sigma) - - mean = property(lambda s: s.mu) - median = property(lambda s: s.mu) - mode = property(lambda s: s.mu) - stddev = property(lambda s: s.sigma) - variance = property(lambda s: s.sigma**2) - -
    [docs] def pdf(s, x): - """ - Return the probability density function as an expression in x - - Examples - ======== - - >>> from sympy.statistics import Normal - >>> Normal(1, 2).pdf(0) - sqrt(2)*exp(-1/8)/(4*sqrt(pi)) - >>> from sympy.abc import x - >>> Normal(1, 2).pdf(x) - sqrt(2)*exp(-(x - 1)**2/8)/(4*sqrt(pi)) - - """ - x = sympify(x) - return 1/(s.sigma*sqrt(2*pi)) * exp(-(x - s.mu)**2 / (2*s.sigma**2)) -
    -
    [docs] def cdf(s, x): - """ - Return the cumulative density function as an expression in x - - Examples - ======== - - >>> from sympy.statistics import Normal - >>> Normal(1, 2).cdf(0) - -erf(sqrt(2)/4)/2 + 1/2 - >>> from sympy.abc import x - >>> Normal(1, 2).cdf(x) - erf(sqrt(2)*(x - 1)/4)/2 + 1/2 - - """ - x = sympify(x) - return (1 + erf((x - s.mu)/(s.sigma*sqrt(2))))/2 -
    - def _random(s): - return random.gauss(float(s.mu), float(s.sigma)) - -
    [docs] def confidence(s, p): - """Return a symmetric (p*100)% confidence interval. For example, - p=0.95 gives a 95% confidence interval. Currently this function - only handles numerical values except in the trivial case p=1. - - For example, one standard deviation: - - >>> from sympy.statistics import Normal - >>> N = Normal(0, 1) - >>> N.confidence(0.68) - (-0.994457883209753, 0.994457883209753) - >>> N.probability(*_).evalf() - 0.680000000000000 - - Two standard deviations: - - >>> N = Normal(0, 1) - >>> N.confidence(0.95) - (-1.95996398454005, 1.95996398454005) - >>> N.probability(*_).evalf() - 0.950000000000000 - - """ - - if p == 1: - return (-oo, oo) - - assert p <= 1 - - # In terms of n*sigma, we have n = sqrt(2)*ierf(p). The inverse - # error function is not yet implemented in SymPy but can easily be - # computed numerically - - from sympy.mpmath import mpf, erfinv - - # calculate y = ierf(p) by solving erf(y) - p = 0 - y = erfinv(mpf(p)) - t = Float(str(mpf(float(s.sigma)) * mpf(2)**0.5 * y)) - mu = s.mu.evalf() - return (mu - t, mu + t) -
    - @staticmethod -
    [docs] def fit(sample): - """ - Create a normal distribution fit to the mean and standard - deviation of the given distribution or sample. - - Examples - ======== - - >>> from sympy.statistics import Normal - >>> Normal.fit([1,2,3,4,5]) - Normal(3, sqrt(2)) - >>> from sympy.abc import x, y - >>> Normal.fit([x, y]) - Normal(x/2 + y/2, sqrt((-x/2 + y/2)**2/2 + (x/2 - y/2)**2/2)) - - """ - if not hasattr(sample, "stddev"): - sample = Sample(sample) - return Normal(sample.mean, sample.stddev) - -
    -
    [docs]class Uniform(ContinuousProbability): - """ - Uniform(a, b) represents a probability distribution with uniform - probability density on the interval [a, b] and zero density - everywhere else. - """ - def __init__(self, a, b): - self.a = sympify(a) - self.b = sympify(b) - - mean = property(lambda s: (s.a + s.b)/2) - median = property(lambda s: (s.a + s.b)/2) - mode = property(lambda s: (s.a + s.b)/2) # arbitrary - variance = property(lambda s: (s.b - s.a)**2 / 12) - stddev = property(lambda s: sqrt(s.variance)) - -
    [docs] def pdf(s, x): - """ - Return the probability density function as an expression in x - - Examples - ======== - - >>> from sympy.statistics import Uniform - >>> Uniform(1, 5).pdf(1) - 1/4 - >>> Uniform(2, 4).pdf(2) - 1/2 - - """ - x = sympify(x) - if not x.is_Number: - raise NotImplementedError("SymPy does not yet support" - "piecewise functions") - if x < s.a or x > s.b: - return Rational(0) - return 1/(s.b - s.a) -
    -
    [docs] def cdf(s, x): - """ - Return the cumulative density function as an expression in x - - Examples - ======== - - >>> from sympy.statistics import Uniform - >>> Uniform(1, 5).cdf(2) - 1/4 - >>> Uniform(1, 5).cdf(4) - 3/4 - - """ - x = sympify(x) - if not x.is_Number: - raise NotImplementedError("SymPy does not yet support" - "piecewise functions") - if x <= s.a: - return Rational(0) - if x >= s.b: - return Rational(1) - return (x - s.a)/(s.b - s.a) -
    - def _random(s): - return Float(random.uniform(float(s.a), float(s.b))) - -
    [docs] def confidence(s, p): - """Generate a symmetric (p*100)% confidence interval. - - >>> from sympy import Rational - >>> from sympy.statistics import Uniform - >>> U = Uniform(1, 2) - >>> U.confidence(1) - (1, 2) - >>> U.confidence(Rational(1,2)) - (5/4, 7/4) - - """ - p = sympify(p) - assert p <= 1 - - d = (s.b - s.a)*p / 2 - return (s.mean - d, s.mean + d) -
    - @staticmethod -
    [docs] def fit(sample): - """ - Create a uniform distribution fit to the mean and standard - deviation of the given distribution or sample. - - Examples - ======== - - >>> from sympy.statistics import Uniform - >>> Uniform.fit([1, 2, 3, 4, 5]) - Uniform(-sqrt(6) + 3, sqrt(6) + 3) - >>> Uniform.fit([1, 2]) - Uniform(-sqrt(3)/2 + 3/2, sqrt(3)/2 + 3/2) - - """ - if not hasattr(sample, "stddev"): - sample = Sample(sample) - m = sample.mean - d = sqrt(12*sample.variance)/2 - return Uniform(m - d, m + d) - -
    -
    [docs]class PDF(ContinuousProbability): - """ - PDF(func, (x, a, b)) represents continuous probability distribution - with probability distribution function func(x) on interval (a, b) - - If func is not normalized so that integrate(func, (x, a, b)) == 1, - it can be normalized using PDF.normalize() method - - Examples - ======== - - >>> from sympy import Symbol, exp, oo - >>> from sympy.statistics.distributions import PDF - >>> from sympy.abc import x - >>> a = Symbol('a', positive=True) - - >>> exponential = PDF(exp(-x/a)/a, (x,0,oo)) - >>> exponential.pdf(x) - exp(-x/a)/a - >>> exponential.cdf(x) - 1 - exp(-x/a) - >>> exponential.mean - a - >>> exponential.variance - a**2 - - """ - - def __init__(self, pdf, var): - #XXX maybe add some checking of parameters - if isinstance(var, (tuple, list)): - self.pdf = Lambda(var[0], pdf) - self.domain = tuple(var[1:]) - else: - self.pdf = Lambda(var, pdf) - self.domain = (-oo, oo) - self._cdf = None - self._mean = None - self._variance = None - self._stddev = None - -
    [docs] def normalize(self): - """ - Normalize the probability distribution function so that - integrate(self.pdf(x), (x, a, b)) == 1 - - Examples - ======== - - >>> from sympy import Symbol, exp, oo - >>> from sympy.statistics.distributions import PDF - >>> from sympy.abc import x - >>> a = Symbol('a', positive=True) - - >>> exponential = PDF(exp(-x/a), (x,0,oo)) - >>> exponential.normalize().pdf(x) - exp(-x/a)/a - - """ - - norm = self.probability(*self.domain) - if norm != 1: - w = Dummy('w', real=True) - return self.__class__(self.pdf(w)/norm, (w, self.domain[0], self.domain[1])) - #self._cdf = Lambda(w, (self.cdf(w) - self.cdf(self.domain[0]))/norm) - #if self._mean is not None: - # self._mean /= norm - #if self._variance is not None: - # self._variance = (self._variance + (self._mean*norm)**2)/norm - self.mean**2 - #if self._stddev is not None: - # self._stddev = sqrt(self._variance) - else: - return self -
    -
    [docs] def cdf(self, x): - """ - Return the cumulative density function as an expression in x - - Examples - ======== - - >>> from sympy.statistics.distributions import PDF - >>> from sympy import exp, oo - >>> from sympy.abc import x, y - >>> PDF(exp(-x/y), (x,0,oo)).cdf(4) - y - y*exp(-4/y) - >>> PDF(2*x + y, (x, 10, oo)).cdf(0) - -10*y - 100 - - """ - x = sympify(x) - if self._cdf is not None: - return self._cdf(x) - else: - from sympy import integrate - w = Dummy('w', real=True) - self._cdf = integrate(self.pdf(w), w) - self._cdf = Lambda( - w, self._cdf - self._cdf.subs(w, self.domain[0])) - return self._cdf(x) -
    - def _get_mean(self): - if self._mean is not None: - return self._mean - else: - from sympy import integrate - w = Dummy('w', real=True) - self._mean = integrate( - self.pdf(w)*w, (w, self.domain[0], self.domain[1])) - return self._mean - - def _get_variance(self): - if self._variance is not None: - return self._variance - else: - from sympy import integrate, simplify - w = Dummy('w', real=True) - self._variance = integrate(self.pdf( - w)*w**2, (w, self.domain[0], self.domain[1])) - self.mean**2 - self._variance = simplify(self._variance) - return self._variance - - def _get_stddev(self): - if self._stddev is not None: - return self._stddev - else: - self._stddev = sqrt(self.variance) - return self._stddev - - mean = property(_get_mean) - variance = property(_get_variance) - stddev = property(_get_stddev) - - def _random(s): - raise NotImplementedError - -
    [docs] def transform(self, func, var): - """ - Return a probability distribution of random variable func(x) - currently only some simple injective functions are supported - - Examples - ======== - - >>> from sympy.statistics.distributions import PDF - >>> from sympy import oo - >>> from sympy.abc import x, y - >>> PDF(2*x + y, (x, 10, oo)).transform(x, y) - PDF(0, ((_w,), x, x)) - - """ - - w = Dummy('w', real=True) - - from sympy import solve - from sympy import S - inverse = solve(func - w, var) - newPdf = S.Zero - funcdiff = func.diff(var) - #TODO check if x is in domain - for x in inverse: - # this assignment holds only for x in domain - # in general it would require implementing - # piecewise defined functions in sympy - newPdf += (self.pdf(var)/abs(funcdiff)).subs(var, x) - - return PDF(newPdf, (w, func.subs(var, self.domain[0]), func.subs(var, self.domain[1])))
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/stats.html b/dev-py3k/_modules/sympy/stats.html deleted file mode 100644 index 3d1f402322a..00000000000 --- a/dev-py3k/_modules/sympy/stats.html +++ /dev/null @@ -1,181 +0,0 @@ - - - - - - - - - - sympy.stats — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.stats

    -"""
    -SymPy statistics module
    -
    -Introduces a random variable type into the SymPy language.
    -
    -Random variables may be declared using prebuilt functions such as
    -Normal, Exponential, Coin, Die, etc...  or built with functions like FiniteRV.
    -
    -Queries on random expressions can be made using the functions
    -
    -===================== =============================
    -    Expression                Meaning
    ---------------------- -----------------------------
    - P(condition)          Probability
    - E(expression)         Expectation value
    - variance(expression)  Variance
    - density(expression)   Probability Density Function
    - sample(expression)    Produce a realization
    - where(condition)      Where the condition is true
    -===================== =============================
    -
    -Examples
    -========
    -
    ->>> from sympy.stats import P, E, variance, Die, Normal
    ->>> from sympy import Eq, simplify
    ->>> X, Y = Die('X', 6), Die('Y', 6) # Define two six sided dice
    ->>> Z = Normal('Z', 0, 1) # Declare a Normal random variable with mean 0, std 1
    ->>> P(X>3) # Probability X is greater than 3
    -1/2
    ->>> E(X+Y) # Expectation of the sum of two dice
    -7
    ->>> variance(X+Y) # Variance of the sum of two dice
    -35/6
    ->>> simplify(P(Z>1)) # Probability of Z being greater than 1
    --erf(sqrt(2)/2)/2 + 1/2
    -"""
    -
    -__all__ = []
    -
    -from . import rv_interface
    -from .rv_interface import (
    -    cdf, covariance, density, dependent, E, given, independent, P, pspace,
    -    random_symbols, sample, sample_iter, skewness, std, variance, where,
    -)
    -__all__.extend(rv_interface.__all__)
    -
    -from . import frv_types
    -from .frv_types import (
    -    Bernoulli, Binomial, Coin, Die, DiscreteUniform, FiniteRV, Hypergeometric,
    -)
    -__all__.extend(frv_types.__all__)
    -
    -from . import crv_types
    -from .crv_types import (
    -    ContinuousRV,
    -    Arcsin, Benini, Beta, BetaPrime, Cauchy, Chi, ChiNoncentral, ChiSquared,
    -    Dagum, Erlang, Exponential, FDistribution, FisherZ, Frechet, Gamma,
    -    GammaInverse, Kumaraswamy, Laplace, Logistic, LogNormal, Maxwell,
    -    Nakagami, Normal, Pareto, QuadraticU, RaisedCosine, Rayleigh,
    -    StudentT, Triangular, Uniform, UniformSum, VonMises, Weibull,
    -    WignerSemicircle
    -)
    -__all__.extend(crv_types.__all__)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/stats/crv.html b/dev-py3k/_modules/sympy/stats/crv.html deleted file mode 100644 index ec85c6f7149..00000000000 --- a/dev-py3k/_modules/sympy/stats/crv.html +++ /dev/null @@ -1,437 +0,0 @@ - - - - - - - - - - sympy.stats.crv — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.stats.crv

    -"""
    -Continuous Random Variables Module
    -
    -See Also
    -========
    -sympy.stats.crv_types
    -sympy.stats.rv
    -sympy.stats.frv
    -"""
    -
    -from sympy.stats.rv import (RandomDomain, SingleDomain, ConditionalDomain,
    -        ProductDomain, PSpace, SinglePSpace, random_symbols, ProductPSpace)
    -from sympy.functions.special.delta_functions import DiracDelta
    -from sympy import (S, Interval, symbols, sympify, Dummy, FiniteSet, Mul, Tuple,
    -        Integral, And, Or, Piecewise, solve, cacheit, integrate, oo, Lambda)
    -from sympy.solvers.inequalities import reduce_poly_inequalities
    -from sympy.polys.polyerrors import PolynomialError
    -import random
    -
    -
    -
    [docs]class ContinuousDomain(RandomDomain): - """ - A domain with continuous support - - Represented using symbols and Intervals. - """ - is_Continuous = True - - def as_boolean(self): - raise NotImplementedError("Not Implemented for generic Domains") - -
    -class SingleContinuousDomain(ContinuousDomain, SingleDomain): - """ - A univariate domain with continuous support - - Represented using a single symbol and interval. - """ - def __new__(cls, symbol, set): - assert symbol.is_Symbol - symbols = FiniteSet(symbol) - return RandomDomain.__new__(cls, symbols, set) - - def integrate(self, expr, variables=None, **kwargs): - if variables is None: - variables = self.symbols - if not variables: - return expr - assert frozenset(variables) == frozenset(self.symbols) - # assumes only intervals - evaluate = kwargs.pop('evaluate', True) - if evaluate: - return integrate(expr, (self.symbol, self.set), **kwargs) - else: - return Integral(expr, (self.symbol, self.set), **kwargs) - - def as_boolean(self): - return self.set.as_relational(self.symbol) - - -class ProductContinuousDomain(ProductDomain, ContinuousDomain): - """ - A collection of independent domains with continuous support - """ - - def integrate(self, expr, variables=None, **kwargs): - if variables is None: - variables = self.symbols - for domain in self.domains: - domain_vars = frozenset(variables) & frozenset(domain.symbols) - if domain_vars: - expr = domain.integrate(expr, domain_vars, **kwargs) - return expr - - def as_boolean(self): - return And(*[domain.as_boolean() for domain in self.domains]) - - -class ConditionalContinuousDomain(ContinuousDomain, ConditionalDomain): - """ - A domain with continuous support that has been further restricted by a - condition such as x > 3 - """ - - def integrate(self, expr, variables=None, **kwargs): - if variables is None: - variables = self.symbols - if not variables: - return expr - # Extract the full integral - fullintgrl = self.fulldomain.integrate(expr, variables, evaluate=False) - # separate into integrand and limits - integrand, limits = fullintgrl.function, list(fullintgrl.limits) - - conditions = [self.condition] - while conditions: - cond = conditions.pop() - if cond.is_Boolean: - if isinstance(cond, And): - conditions.extend(cond.args) - elif isinstance(cond, Or): - raise NotImplementedError("Or not implemented here") - elif cond.is_Relational: - if cond.is_Equality: - # Add the appropriate Delta to the integrand - integrand *= DiracDelta(cond.lhs - cond.rhs) - else: - symbols = cond.free_symbols & set(self.symbols) - if len(symbols) != 1: # Can't handle x > y - raise NotImplementedError( - "Multivariate Inequalities not yet implemented") - # Can handle x > 0 - symbol = symbols.pop() - # Find the limit with x, such as (x, -oo, oo) - for i, limit in enumerate(limits): - if limit[0] == symbol: - # Make condition into an Interval like [0, oo] - cintvl = reduce_poly_inequalities_wrap( - cond, symbol) - # Make limit into an Interval like [-oo, oo] - lintvl = Interval(limit[1], limit[2]) - # Intersect them to get [0, oo] - intvl = cintvl.intersect(lintvl) - # Put back into limits list - limits[i] = (symbol, intvl.left, intvl.right) - else: - raise TypeError( - "Condition %s is not a relational or Boolean" % cond) - - evaluate = kwargs.pop('evaluate', True) - if evaluate: - return integrate(integrand, *limits, **kwargs) - return Integral(integrand, *limits, **kwargs) - - def as_boolean(self): - return And(self.fulldomain.as_boolean(), self.condition) - - @property - def set(self): - if len(self.symbols) == 1: - return (self.fulldomain.set & reduce_poly_inequalities_wrap( - self.condition, tuple(self.symbols)[0])) - else: - raise NotImplementedError( - "Set of Conditional Domain not Implemented") - - -
    [docs]class ContinuousPSpace(PSpace): - """ - A Continuous Probability Space - - Represents the likelihood of an event space defined over a continuum. - - Represented with a set of symbols and a probability density function. - """ - - is_Continuous = True - - def integrate(self, expr, rvs=None, **kwargs): - if rvs is None: - rvs = self.values - else: - rvs = frozenset(rvs) - - expr = expr.subs(dict((rv, rv.symbol) for rv in rvs)) - - domain_symbols = frozenset(rv.symbol for rv in rvs) - - return self.domain.integrate(self.density * expr, - domain_symbols, **kwargs) - - def compute_density(self, expr, **kwargs): - # Common case Density(X) where X in self.values - if expr in self.values: - # Marginalize all other random symbols out of the density - density = self.domain.integrate(self.density, set(rs.symbol - for rs in self.values - frozenset((expr,))), **kwargs) - return Lambda(expr.symbol, density) - - z = Dummy('z', real=True, bounded=True) - return Lambda(z, self.integrate(DiracDelta(expr - z), **kwargs)) - - @cacheit - def compute_cdf(self, expr, **kwargs): - if not self.domain.set.is_Interval: - raise ValueError( - "CDF not well defined on multivariate expressions") - - d = self.compute_density(expr, **kwargs) - x, z = symbols('x, z', real=True, bounded=True, cls=Dummy) - left_bound = self.domain.set.start - - # CDF is integral of PDF from left bound to z - cdf = integrate(d(x), (x, left_bound, z), **kwargs) - # CDF Ensure that CDF left of left_bound is zero - cdf = Piecewise((cdf, z >= left_bound), (0, True)) - return Lambda(z, cdf) - - def probability(self, condition, **kwargs): - z = Dummy('z', real=True, bounded=True) - # Univariate case can be handled by where - try: - domain = self.where(condition) - rv = [rv for rv in self.values if rv.symbol == domain.symbol][0] - # Integrate out all other random variables - pdf = self.compute_density(rv, **kwargs) - # Integrate out the last variable over the special domain - evaluate = kwargs.pop("evaluate", True) - if evaluate: - return integrate(pdf(z), (z, domain.set), **kwargs) - else: - return Integral(pdf(z), (z, domain.set), **kwargs) - - # Other cases can be turned into univariate case - # by computing a density handled by density computation - except NotImplementedError: - expr = condition.lhs - condition.rhs - density = self.compute_density(expr, **kwargs) - # Turn problem into univariate case - space = SingleContinuousPSpace(z, density(z)) - return space.probability(condition.__class__(space.value, 0)) - - def where(self, condition): - rvs = frozenset(random_symbols(condition)) - if not (len(rvs) == 1 and rvs.issubset(self.values)): - raise NotImplementedError( - "Multiple continuous random variables not supported") - rv = tuple(rvs)[0] - interval = reduce_poly_inequalities_wrap(condition, rv) - interval = interval.intersect(self.domain.set) - return SingleContinuousDomain(rv.symbol, interval) - - def conditional_space(self, condition, normalize=True, **kwargs): - - condition = condition.subs(dict((rv, rv.symbol) for rv in self.values)) - - domain = ConditionalContinuousDomain(self.domain, condition) - density = self.density - if normalize: - density = density / domain.integrate(density, **kwargs) - - return ContinuousPSpace(domain, density) - -
    -class SingleContinuousPSpace(ContinuousPSpace, SinglePSpace): - """ - A continuous probability space over a single univariate domain - - This class is commonly implemented by the various ContinuousRV types - such as Normal, Exponential, Uniform, etc.... - """ - def __new__(cls, symbol, density, set=Interval(-oo, oo)): - domain = SingleContinuousDomain(sympify(symbol), set) - obj = ContinuousPSpace.__new__(cls, domain, density) - obj._cdf = None - return obj - - @cacheit - def _inverse_cdf_expression(self): - """ - Inverse of the CDF - - See Also - ======== - compute_cdf - sample - """ - d = self.compute_cdf(self.value) - x, z = symbols('x, z', real=True, positive=True, cls=Dummy) - # Invert CDF - try: - inverse_cdf = solve(d(x) - z, x) - except NotImplementedError: - inverse_cdf = None - if not inverse_cdf or len(inverse_cdf) != 1: - raise NotImplementedError("Could not invert CDF") - - return Lambda(z, inverse_cdf[0]) - - def sample(self): - """ - Internal sample method - - Returns dictionary mapping RandomSymbol to realization value. - """ - icdf = self._inverse_cdf_expression() - return {self.value: icdf(random.uniform(0, 1))} - - -class ProductContinuousPSpace(ProductPSpace, ContinuousPSpace): - """ - A collection of independent continuous probability spaces - """ - @property - def density(self): - return Mul(*[space.density for space in self.spaces]) - - -def _reduce_inequalities(conditions, var, **kwargs): - try: - return reduce_poly_inequalities(conditions, var, **kwargs) - except PolynomialError: - raise ValueError("Reduction of condition failed %s\n" % conditions[0]) - - -def reduce_poly_inequalities_wrap(condition, var): - if condition.is_Relational: - return _reduce_inequalities([[condition]], var, relational=False) - if condition.__class__ is Or: - return _reduce_inequalities([list(condition.args)], - var, relational=False) - if condition.__class__ is And: - intervals = [_reduce_inequalities([[arg]], var, relational=False) - for arg in condition.args] - I = intervals[0] - for i in intervals: - I = I.intersect(i) - return I -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/stats/crv_types.html b/dev-py3k/_modules/sympy/stats/crv_types.html deleted file mode 100644 index d59ed5be5ac..00000000000 --- a/dev-py3k/_modules/sympy/stats/crv_types.html +++ /dev/null @@ -1,2601 +0,0 @@ - - - - - - - - - - sympy.stats.crv_types — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.stats.crv_types

    -"""
    -Continuous Random Variables - Prebuilt variables
    -
    -Contains
    -========
    -Arcsin
    -Benini
    -Beta
    -BetaPrime
    -Cauchy
    -Chi
    -ChiNoncentral
    -ChiSquared
    -Dagum
    -Erlang
    -Exponential
    -FDistribution
    -FisherZ
    -Frechet
    -Gamma
    -GammaInverse
    -Kumaraswamy
    -Laplace
    -Logistic
    -LogNormal
    -Maxwell
    -Nakagami
    -Normal
    -Pareto
    -QuadraticU
    -RaisedCosine
    -Rayleigh
    -StudentT
    -Triangular
    -Uniform
    -UniformSum
    -VonMises
    -Weibull
    -WignerSemicircle
    -"""
    -
    -from sympy import (exp, log, sqrt, pi, S, Dummy, Interval, S, sympify, gamma,
    -                   Piecewise, And, Eq, binomial, factorial, Sum, floor, Abs,
    -                   Symbol, log, besseli)
    -from sympy import beta as beta_fn
    -from sympy import cos, exp, besseli
    -from .crv import SingleContinuousPSpace
    -from sympy.core.decorators import _sympifyit
    -import random
    -
    -oo = S.Infinity
    -
    -__all__ = ['ContinuousRV',
    -'Arcsin',
    -'Benini',
    -'Beta',
    -'BetaPrime',
    -'Cauchy',
    -'Chi',
    -'ChiNoncentral',
    -'ChiSquared',
    -'Dagum',
    -'Erlang',
    -'Exponential',
    -'FDistribution',
    -'FisherZ',
    -'Frechet',
    -'Gamma',
    -'GammaInverse',
    -'Kumaraswamy',
    -'Laplace',
    -'Logistic',
    -'LogNormal',
    -'Maxwell',
    -'Nakagami',
    -'Normal',
    -'Pareto',
    -'QuadraticU',
    -'RaisedCosine',
    -'Rayleigh',
    -'StudentT',
    -'Triangular',
    -'Uniform',
    -'UniformSum',
    -'VonMises',
    -'Weibull',
    -'WignerSemicircle'
    -]
    -
    -
    -def _value_check(condition, message):
    -    """
    -    Check a condition on input value.
    -
    -    Raises ValueError with message if condition is not True
    -    """
    -    if condition is not True:
    -        raise ValueError(message)
    -
    -
    -def ContinuousRV(symbol, density, set=Interval(-oo, oo)):
    -    """
    -    Create a Continuous Random Variable given the following:
    -
    -    -- a symbol
    -    -- a probability density function
    -    -- set on which the pdf is valid (defaults to entire real line)
    -
    -    Returns a RandomSymbol.
    -
    -    Many common continuous random variable types are already implemented.
    -    This function should be necessary only very rarely.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy import Symbol, sqrt, exp, pi
    -    >>> from sympy.stats import ContinuousRV, P, E
    -
    -    >>> x = Symbol("x")
    -
    -    >>> pdf = sqrt(2)*exp(-x**2/2)/(2*sqrt(pi)) # Normal distribution
    -    >>> X = ContinuousRV(x, pdf)
    -
    -    >>> E(X)
    -    0
    -    >>> P(X>0)
    -    1/2
    -    """
    -    return SingleContinuousPSpace(symbol, density, set).value
    -
    -########################################
    -# Continuous Probability Distributions #
    -########################################
    -
    -#-------------------------------------------------------------------------------
    -# Arcsin distribution ----------------------------------------------------------
    -
    -
    -class ArcsinPSpace(SingleContinuousPSpace):
    -    def __new__(cls, name, a, b):
    -        a, b = sympify(a), sympify(b)
    -        x = Symbol(name)
    -        pdf = 1/(pi*sqrt((x - a)*(b - x)))
    -        obj = SingleContinuousPSpace.__new__(cls, x, pdf, set=Interval(a, b))
    -        return obj
    -
    -
    -def Arcsin(name, a=0, b=1):
    -    r"""
    -    Create a Continuous Random Variable with an arcsin distribution.
    -
    -    The density of the arcsin distribution is given by
    -
    -    .. math::
    -        f(x) := \frac{1}{\pi\sqrt{(x-a)(b-x)}}
    -
    -    with :math:`x \in [a,b]`. It must hold that :math:`-\infty < a < b < \infty`.
    -
    -    Parameters
    -    ==========
    -
    -    a : Real number, the left interval boundary
    -    b : Real number, the right interval boundary
    -
    -    Returns
    -    =======
    -
    -    A RandomSymbol.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.stats import Arcsin, density
    -    >>> from sympy import Symbol, simplify
    -
    -    >>> a = Symbol("a", real=True)
    -    >>> b = Symbol("b", real=True)
    -
    -    >>> X = Arcsin("x", a, b)
    -
    -    >>> density(X)
    -    Lambda(_x, 1/(pi*sqrt((-_x + b)*(_x - a))))
    -
    -    References
    -    ==========
    -
    -    [1] http://en.wikipedia.org/wiki/Arcsine_distribution
    -    """
    -
    -    return ArcsinPSpace(name, a, b).value
    -
    -#-------------------------------------------------------------------------------
    -# Benini distribution ----------------------------------------------------------
    -
    -
    -class BeniniPSpace(SingleContinuousPSpace):
    -    def __new__(cls, name, alpha, beta, sigma):
    -        alpha, beta, sigma = sympify(alpha), sympify(beta), sympify(sigma)
    -        x = Symbol(name)
    -        pdf = (exp(-alpha*log(x/sigma) - beta*log(x/sigma)**2)
    -               *(alpha/x + 2*beta*log(x/sigma)/x))
    -        obj = SingleContinuousPSpace.__new__(cls, x, pdf,
    -                                             set=Interval(sigma, oo))
    -        return obj
    -
    -
    -def Benini(name, alpha, beta, sigma):
    -    r"""
    -    Create a Continuous Random Variable with a Benini distribution.
    -
    -    The density of the Benini distribution is given by
    -
    -    .. math::
    -        f(x) := e^{-\alpha\log{\frac{x}{\sigma}}
    -                -\beta\log\left[{\frac{x}{\sigma}}\right]^2}
    -                \left(\frac{\alpha}{x}+\frac{2\beta\log{\frac{x}{\sigma}}}{x}\right)
    -
    -    Parameters
    -    ==========
    -
    -    alpha : Real number, `alpha` > 0 a shape
    -    beta : Real number, `beta` > 0 a shape
    -    sigma : Real number, `sigma` > 0 a scale
    -
    -    Returns
    -    =======
    -
    -    A RandomSymbol.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.stats import Benini, density
    -    >>> from sympy import Symbol, simplify, pprint
    -
    -    >>> alpha = Symbol("alpha", positive=True)
    -    >>> beta = Symbol("beta", positive=True)
    -    >>> sigma = Symbol("sigma", positive=True)
    -
    -    >>> X = Benini("x", alpha, beta, sigma)
    -
    -    >>> D = density(X)
    -    >>> pprint(D, use_unicode=False)
    -          /                                                             2       \
    -          |   /                  /  x  \\             /  x  \            /  x  \|
    -          |   |        2*beta*log|-----||  - alpha*log|-----| - beta*log |-----||
    -          |   |alpha             \sigma/|             \sigma/            \sigma/|
    -    Lambda|x, |----- + -----------------|*e                                     |
    -          \   \  x             x        /                                       /
    -
    -    References
    -    ==========
    -
    -    [1] http://en.wikipedia.org/wiki/Benini_distribution
    -    """
    -
    -    return BeniniPSpace(name, alpha, beta, sigma).value
    -
    -#-------------------------------------------------------------------------------
    -# Beta distribution ------------------------------------------------------------
    -
    -
    -class BetaPSpace(SingleContinuousPSpace):
    -    def __new__(cls, name, alpha, beta):
    -        alpha, beta = sympify(alpha), sympify(beta)
    -
    -        _value_check(alpha > 0, "Alpha must be positive")
    -        _value_check(beta > 0, "Beta must be positive")
    -
    -        x = Symbol(name)
    -        pdf = x**(alpha - 1) * (1 - x)**(beta - 1) / beta_fn(alpha, beta)
    -
    -        obj = SingleContinuousPSpace.__new__(cls, x, pdf, set=Interval(0, 1))
    -        obj.alpha = alpha
    -        obj.beta = beta
    -        return obj
    -
    -    def sample(self):
    -        return {self.value: random.betavariate(self.alpha, self.beta)}
    -
    -
    -def Beta(name, alpha, beta):
    -    r"""
    -    Create a Continuous Random Variable with a Beta distribution.
    -
    -    The density of the Beta distribution is given by
    -
    -    .. math::
    -        f(x) := \frac{x^{\alpha-1}(1-x)^{\beta-1}} {\mathrm{B}(\alpha,\beta)}
    -
    -    with :math:`x \in [0,1]`.
    -
    -    Parameters
    -    ==========
    -
    -    alpha : Real number, `alpha` > 0 a shape
    -    beta : Real number, `beta` > 0 a shape
    -
    -    Returns
    -    =======
    -
    -    A RandomSymbol.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.stats import Beta, density, E, variance
    -    >>> from sympy import Symbol, simplify, pprint
    -
    -    >>> alpha = Symbol("alpha", positive=True)
    -    >>> beta = Symbol("beta", positive=True)
    -
    -    >>> X = Beta("x", alpha, beta)
    -
    -    >>> D = density(X)
    -    >>> pprint(D, use_unicode=False)
    -          /    alpha - 1         beta - 1                    \
    -          |   x         *(-x + 1)        *gamma(alpha + beta)|
    -    Lambda|x, -----------------------------------------------|
    -          \               gamma(alpha)*gamma(beta)           /
    -
    -    >>> simplify(E(X, meijerg=True))
    -    alpha/(alpha + beta)
    -
    -    >>> simplify(variance(X, meijerg=True))
    -    alpha*beta/((alpha + beta)**2*(alpha + beta + 1))
    -
    -    References
    -    ==========
    -
    -    [1] http://en.wikipedia.org/wiki/Beta_distribution
    -    [2] http://mathworld.wolfram.com/BetaDistribution.html
    -    """
    -
    -    return BetaPSpace(name, alpha, beta).value
    -
    -#-------------------------------------------------------------------------------
    -# Beta prime distribution ------------------------------------------------------
    -
    -
    -class BetaPrimePSpace(SingleContinuousPSpace):
    -    def __new__(cls, name, alpha, beta):
    -        alpha, beta = sympify(alpha), sympify(beta)
    -        x = Symbol(name)
    -        pdf = x**(alpha - 1)*(1 + x)**(-alpha - beta)/beta_fn(alpha, beta)
    -        obj = SingleContinuousPSpace.__new__(cls, x, pdf, set=Interval(0, oo))
    -        return obj
    -
    -
    -def BetaPrime(name, alpha, beta):
    -    r"""
    -    Create a continuous random variable with a Beta prime distribution.
    -
    -    The density of the Beta prime distribution is given by
    -
    -    .. math::
    -        f(x) := \frac{x^{\alpha-1} (1+x)^{-\alpha -\beta}}{B(\alpha,\beta)}
    -
    -    with :math:`x > 0`.
    -
    -    Parameters
    -    ==========
    -
    -    alpha : Real number, `alpha` > 0 a shape
    -    beta : Real number, `beta` > 0 a shape
    -
    -    Returns
    -    =======
    -
    -    A RandomSymbol.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.stats import BetaPrime, density
    -    >>> from sympy import Symbol, pprint
    -
    -    >>> alpha = Symbol("alpha", positive=True)
    -    >>> beta = Symbol("beta", positive=True)
    -
    -    >>> X = BetaPrime("x", alpha, beta)
    -
    -    >>> D = density(X)
    -    >>> pprint(D, use_unicode=False)
    -          /    alpha - 1        -alpha - beta                    \
    -          |   x         *(x + 1)             *gamma(alpha + beta)|
    -    Lambda|x, ---------------------------------------------------|
    -          \                 gamma(alpha)*gamma(beta)             /
    -
    -    References
    -    ==========
    -
    -    [1] http://en.wikipedia.org/wiki/Beta_prime_distribution
    -    [2] http://mathworld.wolfram.com/BetaPrimeDistribution.html
    -    """
    -
    -    return BetaPrimePSpace(name, alpha, beta).value
    -
    -#-------------------------------------------------------------------------------
    -# Cauchy distribution ----------------------------------------------------------
    -
    -
    -class CauchyPSpace(SingleContinuousPSpace):
    -    def __new__(cls, name, x0, gamma):
    -        x0, gamma = sympify(x0), sympify(gamma)
    -        x = Symbol(name)
    -        pdf = 1/(pi*gamma*(1 + ((x - x0)/gamma)**2))
    -        obj = SingleContinuousPSpace.__new__(cls, x, pdf)
    -        return obj
    -
    -
    -def Cauchy(name, x0, gamma):
    -    r"""
    -    Create a continuous random variable with a Cauchy distribution.
    -
    -    The density of the Cauchy distribution is given by
    -
    -    .. math::
    -        f(x) := \frac{1}{\pi} \arctan\left(\frac{x-x_0}{\gamma}\right)
    -                +\frac{1}{2}
    -
    -    Parameters
    -    ==========
    -
    -    x0 : Real number, the location
    -    gamma : Real number, `gamma` > 0 the scale
    -
    -    Returns
    -    =======
    -
    -    A RandomSymbol.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.stats import Cauchy, density
    -    >>> from sympy import Symbol
    -
    -    >>> x0 = Symbol("x0")
    -    >>> gamma = Symbol("gamma", positive=True)
    -
    -    >>> X = Cauchy("x", x0, gamma)
    -
    -    >>> density(X)
    -    Lambda(_x, 1/(pi*gamma*(1 + (_x - x0)**2/gamma**2)))
    -
    -    References
    -    ==========
    -
    -    [1] http://en.wikipedia.org/wiki/Cauchy_distribution
    -    [2] http://mathworld.wolfram.com/CauchyDistribution.html
    -    """
    -
    -    return CauchyPSpace(name, x0, gamma).value
    -
    -#-------------------------------------------------------------------------------
    -# Chi distribution -------------------------------------------------------------
    -
    -
    -class ChiPSpace(SingleContinuousPSpace):
    -    def __new__(cls, name, k):
    -        k = sympify(k)
    -        x = Symbol(name)
    -        pdf = 2**(1 - k/2)*x**(k - 1)*exp(-x**2/2)/gamma(k/2)
    -        obj = SingleContinuousPSpace.__new__(cls, x, pdf, set=Interval(0, oo))
    -        return obj
    -
    -
    -def Chi(name, k):
    -    r"""
    -    Create a continuous random variable with a Chi distribution.
    -
    -    The density of the Chi distribution is given by
    -
    -    .. math::
    -        f(x) := \frac{2^{1-k/2}x^{k-1}e^{-x^2/2}}{\Gamma(k/2)}
    -
    -    with :math:`x \geq 0`.
    -
    -    Parameters
    -    ==========
    -
    -    k : Integer, `k` > 0 the number of degrees of freedom
    -
    -    Returns
    -    =======
    -
    -    A RandomSymbol.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.stats import Chi, density, E, std
    -    >>> from sympy import Symbol, simplify
    -
    -    >>> k = Symbol("k", integer=True)
    -
    -    >>> X = Chi("x", k)
    -
    -    >>> density(X)
    -    Lambda(_x, 2**(-k/2 + 1)*_x**(k - 1)*exp(-_x**2/2)/gamma(k/2))
    -
    -    References
    -    ==========
    -
    -    [1] http://en.wikipedia.org/wiki/Chi_distribution
    -    [2] http://mathworld.wolfram.com/ChiDistribution.html
    -    """
    -
    -    return ChiPSpace(name, k).value
    -
    -#-------------------------------------------------------------------------------
    -# Non-central Chi distribution -------------------------------------------------
    -
    -
    -class ChiNoncentralPSpace(SingleContinuousPSpace):
    -    def __new__(cls, name, k, l):
    -        k = sympify(k)
    -        l = sympify(l)
    -        x = Symbol(name)
    -        pdf = exp(-(x**2+l**2)/2)*x**k*l / (l*x)**(k/2) * besseli(k/2-1, l*x)
    -        obj = SingleContinuousPSpace.__new__(cls, x, pdf, set = Interval(0, oo))
    -        return obj
    -
    -
    -def ChiNoncentral(name, k, l):
    -    r"""
    -    Create a continuous random variable with a non-central Chi distribution.
    -
    -    The density of the non-central Chi distribution is given by
    -
    -    .. math::
    -        f(x) := \frac{e^{-(x^2+\lambda^2)/2} x^k\lambda}
    -                {(\lambda x)^{k/2}} I_{k/2-1}(\lambda x)
    -
    -    with :math:`x \geq 0`.
    -
    -    Parameters
    -    ==========
    -
    -    k : `k` > 0 the number of degrees of freedom
    -    l : Shift parameter
    -
    -    Returns
    -    =======
    -
    -    A RandomSymbol.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.stats import ChiNoncentral, density, E, std
    -    >>> from sympy import Symbol, simplify
    -
    -    >>> k = Symbol("k", integer=True)
    -    >>> l = Symbol("l")
    -
    -    >>> X = ChiNoncentral("x", k, l)
    -
    -    >>> density(X)
    -    Lambda(_x, _x**k*l*(_x*l)**(-k/2)*exp(-_x**2/2 - l**2/2)*besseli(k/2 - 1, _x*l))
    -
    -    References
    -    ==========
    -
    -    [1] http://en.wikipedia.org/wiki/Noncentral_chi_distribution
    -    """
    -
    -    return ChiNoncentralPSpace(name, k, l).value
    -
    -#-------------------------------------------------------------------------------
    -# Chi squared distribution -----------------------------------------------------
    -
    -
    -class ChiSquaredPSpace(SingleContinuousPSpace):
    -    def __new__(cls, name, k):
    -        k = sympify(k)
    -        x = Symbol(name)
    -        pdf = 1/(2**(k/2)*gamma(k/2))*x**(k/2 - 1)*exp(-x/2)
    -        obj = SingleContinuousPSpace.__new__(cls, x, pdf, set=Interval(0, oo))
    -        return obj
    -
    -
    -def ChiSquared(name, k):
    -    r"""
    -    Create a continuous random variable with a Chi-squared distribution.
    -
    -    The density of the Chi-squared distribution is given by
    -
    -    .. math::
    -        f(x) := \frac{1}{2^{\frac{k}{2}}\Gamma\left(\frac{k}{2}\right)}
    -                x^{\frac{k}{2}-1} e^{-\frac{x}{2}}
    -
    -    with :math:`x \geq 0`.
    -
    -    Parameters
    -    ==========
    -
    -    k : Integer, `k` > 0 the number of degrees of freedom
    -
    -    Returns
    -    =======
    -
    -    A RandomSymbol.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.stats import ChiSquared, density, E, variance
    -    >>> from sympy import Symbol, simplify, combsimp, expand_func
    -
    -    >>> k = Symbol("k", integer=True, positive=True)
    -
    -    >>> X = ChiSquared("x", k)
    -
    -    >>> density(X)
    -    Lambda(_x, 2**(-k/2)*_x**(k/2 - 1)*exp(-_x/2)/gamma(k/2))
    -
    -    >>> combsimp(E(X))
    -    k
    -
    -    >>> simplify(expand_func(variance(X)))
    -    2*k
    -
    -    References
    -    ==========
    -
    -    [1] http://en.wikipedia.org/wiki/Chi_squared_distribution
    -    [2] http://mathworld.wolfram.com/Chi-SquaredDistribution.html
    -    """
    -
    -    return ChiSquaredPSpace(name, k).value
    -
    -#-------------------------------------------------------------------------------
    -# Dagum distribution -----------------------------------------------------------
    -
    -
    -class DagumPSpace(SingleContinuousPSpace):
    -    def __new__(cls, name, p, a, b):
    -        p, a, b = sympify(p), sympify(a), sympify(b)
    -        x = Symbol(name)
    -        pdf = a*p/x*((x/b)**(a*p)/(((x/b)**a + 1)**(p + 1)))
    -        obj = SingleContinuousPSpace.__new__(cls, x, pdf)
    -        return obj
    -
    -
    -def Dagum(name, p, a, b):
    -    r"""
    -    Create a continuous random variable with a Dagum distribution.
    -
    -    The density of the Dagum distribution is given by
    -
    -    .. math::
    -        f(x) := \frac{a p}{x} \left( \frac{(\tfrac{x}{b})^{a p}}
    -                {\left((\tfrac{x}{b})^a + 1 \right)^{p+1}} \right)
    -
    -    with :math:`x > 0`.
    -
    -    Parameters
    -    ==========
    -
    -    p : Real number, `p` > 0 a shape
    -    a : Real number, `a` > 0 a shape
    -    b : Real number, `b` > 0 a scale
    -
    -    Returns
    -    =======
    -
    -    A RandomSymbol.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.stats import Dagum, density
    -    >>> from sympy import Symbol, simplify
    -
    -    >>> p = Symbol("p", positive=True)
    -    >>> b = Symbol("b", positive=True)
    -    >>> a = Symbol("a", positive=True)
    -
    -    >>> X = Dagum("x", p, a, b)
    -
    -    >>> density(X)
    -    Lambda(_x, a*p*(_x/b)**(a*p)*((_x/b)**a + 1)**(-p - 1)/_x)
    -
    -    References
    -    ==========
    -
    -    [1] http://en.wikipedia.org/wiki/Dagum_distribution
    -    """
    -
    -    return DagumPSpace(name, p, a, b).value
    -
    -#-------------------------------------------------------------------------------
    -# Erlang distribution ----------------------------------------------------------
    -
    -def Erlang(name, k, l):
    -    r"""
    -    Create a continuous random variable with an Erlang distribution.
    -
    -    The density of the Erlang distribution is given by
    -
    -    .. math::
    -        f(x) := \frac{\lambda^k x^{k-1} e^{-\lambda x}}{(k-1)!}
    -
    -    with :math:`x \in [0,\infty]`.
    -
    -    Parameters
    -    ==========
    -
    -    k : Integer
    -    l : Real number, :math:`\lambda` > 0 the rate
    -
    -    Returns
    -    =======
    -
    -    A RandomSymbol.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.stats import Erlang, density, cdf, E, variance
    -    >>> from sympy import Symbol, simplify, pprint
    -
    -    >>> k = Symbol("k", integer=True, positive=True)
    -    >>> l = Symbol("l", positive=True)
    -
    -    >>> X = Erlang("x", k, l)
    -
    -    >>> D = density(X)
    -    >>> pprint(D, use_unicode=False)
    -          /    k - 1  k  -x*l\
    -          |   x     *l *e    |
    -    Lambda|x, ---------------|
    -          \       gamma(k)   /
    -
    -    >>> C = cdf(X, meijerg=True)
    -    >>> pprint(C, use_unicode=False)
    -          /   /  k*lowergamma(k, 0)   k*lowergamma(k, z*l)            \
    -    Lambda|z, |- ------------------ + --------------------  for z >= 0|
    -          |   <     gamma(k + 1)          gamma(k + 1)                |
    -          |   |                                                       |
    -          \   \                     0                       otherwise /
    -
    -    >>> simplify(E(X))
    -    k/l
    -
    -    >>> simplify(variance(X))
    -    (gamma(k)*gamma(k + 2) - gamma(k + 1)**2)/(l**2*gamma(k)**2)
    -
    -    References
    -    ==========
    -
    -    [1] http://en.wikipedia.org/wiki/Erlang_distribution
    -    [2] http://mathworld.wolfram.com/ErlangDistribution.html
    -    """
    -
    -    return GammaPSpace(name, k, 1/l).value
    -
    -#-------------------------------------------------------------------------------
    -# Exponential distribution -----------------------------------------------------
    -
    -
    -class ExponentialPSpace(SingleContinuousPSpace):
    -    def __new__(cls, name, rate):
    -        rate = sympify(rate)
    -
    -        _value_check(rate > 0, "Rate must be positive.")
    -
    -        x = Symbol(name)
    -        pdf = rate * exp(-rate*x)
    -
    -        obj = SingleContinuousPSpace.__new__(cls, x, pdf, set=Interval(0, oo))
    -        obj.rate = rate
    -        return obj
    -
    -    def sample(self):
    -        return {self.value: random.expovariate(self.rate)}
    -
    -
    -def Exponential(name, rate):
    -    r"""
    -    Create a continuous random variable with an Exponential distribution.
    -
    -    The density of the exponential distribution is given by
    -
    -    .. math::
    -        f(x) := \lambda \exp(-\lambda x)
    -
    -    with :math:`x > 0`.
    -
    -    Parameters
    -    ==========
    -
    -    rate : Real number, `rate` > 0 the rate or inverse scale
    -
    -    Returns
    -    =======
    -
    -    A RandomSymbol.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.stats import Exponential, density, cdf, E
    -    >>> from sympy.stats import variance, std, skewness
    -    >>> from sympy import Symbol
    -
    -    >>> l = Symbol("lambda", positive=True)
    -
    -    >>> X = Exponential("x", l)
    -
    -    >>> density(X)
    -    Lambda(_x, lambda*exp(-_x*lambda))
    -
    -    >>> cdf(X)
    -    Lambda(_z, Piecewise((1 - exp(-_z*lambda), _z >= 0), (0, True)))
    -
    -    >>> E(X)
    -    1/lambda
    -
    -    >>> variance(X)
    -    lambda**(-2)
    -
    -    >>> skewness(X)
    -    2
    -
    -    >>> X = Exponential('x', 10)
    -
    -    >>> density(X)
    -    Lambda(_x, 10*exp(-10*_x))
    -
    -    >>> E(X)
    -    1/10
    -
    -    >>> std(X)
    -    1/10
    -
    -    References
    -    ==========
    -
    -    [1] http://en.wikipedia.org/wiki/Exponential_distribution
    -    [2] http://mathworld.wolfram.com/ExponentialDistribution.html
    -    """
    -
    -    return ExponentialPSpace(name, rate).value
    -
    -#-------------------------------------------------------------------------------
    -# F distribution ---------------------------------------------------------------
    -
    -class FDistributionPSpace(SingleContinuousPSpace):
    -    def __new__(cls, name, d1, d2):
    -        d1 = sympify(d1)
    -        d2 = sympify(d2)
    -        x = Symbol(name)
    -        pdf = (sqrt((d1*x)**d1*d2**d2 / (d1*x+d2)**(d1+d2))
    -               / (x * beta_fn(d1/2, d2/2)))
    -        obj = SingleContinuousPSpace.__new__(cls, x, pdf, set=Interval(0, oo))
    -        return obj
    -
    -def FDistribution(name, d1, d2):
    -    r"""
    -    Create a continuous random variable with a F distribution.
    -
    -    The density of the F distribution is given by
    -
    -    .. math::
    -        f(x) := \frac{\sqrt{\frac{(d_1 x)^{d_1} d_2^{d_2}}
    -                {(d_1 x + d_2)^{d_1 + d_2}}}}
    -                {x \mathrm{B} \left(\frac{d_1}{2}, \frac{d_2}{2}\right)}
    -
    -    with :math:`x > 0`.
    -
    -    Parameters
    -    ==========
    -
    -    d1 : `d1` > 0 a parameter
    -    d2 : `d2` > 0 a parameter
    -
    -    Returns
    -    =======
    -
    -    A RandomSymbol.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.stats import FDistribution, density
    -    >>> from sympy import Symbol, simplify, pprint
    -
    -    >>> d1 = Symbol("d1", positive=True)
    -    >>> d2 = Symbol("d2", positive=True)
    -
    -    >>> X = FDistribution("x", d1, d2)
    -
    -    >>> D = density(X)
    -    >>> pprint(D, use_unicode=False)
    -          /     d2                                                 \
    -          |     --    ______________________________               |
    -          |     2    /       d1            -d1 - d2       /d1   d2\|
    -          |   d2  *\/  (x*d1)  *(x*d1 + d2)         *gamma|-- + --||
    -          |                                               \2    2 /|
    -    Lambda|x, -----------------------------------------------------|
    -          |                          /d1\      /d2\                |
    -          |                   x*gamma|--|*gamma|--|                |
    -          \                          \2 /      \2 /                /
    -
    -    References
    -    ==========
    -
    -    [1] http://en.wikipedia.org/wiki/F-distribution
    -    [2] http://mathworld.wolfram.com/F-Distribution.html
    -    """
    -
    -    return FDistributionPSpace(name, d1, d2).value
    -
    -#-------------------------------------------------------------------------------
    -# Fisher Z distribution --------------------------------------------------------
    -
    -class FisherZPSpace(SingleContinuousPSpace):
    -    def __new__(cls, name, d1, d2):
    -        d1 = sympify(d1)
    -        d2 = sympify(d2)
    -        x = Symbol(name)
    -        pdf = (2*d1**(d1/2)*d2**(d2/2) / beta_fn(d1/2, d2/2) *
    -               exp(d1*x) / (d1*exp(2*x)+d2)**((d1+d2)/2))
    -        obj = SingleContinuousPSpace.__new__(cls, x, pdf)
    -        return obj
    -
    -def FisherZ(name, d1, d2):
    -    r"""
    -    Create a Continuous Random Variable with an Fisher's Z distribution.
    -
    -    The density of the Fisher's Z distribution is given by
    -
    -    .. math::
    -        f(x) := \frac{2d_1^{d_1/2} d_2^{d_2/2}} {\mathrm{B}(d_1/2, d_2/2)}
    -                \frac{e^{d_1z}}{\left(d_1e^{2z}+d_2\right)^{\left(d_1+d_2\right)/2}}
    -
    -    Parameters
    -    ==========
    -
    -    d1 : `d1` > 0, degree of freedom
    -    d2 : `d2` > 0, degree of freedom
    -
    -    Returns
    -    =======
    -
    -    A RandomSymbol.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.stats import FisherZ, density
    -    >>> from sympy import Symbol, simplify, pprint
    -
    -    >>> d1 = Symbol("d1", positive=True)
    -    >>> d2 = Symbol("d2", positive=True)
    -
    -    >>> X = FisherZ("x", d1, d2)
    -
    -    >>> D = density(X)
    -    >>> pprint(D, use_unicode=False)
    -          /                               d1   d2                     \
    -          |       d1   d2               - -- - --                     |
    -          |       --   --                 2    2                      |
    -          |       2    2  /    2*x     \           x*d1      /d1   d2\|
    -          |   2*d1  *d2  *\d1*e    + d2/         *e    *gamma|-- + --||
    -          |                                                  \2    2 /|
    -    Lambda|x, --------------------------------------------------------|
    -          |                          /d1\      /d2\                   |
    -          |                     gamma|--|*gamma|--|                   |
    -          \                          \2 /      \2 /                   /
    -
    -    References
    -    ==========
    -
    -    [1] http://en.wikipedia.org/wiki/Fisher%27s_z-distribution
    -    [2] http://mathworld.wolfram.com/Fishersz-Distribution.html
    -    """
    -
    -    return FisherZPSpace(name, d1, d2).value
    -
    -#-------------------------------------------------------------------------------
    -# Frechet distribution ---------------------------------------------------------
    -
    -class FrechetPSpace(SingleContinuousPSpace):
    -    def __new__(cls, name, a, s=0, m=0):
    -        a = sympify(a)
    -        s = sympify(s)
    -        m = sympify(m)
    -        x = Symbol(name)
    -        pdf = a/s * ((x-m)/s)**(-1-a) * exp(-((x-m)/s)-a)
    -        obj = SingleContinuousPSpace.__new__(cls, x, pdf, set = Interval(m, oo))
    -        return obj
    -
    -def Frechet(name, a, s=1, m=0):
    -    r"""
    -    Create a continuous random variable with a Frechet distribution.
    -
    -    The density of the Frechet distribution is given by
    -
    -    .. math::
    -        f(x) := \frac{\alpha}{s} \left(\frac{x-m}{s}\right)^{-1-\alpha}
    -                 e^{-(\frac{x-m}{s})^{-\alpha}}
    -
    -    with :math:`x \geq m`.
    -
    -    Parameters
    -    ==========
    -
    -    a : Real number, :math:`a \in \left(0, \infty\right)` the shape
    -    s : Real number, :math:`s \in \left(0, \infty\right)` the scale
    -    m : Real number, :math:`m \in \left(-\infty, \infty\right)` the minimum
    -
    -    Returns
    -    =======
    -
    -    A RandomSymbol.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.stats import Frechet, density, E, std
    -    >>> from sympy import Symbol, simplify
    -
    -    >>> a = Symbol("a", positive=True)
    -    >>> s = Symbol("s", positive=True)
    -    >>> m = Symbol("m", real=True)
    -
    -    >>> X = Frechet("x", a, s, m)
    -
    -    >>> density(X)
    -    Lambda(_x, a*((_x - m)/s)**(-a - 1)*exp(-a - (_x - m)/s)/s)
    -
    -    References
    -    ==========
    -
    -    [1] http://en.wikipedia.org/wiki/Fr%C3%A9chet_distribution
    -    """
    -
    -    return FrechetPSpace(name, a, s, m).value
    -
    -#-------------------------------------------------------------------------------
    -# Gamma distribution -----------------------------------------------------------
    -
    -
    -class GammaPSpace(SingleContinuousPSpace):
    -    def __new__(cls, name, k, theta):
    -        k, theta = sympify(k), sympify(theta)
    -
    -        _value_check(k > 0, "k must be positive")
    -        _value_check(theta > 0, "Theta must be positive")
    -
    -        x = Symbol(name)
    -        pdf = x**(k - 1) * exp(-x/theta) / (gamma(k)*theta**k)
    -
    -        obj = SingleContinuousPSpace.__new__(cls, x, pdf, set=Interval(0, oo))
    -        obj.k = k
    -        obj.theta = theta
    -        return obj
    -
    -    def sample(self):
    -        return {self.value: random.gammavariate(self.k, self.theta)}
    -
    -
    -def Gamma(name, k, theta):
    -    r"""
    -    Create a continuous random variable with a Gamma distribution.
    -
    -    The density of the Gamma distribution is given by
    -
    -    .. math::
    -        f(x) := \frac{1}{\Gamma(k) \theta^k} x^{k - 1} e^{-\frac{x}{\theta}}
    -
    -    with :math:`x \in [0,1]`.
    -
    -    Parameters
    -    ==========
    -
    -    k : Real number, `k` > 0 a shape
    -    theta : Real number, `theta` > 0 a scale
    -
    -    Returns
    -    =======
    -
    -    A RandomSymbol.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.stats import Gamma, density, cdf, E, variance
    -    >>> from sympy import Symbol, pprint
    -
    -    >>> k = Symbol("k", positive=True)
    -    >>> theta = Symbol("theta", positive=True)
    -
    -    >>> X = Gamma("x", k, theta)
    -
    -    >>> D = density(X)
    -    >>> pprint(D, use_unicode=False)
    -          /                     -x \
    -          |                   -----|
    -          |    k - 1      -k  theta|
    -          |   x     *theta  *e     |
    -    Lambda|x, ---------------------|
    -          \          gamma(k)      /
    -
    -    >>> C = cdf(X, meijerg=True)
    -    >>> pprint(C, use_unicode=False)
    -          /   /                                   /     z  \            \
    -          |   |                       k*lowergamma|k, -----|            |
    -          |   |  k*lowergamma(k, 0)               \   theta/            |
    -    Lambda|z, <- ------------------ + ----------------------  for z >= 0|
    -          |   |     gamma(k + 1)           gamma(k + 1)                 |
    -          |   |                                                         |
    -          \   \                      0                        otherwise /
    -
    -    >>> E(X)
    -    theta*gamma(k + 1)/gamma(k)
    -
    -    >>> V = variance(X)
    -    >>> pprint(V, use_unicode=False)
    -           2      2                     -k      k + 1
    -      theta *gamma (k + 1)   theta*theta  *theta     *gamma(k + 2)
    -    - -------------------- + -------------------------------------
    -                2                           gamma(k)
    -           gamma (k)
    -
    -    References
    -    ==========
    -
    -    [1] http://en.wikipedia.org/wiki/Gamma_distribution
    -    [2] http://mathworld.wolfram.com/GammaDistribution.html
    -    """
    -
    -    return GammaPSpace(name, k, theta).value
    -
    -#-------------------------------------------------------------------------------
    -# Inverse Gamma distribution ---------------------------------------------------
    -
    -class GammaInversePSpace(SingleContinuousPSpace):
    -    def __new__(cls, name, a, b):
    -        a = sympify(a)
    -        b = sympify(b)
    -        _value_check(a > 0, "alpha must be positive")
    -        _value_check(b > 0, "beta must be positive")
    -        x = Symbol(name)
    -        pdf = b**a/gamma(a) * x**(-a-1) * exp(-b/x)
    -        obj = SingleContinuousPSpace.__new__(cls, x, pdf, set=Interval(0, oo))
    -        obj.a = a
    -        obj.b = b
    -        return obj
    -
    -def GammaInverse(name, a, b):
    -    r"""
    -    Create a continuous random variable with an inverse Gamma distribution.
    -
    -    The density of the inverse Gamma distribution is given by
    -
    -    .. math::
    -        f(x) := \frac{\beta^\alpha}{\Gamma(\alpha)} x^{-\alpha - 1}
    -                \exp\left(\frac{-\beta}{x}\right)
    -
    -    with :math:`x > 0`.
    -
    -    Parameters
    -    ==========
    -
    -    a : Real number, `a` > 0 a shape
    -    b : Real number, `b` > 0 a scale
    -
    -    Returns
    -    =======
    -
    -    A RandomSymbol.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.stats import GammaInverse, density, cdf, E, variance
    -    >>> from sympy import Symbol, pprint
    -
    -    >>> a = Symbol("a", positive=True)
    -    >>> b = Symbol("b", positive=True)
    -
    -    >>> X = GammaInverse("x", a, b)
    -
    -    >>> D = density(X)
    -    >>> pprint(D, use_unicode=False)
    -          /               -b\
    -          |               --|
    -          |    -a - 1  a  x |
    -          |   x      *b *e  |
    -    Lambda|x, --------------|
    -          \      gamma(a)   /
    -
    -    References
    -    ==========
    -
    -    [1] http://en.wikipedia.org/wiki/Inverse-gamma_distribution
    -    """
    -
    -    return GammaInversePSpace(name, a, b).value
    -
    -#-------------------------------------------------------------------------------
    -# Kumaraswamy distribution -----------------------------------------------------
    -
    -class KumaraswamyPSpace(SingleContinuousPSpace):
    -    def __new__(cls, name, a, b):
    -        a, b = sympify(a), sympify(b)
    -
    -        _value_check(a > 0, "a must be positive")
    -        _value_check(b > 0, "b must be positive")
    -
    -        x = Symbol(name)
    -        pdf = a * b * x**(a-1) * (1-x**a)**(b-1)
    -
    -        obj = SingleContinuousPSpace.__new__(cls, x, pdf, set=Interval(0, 1))
    -        obj.a = a
    -        obj.b = b
    -        return obj
    -
    -def Kumaraswamy(name, a, b):
    -    r"""
    -    Create a Continuous Random Variable with a Kumaraswamy distribution.
    -
    -    The density of the Kumaraswamy distribution is given by
    -
    -    .. math::
    -        f(x) := a b x^{a-1} (1-x^a)^{b-1}
    -
    -    with :math:`x \in [0,1]`.
    -
    -    Parameters
    -    ==========
    -
    -    a : Real number, `a` > 0 a shape
    -    b : Real number, `b` > 0 a shape
    -
    -    Returns
    -    =======
    -
    -    A RandomSymbol.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.stats import Kumaraswamy, density, E, variance
    -    >>> from sympy import Symbol, simplify, pprint
    -
    -    >>> a = Symbol("a", positive=True)
    -    >>> b = Symbol("b", positive=True)
    -
    -    >>> X = Kumaraswamy("x", a, b)
    -
    -    >>> D = density(X)
    -    >>> pprint(D, use_unicode=False)
    -          /                        b - 1\
    -          |    a - 1     /   a    \     |
    -    Lambda\x, x     *a*b*\- x  + 1/     /
    -
    -    >>> simplify(E(X, meijerg=True))
    -    gamma(1 + 1/a)*gamma(b + 1)/gamma(b + 1 + 1/a)
    -
    -    References
    -    ==========
    -
    -    [1] http://en.wikipedia.org/wiki/Kumaraswamy_distribution
    -    """
    -
    -    return KumaraswamyPSpace(name, a, b).value
    -
    -#-------------------------------------------------------------------------------
    -# Laplace distribution ---------------------------------------------------------
    -
    -
    -class LaplacePSpace(SingleContinuousPSpace):
    -    def __new__(cls, name, mu, b):
    -        mu, b = sympify(mu), sympify(b)
    -        x = Symbol(name)
    -        pdf = 1/(2*b)*exp(-Abs(x - mu)/b)
    -        obj = SingleContinuousPSpace.__new__(cls, x, pdf)
    -        return obj
    -
    -
    -def Laplace(name, mu, b):
    -    r"""
    -    Create a continuous random variable with a Laplace distribution.
    -
    -    The density of the Laplace distribution is given by
    -
    -    .. math::
    -        f(x) := \frac{1}{2 b} \exp \left(-\frac{|x-\mu|}b \right)
    -
    -    Parameters
    -    ==========
    -
    -    mu : Real number, the location
    -    b : Real number, `b` > 0 a scale
    -
    -    Returns
    -    =======
    -
    -    A RandomSymbol.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.stats import Laplace, density
    -    >>> from sympy import Symbol
    -
    -    >>> mu = Symbol("mu")
    -    >>> b = Symbol("b", positive=True)
    -
    -    >>> X = Laplace("x", mu, b)
    -
    -    >>> density(X)
    -    Lambda(_x, exp(-Abs(_x - mu)/b)/(2*b))
    -
    -    References
    -    ==========
    -
    -    [1] http://en.wikipedia.org/wiki/Laplace_distribution
    -    [2] http://mathworld.wolfram.com/LaplaceDistribution.html
    -    """
    -
    -    return LaplacePSpace(name, mu, b).value
    -
    -#-------------------------------------------------------------------------------
    -# Logistic distribution --------------------------------------------------------
    -
    -
    -class LogisticPSpace(SingleContinuousPSpace):
    -    def __new__(cls, name, mu, s):
    -        mu, s = sympify(mu), sympify(s)
    -
    -        x = Symbol(name)
    -        pdf = exp(-(x - mu)/s)/(s*(1 + exp(-(x - mu)/s))**2)
    -
    -        obj = SingleContinuousPSpace.__new__(cls, x, pdf)
    -        return obj
    -
    -
    -def Logistic(name, mu, s):
    -    r"""
    -    Create a continuous random variable with a logistic distribution.
    -
    -    The density of the logistic distribution is given by
    -
    -    .. math::
    -        f(x) := \frac{e^{-(x-\mu)/s}} {s\left(1+e^{-(x-\mu)/s}\right)^2}
    -
    -    Parameters
    -    ==========
    -
    -    mu : Real number, the location
    -    s : Real number, `s` > 0 a scale
    -
    -    Returns
    -    =======
    -
    -    A RandomSymbol.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.stats import Logistic, density
    -    >>> from sympy import Symbol
    -
    -    >>> mu = Symbol("mu", real=True)
    -    >>> s = Symbol("s", positive=True)
    -
    -    >>> X = Logistic("x", mu, s)
    -
    -    >>> density(X)
    -    Lambda(_x, exp((-_x + mu)/s)/(s*(exp((-_x + mu)/s) + 1)**2))
    -
    -    References
    -    ==========
    -
    -    [1] http://en.wikipedia.org/wiki/Logistic_distribution
    -    [2] http://mathworld.wolfram.com/LogisticDistribution.html
    -    """
    -
    -    return LogisticPSpace(name, mu, s).value
    -
    -#-------------------------------------------------------------------------------
    -# Log Normal distribution ------------------------------------------------------
    -
    -
    -class LogNormalPSpace(SingleContinuousPSpace):
    -    def __new__(cls, name, mean, std):
    -        mean, std = sympify(mean), sympify(std)
    -
    -        x = Symbol(name)
    -        pdf = exp(-(log(x) - mean)**2 / (2*std**2)) / (x*sqrt(2*pi)*std)
    -
    -        obj = SingleContinuousPSpace.__new__(cls, x, pdf, set=Interval(0, oo))
    -        obj.mean = mean
    -        obj.std = std
    -        return obj
    -
    -    def sample(self):
    -        return {self.value: random.lognormvariate(self.mean, self.std)}
    -
    -
    -def LogNormal(name, mean, std):
    -    r"""
    -    Create a continuous random variable with a log-normal distribution.
    -
    -    The density of the log-normal distribution is given by
    -
    -    .. math::
    -        f(x) := \frac{1}{x\sqrt{2\pi\sigma^2}}
    -                e^{-\frac{\left(\ln x-\mu\right)^2}{2\sigma^2}}
    -
    -    with :math:`x \geq 0`.
    -
    -    Parameters
    -    ==========
    -
    -    mu : Real number, the log-scale
    -    sigma : Real number, :math:`\sigma^2 > 0` a shape
    -
    -    Returns
    -    =======
    -
    -    A RandomSymbol.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.stats import LogNormal, density
    -    >>> from sympy import Symbol, simplify, pprint
    -
    -    >>> mu = Symbol("mu", real=True)
    -    >>> sigma = Symbol("sigma", positive=True)
    -
    -    >>> X = LogNormal("x", mu, sigma)
    -
    -    >>> D = density(X)
    -    >>> pprint(D, use_unicode=False)
    -          /                         2\
    -          |          -(-mu + log(x)) |
    -          |          ----------------|
    -          |                     2    |
    -          |     ___      2*sigma     |
    -          |   \/ 2 *e                |
    -    Lambda|x, -----------------------|
    -          |             ____         |
    -          \       2*x*\/ pi *sigma   /
    -
    -    >>> X = LogNormal('x', 0, 1) # Mean 0, standard deviation 1
    -
    -    >>> density(X)
    -    Lambda(_x, sqrt(2)*exp(-log(_x)**2/2)/(2*_x*sqrt(pi)))
    -
    -    References
    -    ==========
    -
    -    [1] http://en.wikipedia.org/wiki/Lognormal
    -    [2] http://mathworld.wolfram.com/LogNormalDistribution.html
    -    """
    -
    -    return LogNormalPSpace(name, mean, std).value
    -
    -#-------------------------------------------------------------------------------
    -# Maxwell distribution ---------------------------------------------------------
    -
    -
    -class MaxwellPSpace(SingleContinuousPSpace):
    -    def __new__(cls, name, a):
    -        a = sympify(a)
    -
    -        x = Symbol(name)
    -
    -        pdf = sqrt(2/pi)*x**2*exp(-x**2/(2*a**2))/a**3
    -        obj = SingleContinuousPSpace.__new__(cls, x, pdf, set=Interval(0, oo))
    -        return obj
    -
    -
    -def Maxwell(name, a):
    -    r"""
    -    Create a continuous random variable with a Maxwell distribution.
    -
    -    The density of the Maxwell distribution is given by
    -
    -    .. math::
    -        f(x) := \sqrt{\frac{2}{\pi}} \frac{x^2 e^{-x^2/(2a^2)}}{a^3}
    -
    -    with :math:`x \geq 0`.
    -
    -    Parameters
    -    ==========
    -
    -    a : Real number, `a` > 0
    -
    -    Returns
    -    =======
    -
    -    A RandomSymbol.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.stats import Maxwell, density, E, variance
    -    >>> from sympy import Symbol, simplify
    -
    -    >>> a = Symbol("a", positive=True)
    -
    -    >>> X = Maxwell("x", a)
    -
    -    >>> density(X)
    -    Lambda(_x, sqrt(2)*_x**2*exp(-_x**2/(2*a**2))/(sqrt(pi)*a**3))
    -
    -    >>> E(X)
    -    2*sqrt(2)*a/sqrt(pi)
    -
    -    >>> simplify(variance(X))
    -    a**2*(-8 + 3*pi)/pi
    -
    -    References
    -    ==========
    -
    -    [1] http://en.wikipedia.org/wiki/Maxwell_distribution
    -    [2] http://mathworld.wolfram.com/MaxwellDistribution.html
    -    """
    -
    -    return MaxwellPSpace(name, a).value
    -
    -#-------------------------------------------------------------------------------
    -# Nakagami distribution --------------------------------------------------------
    -
    -
    -class NakagamiPSpace(SingleContinuousPSpace):
    -    def __new__(cls, name, mu, omega):
    -        mu, omega = sympify(mu), sympify(omega)
    -        x = Symbol(name)
    -        pdf = 2*mu**mu/(gamma(mu)*omega**mu)*x**(2*mu - 1)*exp(-mu/omega*x**2)
    -        obj = SingleContinuousPSpace.__new__(cls, x, pdf, set=Interval(0, oo))
    -        return obj
    -
    -
    -def Nakagami(name, mu, omega):
    -    r"""
    -    Create a continuous random variable with a Nakagami distribution.
    -
    -    The density of the Nakagami distribution is given by
    -
    -    .. math::
    -        f(x) := \frac{2\mu^\mu}{\Gamma(\mu)\omega^\mu} x^{2\mu-1}
    -                \exp\left(-\frac{\mu}{\omega}x^2 \right)
    -
    -    with :math:`x > 0`.
    -
    -    Parameters
    -    ==========
    -
    -    mu : Real number, :math:`mu \geq \frac{1}{2}` a shape
    -    omega : Real number, `omega` > 0 the spread
    -
    -    Returns
    -    =======
    -
    -    A RandomSymbol.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.stats import Nakagami, density, E, variance
    -    >>> from sympy import Symbol, simplify, pprint
    -
    -    >>> mu = Symbol("mu", positive=True)
    -    >>> omega = Symbol("omega", positive=True)
    -
    -    >>> X = Nakagami("x", mu, omega)
    -
    -    >>> D = density(X)
    -    >>> pprint(D, use_unicode=False)
    -          /                                2   \
    -          |                              -x *mu|
    -          |                              ------|
    -          |      2*mu - 1   mu      -mu  omega |
    -          |   2*x        *mu  *omega   *e      |
    -    Lambda|x, ---------------------------------|
    -          \               gamma(mu)            /
    -
    -    >>> simplify(E(X, meijerg=True))
    -    sqrt(mu)*sqrt(omega)*gamma(mu + 1/2)/gamma(mu + 1)
    -
    -    >>> V = simplify(variance(X, meijerg=True))
    -    >>> pprint(V, use_unicode=False)
    -          /                               2          \
    -    omega*\gamma(mu)*gamma(mu + 1) - gamma (mu + 1/2)/
    -    --------------------------------------------------
    -                 gamma(mu)*gamma(mu + 1)
    -
    -    References
    -    ==========
    -
    -    [1] http://en.wikipedia.org/wiki/Nakagami_distribution
    -    """
    -
    -    return NakagamiPSpace(name, mu, omega).value
    -
    -#-------------------------------------------------------------------------------
    -# Normal distribution ----------------------------------------------------------
    -
    -
    -
    [docs]class NormalPSpace(SingleContinuousPSpace): - def __new__(cls, name, mean, std): - mean, std = sympify(mean), sympify(std) - - _value_check(std > 0, "Standard deviation must be positive") - - x = Symbol(name) - pdf = exp(-(x - mean)**2 / (2*std**2)) / (sqrt(2*pi)*std) - - obj = SingleContinuousPSpace.__new__(cls, x, pdf) - obj.mean = mean - obj.std = std - obj.variance = std**2 - return obj - - def sample(self): - return {self.value: random.normalvariate(self.mean, self.std)} - -
    -def Normal(name, mean, std): - r""" - Create a continuous random variable with a Normal distribution. - - The density of the Normal distribution is given by - - .. math:: - f(x) := \frac{1}{\sigma\sqrt{2\pi}} e^{ -\frac{(x-\mu)^2}{2\sigma^2} } - - Parameters - ========== - - mu : Real number, the mean - sigma : Real number, :math:`\sigma^2 > 0` the variance - - Returns - ======= - - A RandomSymbol. - - Examples - ======== - - >>> from sympy.stats import Normal, density, E, std, cdf, skewness - >>> from sympy import Symbol, simplify, pprint, factor, together - - >>> mu = Symbol("mu") - >>> sigma = Symbol("sigma", positive=True) - - >>> X = Normal("x", mu, sigma) - - >>> density(X) - Lambda(_x, sqrt(2)*exp(-(_x - mu)**2/(2*sigma**2))/(2*sqrt(pi)*sigma)) - - >>> C = simplify(cdf(X)) # it needs a little more help... - >>> C = C.func(C.args[0], together(factor(C.args[1]), deep=True)) - >>> pprint(C, use_unicode=False) - / / ___ \ \ - | |\/ 2 *(z - mu)| | - | erf|--------------| | - | \ 2*sigma / 1| - Lambda|z, ------------------- + -| - \ 2 2/ - - >>> simplify(skewness(X)) - 0 - - >>> X = Normal("x", 0, 1) # Mean 0, standard deviation 1 - >>> density(X) - Lambda(_x, sqrt(2)*exp(-_x**2/2)/(2*sqrt(pi))) - - >>> E(2*X + 1) - 1 - - >>> simplify(std(2*X + 1)) - 2 - - References - ========== - - [1] http://en.wikipedia.org/wiki/Normal_distribution - [2] http://mathworld.wolfram.com/NormalDistributionFunction.html - """ - - return NormalPSpace(name, mean, std).value - -#------------------------------------------------------------------------------- -# Pareto distribution ---------------------------------------------------------- - - -class ParetoPSpace(SingleContinuousPSpace): - def __new__(cls, name, xm, alpha): - xm, alpha = sympify(xm), sympify(alpha) - - _value_check(xm > 0, "Xm must be positive") - _value_check(alpha > 0, "Alpha must be positive") - - x = Symbol(name) - pdf = alpha * xm**alpha / x**(alpha + 1) - - obj = SingleContinuousPSpace.__new__(cls, x, pdf, set=Interval(xm, oo)) - obj.xm = xm - obj.alpha = alpha - return obj - - def sample(self): - return {self.value: random.paretovariate(self.alpha)} - - -def Pareto(name, xm, alpha): - r""" - Create a continuous random variable with the Pareto distribution. - - The density of the Pareto distribution is given by - - .. math:: - f(x) := \frac{\alpha\,x_\mathrm{m}^\alpha}{x^{\alpha+1}} - - with :math:`x \in [x_m,\infty]`. - - Parameters - ========== - - xm : Real number, `xm` > 0 a scale - alpha : Real number, `alpha` > 0 a shape - - Returns - ======= - - A RandomSymbol. - - Examples - ======== - - >>> from sympy.stats import Pareto, density - >>> from sympy import Symbol - - >>> xm = Symbol("xm", positive=True) - >>> beta = Symbol("beta", positive=True) - - >>> X = Pareto("x", xm, beta) - - >>> density(X) - Lambda(_x, _x**(-beta - 1)*beta*xm**beta) - - References - ========== - - [1] http://en.wikipedia.org/wiki/Pareto_distribution - [2] http://mathworld.wolfram.com/ParetoDistribution.html - """ - - return ParetoPSpace(name, xm, alpha).value - -#------------------------------------------------------------------------------- -# QuadraticU distribution ------------------------------------------------------ - -class QuadraticUPSpace(SingleContinuousPSpace): - def __new__(cls, name, a, b): - a, b = sympify(a), sympify(b) - alpha = 12 / (b-a)**3 - beta = (a+b) / 2 - - x = Symbol(name) - pdf = Piecewise( - (alpha * (x-beta)**2, And(a<=x, x<=b)), - (S.Zero, True)) - - obj = SingleContinuousPSpace.__new__(cls, x, pdf, set=Interval(a, b)) - obj.a = a - obj.b = b - return obj - -def QuadraticU(name, a, b): - r""" - Create a Continuous Random Variable with a U-quadratic distribution. - - The density of the U-quadratic distribution is given by - - .. math:: - f(x) := \alpha (x-\beta)^2 - - with :math:`x \in [a,b]`. - - Parameters - ========== - - a : Real number - b : Real number, :math:`a < b` - - Returns - ======= - - A RandomSymbol. - - Examples - ======== - - >>> from sympy.stats import QuadraticU, density, E, variance - >>> from sympy import Symbol, simplify, factor, pprint - - >>> a = Symbol("a", real=True) - >>> b = Symbol("b", real=True) - - >>> X = QuadraticU("x", a, b) - - >>> D = density(X) - >>> pprint(D, use_unicode=False) - / / 2 \ - | | / a b\ | - | |12*|x - - - -| | - | | \ 2 2/ | - Lambda|x, <--------------- for And(x <= b, a <= x)| - | | 3 | - | | (-a + b) | - | | | - \ \ 0 otherwise / - - References - ========== - - [1] http://en.wikipedia.org/wiki/U-quadratic_distribution - """ - - return QuadraticUPSpace(name, a, b).value - -#------------------------------------------------------------------------------- -# RaisedCosine distribution ---------------------------------------------------- - -class RaisedCosinePSpace(SingleContinuousPSpace): - def __new__(cls, name, mu, s): - mu, s = sympify(mu), sympify(s) - - _value_check(s > 0, "s must be positive") - - x = Symbol(name) - pdf = Piecewise( - ((1+cos(pi*(x-mu)/s)) / (2*s), And(mu-s<=x, x<=mu+s)), - (S.Zero, True)) - - obj = SingleContinuousPSpace.__new__(cls, x, pdf, set=Interval(mu-s, mu+s)) - obj.mu = mu - obj.s = s - return obj - -def RaisedCosine(name, mu, s): - r""" - Create a Continuous Random Variable with a raised cosine distribution. - - The density of the raised cosine distribution is given by - - .. math:: - f(x) := \frac{1}{2s}\left(1+\cos\left(\frac{x-\mu}{s}\pi\right)\right) - - with :math:`x \in [\mu-s,\mu+s]`. - - Parameters - ========== - - mu : Real number - s : Real number, `s` > 0 - - Returns - ======= - - A RandomSymbol. - - Examples - ======== - - >>> from sympy.stats import RaisedCosine, density, E, variance - >>> from sympy import Symbol, simplify, pprint - - >>> mu = Symbol("mu", real=True) - >>> s = Symbol("s", positive=True) - - >>> X = RaisedCosine("x", mu, s) - - >>> D = density(X) - >>> pprint(D, use_unicode=False) - / / /pi*(x - mu)\ \ - | |cos|-----------| + 1 | - | | \ s / | - Lambda|x, <-------------------- for And(x <= mu + s, mu - s <= x)| - | | 2*s | - | | | - \ \ 0 otherwise / - - References - ========== - - [1] http://en.wikipedia.org/wiki/Raised_cosine_distribution - """ - - return RaisedCosinePSpace(name, mu, s).value - -#------------------------------------------------------------------------------- -# Rayleigh distribution -------------------------------------------------------- - - -class RayleighPSpace(SingleContinuousPSpace): - def __new__(cls, name, sigma): - sigma = sympify(sigma) - x = Symbol(name) - pdf = x/sigma**2*exp(-x**2/(2*sigma**2)) - obj = SingleContinuousPSpace.__new__(cls, x, pdf, set=Interval(0, oo)) - return obj - - -def Rayleigh(name, sigma): - r""" - Create a continuous random variable with a Rayleigh distribution. - - The density of the Rayleigh distribution is given by - - .. math :: - f(x) := \frac{x}{\sigma^2} e^{-x^2/2\sigma^2} - - with :math:`x > 0`. - - Parameters - ========== - - sigma : Real number, `sigma` > 0 - - Returns - ======= - - A RandomSymbol. - - Examples - ======== - - >>> from sympy.stats import Rayleigh, density, E, variance - >>> from sympy import Symbol, simplify - - >>> sigma = Symbol("sigma", positive=True) - - >>> X = Rayleigh("x", sigma) - - >>> density(X) - Lambda(_x, _x*exp(-_x**2/(2*sigma**2))/sigma**2) - - >>> E(X) - sqrt(2)*sqrt(pi)*sigma/2 - - >>> variance(X) - -pi*sigma**2/2 + 2*sigma**2 - - References - ========== - - [1] http://en.wikipedia.org/wiki/Rayleigh_distribution - [2] http://mathworld.wolfram.com/RayleighDistribution.html - """ - - return RayleighPSpace(name, sigma).value - -#------------------------------------------------------------------------------- -# StudentT distribution -------------------------------------------------------- - - -class StudentTPSpace(SingleContinuousPSpace): - def __new__(cls, name, nu): - nu = sympify(nu) - x = Symbol(name) - pdf = 1/(sqrt(nu)*beta_fn(S(1)/2, nu/2))*(1 + x**2/nu)**(-(nu + 1)/2) - obj = SingleContinuousPSpace.__new__(cls, x, pdf) - return obj - - -def StudentT(name, nu): - r""" - Create a continuous random variable with a student's t distribution. - - The density of the student's t distribution is given by - - .. math:: - f(x) := \frac{\Gamma \left(\frac{\nu+1}{2} \right)} - {\sqrt{\nu\pi}\Gamma \left(\frac{\nu}{2} \right)} - \left(1+\frac{x^2}{\nu} \right)^{-\frac{\nu+1}{2}} - - Parameters - ========== - - nu : Real number, `nu` > 0, the degrees of freedom - - Returns - ======= - - A RandomSymbol. - - Examples - ======== - - >>> from sympy.stats import StudentT, density, E, variance - >>> from sympy import Symbol, simplify, pprint - - >>> nu = Symbol("nu", positive=True) - - >>> X = StudentT("x", nu) - - >>> D = density(X) - >>> pprint(D, use_unicode=False) - / nu 1 \ - | - -- - - | - | 2 2 | - | / 2 \ | - | |x | /nu 1\| - | |-- + 1| *gamma|-- + -|| - | \nu / \2 2/| - Lambda|x, ------------------------------| - | ____ ____ /nu\ | - | \/ pi *\/ nu *gamma|--| | - \ \2 / / - - References - ========== - - [1] http://en.wikipedia.org/wiki/Student_t-distribution - [2] http://mathworld.wolfram.com/Studentst-Distribution.html - """ - - return StudentTPSpace(name, nu).value - -#------------------------------------------------------------------------------- -# Triangular distribution ------------------------------------------------------ - - -class TriangularPSpace(SingleContinuousPSpace): - def __new__(cls, name, a, b, c): - a, b, c = sympify(a), sympify(b), sympify(c) - - x = Symbol(name) - pdf = Piecewise( - (2*(x - a)/((b - a)*(c - a)), And(a <= x, x < c)), - (2/(b - a), Eq(x, c)), - (2*(b - x)/((b - a)*(b - c)), And(c < x, x <= b)), - (S.Zero, True)) - - obj = SingleContinuousPSpace.__new__(cls, x, pdf) - return obj - - -def Triangular(name, a, b, c): - r""" - Create a continuous random variable with a triangular distribution. - - The density of the triangular distribution is given by - - .. math:: - f(x) := \begin{cases} - 0 & \mathrm{for\ } x < a, \\ - \frac{2(x-a)}{(b-a)(c-a)} & \mathrm{for\ } a \le x < c, \\ - \frac{2}{b-a} & \mathrm{for\ } x = c, \\ - \frac{2(b-x)}{(b-a)(b-c)} & \mathrm{for\ } c < x \le b, \\ - 0 & \mathrm{for\ } b < x. - \end{cases} - - Parameters - ========== - - a : Real number, :math:`a \in \left(-\infty, \infty\right)` - b : Real number, :math:`a < b` - c : Real number, :math:`a \leq c \leq b` - - Returns - ======= - - A RandomSymbol. - - Examples - ======== - - >>> from sympy.stats import Triangular, density, E - >>> from sympy import Symbol - - >>> a = Symbol("a") - >>> b = Symbol("b") - >>> c = Symbol("c") - - >>> X = Triangular("x", a,b,c) - - >>> density(X) - Lambda(_x, Piecewise(((2*_x - 2*a)/((-a + b)*(-a + c)), - And(_x < c, a <= _x)), - (2/(-a + b), _x == c), - ((-2*_x + 2*b)/((-a + b)*(b - c)), - And(_x <= b, c < _x)), - (0, True))) - - References - ========== - - [1] http://en.wikipedia.org/wiki/Triangular_distribution - [2] http://mathworld.wolfram.com/TriangularDistribution.html - """ - - return TriangularPSpace(name, a, b, c).value - -#------------------------------------------------------------------------------- -# Uniform distribution --------------------------------------------------------- - - -class UniformPSpace(SingleContinuousPSpace): - def __new__(cls, name, left, right): - left, right = sympify(left), sympify(right) - - x = Symbol(name) - pdf = Piecewise( - (S.One/(right - left), And(left <= x, x <= right)), - (S.Zero, True)) - - obj = SingleContinuousPSpace.__new__(cls, x, pdf) - obj.left = left - obj.right = right - return obj - - def compute_cdf(self, expr, **kwargs): - from sympy import Lambda, Min - z = Dummy('z', real=True, bounded=True) - result = SingleContinuousPSpace.compute_cdf(self, expr, **kwargs) - result = result(z).subs({Min(z, self.right): z, - Min(z, self.left, self.right): self.left}) - return Lambda(z, result) - - def integrate(self, expr, rvs=None, **kwargs): - from sympy import Max, Min - result = SingleContinuousPSpace.integrate(self, expr, rvs, **kwargs) - result = result.subs({Max(self.left, self.right): self.right, - Min(self.left, self.right): self.left}) - return result - - def sample(self): - return {self.value: random.uniform(self.left, self.right)} - - -def Uniform(name, left, right): - r""" - Create a continuous random variable with a uniform distribution. - - The density of the uniform distribution is given by - - .. math:: - f(x) := \begin{cases} - \frac{1}{b - a} & \text{for } x \in [a,b] \\ - 0 & \text{otherwise} - \end{cases} - - with :math:`x \in [a,b]`. - - Parameters - ========== - - a : Real number, :math:`-\infty < a` the left boundary - b : Real number, :math:`a < b < \infty` the right boundary - - Returns - ======= - - A RandomSymbol. - - Examples - ======== - - >>> from sympy.stats import Uniform, density, cdf, E, variance, skewness - >>> from sympy import Symbol, simplify - - >>> a = Symbol("a") - >>> b = Symbol("b") - - >>> X = Uniform("x", a, b) - - >>> density(X) - Lambda(_x, Piecewise((1/(-a + b), And(_x <= b, a <= _x)), (0, True))) - - >>> cdf(X) - Lambda(_z, _z/(-a + b) - a/(-a + b)) - - >>> simplify(E(X)) - a/2 + b/2 - - >>> simplify(variance(X)) - a**2/12 - a*b/6 + b**2/12 - - >>> simplify(skewness(X)) - 0 - - References - ========== - - [1] http://en.wikipedia.org/wiki/Uniform_distribution_%28continuous%29 - [2] http://mathworld.wolfram.com/UniformDistribution.html - """ - - return UniformPSpace(name, left, right).value - -#------------------------------------------------------------------------------- -# UniformSum distribution ------------------------------------------------------ - - -class UniformSumPSpace(SingleContinuousPSpace): - def __new__(cls, name, n): - n = sympify(n) - - x = Symbol(name) - k = Dummy("k") - pdf = 1/factorial( - n - 1)*Sum((-1)**k*binomial(n, k)*(x - k)**(n - 1), (k, 0, floor(x))) - - obj = SingleContinuousPSpace.__new__(cls, x, pdf, set=Interval(0, n)) - return obj - - -def UniformSum(name, n): - r""" - Create a continuous random variable with an Irwin-Hall distribution. - - The probability distribution function depends on a single parameter - `n` which is an integer. - - The density of the Irwin-Hall distribution is given by - - .. math :: - f(x) := \frac{1}{(n-1)!}\sum_{k=0}^{\lfloor x\rfloor}(-1)^k - \binom{n}{k}(x-k)^{n-1} - - Parameters - ========== - - n : Integral number, `n` > 0 - - Returns - ======= - - A RandomSymbol. - - Examples - ======== - - >>> from sympy.stats import UniformSum, density - >>> from sympy import Symbol, pprint - - >>> n = Symbol("n", integer=True) - - >>> X = UniformSum("x", n) - - >>> D = density(X) - >>> pprint(D, use_unicode=False) - / floor(x) \ - | ___ | - | \ ` | - | \ k n - 1 /n\| - | ) (-1) *(-k + x) *| || - | / \k/| - | /__, | - | k = 0 | - Lambda|x, --------------------------------| - \ (n - 1)! / - - References - ========== - - [1] http://en.wikipedia.org/wiki/Uniform_sum_distribution - [2] http://mathworld.wolfram.com/UniformSumDistribution.html - """ - - return UniformSumPSpace(name, n).value - -#------------------------------------------------------------------------------- -# VonMises distribution -------------------------------------------------------- - -class VonMisesPSpace(SingleContinuousPSpace): - def __new__(cls, name, mu, k): - mu, k = sympify(mu), sympify(k) - - _value_check(k > 0, "k must be positive") - - x = Symbol(name) - pdf = exp(k*cos(x-mu)) / (2*pi*besseli(0, k)) - - obj = SingleContinuousPSpace.__new__(cls, x, pdf, set=Interval(0, 2*pi)) - obj.mu = mu - obj.k = k - return obj - -def VonMises(name, mu, k): - r""" - Create a Continuous Random Variable with a von Mises distribution. - - The density of the von Mises distribution is given by - - .. math:: - f(x) := \frac{e^{\kappa\cos(x-\mu)}}{2\pi I_0(\kappa)} - - with :math:`x \in [0,2\pi]`. - - Parameters - ========== - - mu : Real number, measure of location - k : Real number, measure of concentration - - Returns - ======= - - A RandomSymbol. - - Examples - ======== - - >>> from sympy.stats import VonMises, density, E, variance - >>> from sympy import Symbol, simplify, pprint - - >>> mu = Symbol("mu") - >>> k = Symbol("k", positive=True) - - >>> X = VonMises("x", mu, k) - - >>> D = density(X) - >>> pprint(D, use_unicode=False) - / k*cos(x - mu) \ - | e | - Lambda|x, ------------------| - \ 2*pi*besseli(0, k)/ - - References - ========== - - [1] http://en.wikipedia.org/wiki/Von_Mises_distribution - [2] http://mathworld.wolfram.com/vonMisesDistribution.html - """ - - return VonMisesPSpace(name, mu, k).value - -#------------------------------------------------------------------------------- -# Weibull distribution --------------------------------------------------------- - - -class WeibullPSpace(SingleContinuousPSpace): - def __new__(cls, name, alpha, beta): - alpha, beta = sympify(alpha), sympify(beta) - - _value_check(alpha > 0, "Alpha must be positive") - _value_check(beta > 0, "Beta must be positive") - - x = Symbol(name) - pdf = beta * (x/alpha)**(beta - 1) * exp(-(x/alpha)**beta) / alpha - - obj = SingleContinuousPSpace.__new__(cls, x, pdf, set=Interval(0, oo)) - obj.alpha = alpha - obj.beta = beta - return obj - - def sample(self): - return {self.value: random.weibullvariate(self.alpha, self.beta)} - - -def Weibull(name, alpha, beta): - r""" - Create a continuous random variable with a Weibull distribution. - - The density of the Weibull distribution is given by - - .. math:: - f(x) := \begin{cases} - \frac{k}{\lambda}\left(\frac{x}{\lambda}\right)^{k-1} - e^{-(x/\lambda)^{k}} & x\geq0\\ - 0 & x<0 - \end{cases} - - Parameters - ========== - - lambda : Real number, :math:`\lambda > 0` a scale - k : Real number, `k` > 0 a shape - - Returns - ======= - - A RandomSymbol. - - Examples - ======== - - >>> from sympy.stats import Weibull, density, E, variance - >>> from sympy import Symbol, simplify - - >>> l = Symbol("lambda", positive=True) - >>> k = Symbol("k", positive=True) - - >>> X = Weibull("x", l, k) - - >>> density(X) - Lambda(_x, k*(_x/lambda)**(k - 1)*exp(-(_x/lambda)**k)/lambda) - - >>> simplify(E(X)) - lambda*gamma(1 + 1/k) - - >>> simplify(variance(X)) - lambda**2*(-gamma(1 + 1/k)**2 + gamma(1 + 2/k)) - - References - ========== - - [1] http://en.wikipedia.org/wiki/Weibull_distribution - [2] http://mathworld.wolfram.com/WeibullDistribution.html - - """ - - return WeibullPSpace(name, alpha, beta).value - -#------------------------------------------------------------------------------- -# Wigner semicircle distribution ----------------------------------------------- - - -class WignerSemicirclePSpace(SingleContinuousPSpace): - def __new__(cls, name, R): - R = sympify(R) - - x = Symbol(name) - pdf = 2/(pi*R**2)*sqrt(R**2 - x**2) - - obj = SingleContinuousPSpace.__new__(cls, x, pdf, set=Interval(-R, R)) - return obj - - -def WignerSemicircle(name, R): - r""" - Create a continuous random variable with a Wigner semicircle distribution. - - The density of the Wigner semicircle distribution is given by - - .. math:: - f(x) := \frac2{\pi R^2}\,\sqrt{R^2-x^2} - - with :math:`x \in [-R,R]`. - - Parameters - ========== - - R : Real number, `R` > 0 the radius - - Returns - ======= - - A `RandomSymbol`. - - Examples - ======== - - >>> from sympy.stats import WignerSemicircle, density, E - >>> from sympy import Symbol, simplify - - >>> R = Symbol("R", positive=True) - - >>> X = WignerSemicircle("x", R) - - >>> density(X) - Lambda(_x, 2*sqrt(-_x**2 + R**2)/(pi*R**2)) - - >>> E(X) - 0 - - References - ========== - - [1] http://en.wikipedia.org/wiki/Wigner_semicircle_distribution - [2] http://mathworld.wolfram.com/WignersSemicircleLaw.html - """ - - return WignerSemicirclePSpace(name, R).value -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/stats/frv.html b/dev-py3k/_modules/sympy/stats/frv.html deleted file mode 100644 index 72b225af0f5..00000000000 --- a/dev-py3k/_modules/sympy/stats/frv.html +++ /dev/null @@ -1,408 +0,0 @@ - - - - - - - - - - sympy.stats.frv — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.stats.frv

    -"""
    -Finite Discrete Random Variables Module
    -
    -See Also
    -========
    -sympy.stats.frv_types
    -sympy.stats.rv
    -sympy.stats.crv
    -"""
    -
    -from sympy import (And, Eq, Basic, S, Expr, Symbol, cacheit, sympify, Mul, Add,
    -        And, Or, Tuple)
    -from sympy.core.sets import FiniteSet
    -from sympy.stats.rv import (RandomDomain, ProductDomain, ConditionalDomain,
    -        PSpace, ProductPSpace, SinglePSpace, random_symbols, sumsets, rv_subs)
    -from sympy.core.compatibility import product
    -from sympy.core.containers import Dict
    -import random
    -
    -
    -
    [docs]class FiniteDomain(RandomDomain): - """ - A domain with discrete finite support - - Represented using a FiniteSet. - """ - is_Finite = True - - def __new__(cls, elements): - elements = FiniteSet(*elements) - symbols = FiniteSet(sym for sym, val in elements) - return RandomDomain.__new__(cls, symbols, elements) - - @property - def elements(self): - return self.args[1] - - @property - def dict(self): - return FiniteSet(Dict(dict(el)) for el in self.elements) - - def __contains__(self, other): - return other in self.elements - - def __iter__(self): - return self.elements.__iter__() - - def as_boolean(self): - return Or(*[And(*[Eq(sym, val) for sym, val in item]) for item in self]) - -
    -class SingleFiniteDomain(FiniteDomain): - """ - A FiniteDomain over a single symbol/set - - Example: The possibilities of a *single* die roll. - """ - - def __new__(cls, symbol, set): - return RandomDomain.__new__(cls, (symbol, ), FiniteSet(*set)) - - @property - def symbol(self): - return tuple(self.symbols)[0] - - @property - def elements(self): - return FiniteSet(frozenset(((self.symbol, elem), )) for elem in self.set) - - @property - def set(self): - return self.args[1] - - def __iter__(self): - return (frozenset(((self.symbol, elem),)) for elem in self.set) - - def __contains__(self, other): - sym, val = tuple(other)[0] - return sym == self.symbol and val in self.set - - -class ProductFiniteDomain(ProductDomain, FiniteDomain): - """ - A Finite domain consisting of several other FiniteDomains - - Example: The possibilities of the rolls of three independent dice - """ - - def __iter__(self): - proditer = product(*self.domains) - return (sumsets(items) for items in proditer) - - @property - def elements(self): - return FiniteSet(iter(self)) - - -class ConditionalFiniteDomain(ConditionalDomain, ProductFiniteDomain): - """ - A FiniteDomain that has been restricted by a condition - - Example: The possibilities of a die roll under the condition that the - roll is even. - """ - - def __new__(cls, domain, condition): - cond = rv_subs(condition) - # Check that we aren't passed a condition like die1 == z - # where 'z' is a symbol that we don't know about - # We will never be able to test this equality through iteration - if not cond.free_symbols.issubset(domain.free_symbols): - raise ValueError('Condition "%s" contains foreign symbols \n%s.\n' % ( - condition, tuple(cond.free_symbols - domain.free_symbols)) + - "Will be unable to iterate using this condition") - - return ConditionalDomain.__new__(cls, domain, condition) - - def _test(self, elem): - val = self.condition.subs(dict(elem)) - if val in [True, False]: - return val - elif val.is_Equality: - return val.lhs == val.rhs - raise ValueError("Undeciable if %s" % str(val)) - - def __contains__(self, other): - return other in self.fulldomain and self._test(other) - - def __iter__(self): - return (elem for elem in self.fulldomain if self._test(elem)) - - @property - def set(self): - if self.fulldomain.__class__ is SingleFiniteDomain: - return FiniteSet(elem for elem in self.fulldomain.set - if frozenset(((self.fulldomain.symbol, elem),)) in self) - else: - raise NotImplementedError( - "Not implemented on multi-dimensional conditional domain") - #return FiniteSet(elem for elem in self.fulldomain if elem in self) - - def as_boolean(self): - return FiniteDomain.as_boolean(self) - -#============================================= -#========= Probability Space =============== -#============================================= - - -
    [docs]class FinitePSpace(PSpace): - """ - A Finite Probability Space - - Represents the probabilities of a finite number of events. - """ - - is_Finite = True - - def __new__(cls, domain, density): - density = dict((sympify(key), sympify(val)) - for key, val in list(density.items())) - public_density = Dict(density) - - obj = PSpace.__new__(cls, domain, public_density) - obj._density = density - return obj - - def prob_of(self, elem): - return self._density.get(elem, 0) - - def where(self, condition): - assert all(r.symbol in self.symbols for r in random_symbols(condition)) - return ConditionalFiniteDomain(self.domain, condition) - - def compute_density(self, expr): - expr = expr.subs(dict(((rs, rs.symbol) for rs in self.values))) - d = {} - for elem in self.domain: - val = expr.subs(dict(elem)) - prob = self.prob_of(elem) - d[val] = d.get(val, 0) + prob - return d - - @cacheit - def compute_cdf(self, expr): - d = self.compute_density(expr) - cum_prob = 0 - cdf = [] - for key in sorted(d): - prob = d[key] - cum_prob += prob - cdf.append((key, cum_prob)) - - return dict(cdf) - - @cacheit - def sorted_cdf(self, expr, python_float=False): - cdf = self.compute_cdf(expr) - items = list(cdf.items()) - sorted_items = sorted(items, key=lambda val_cum_prob: val_cum_prob[1]) - if python_float: - sorted_items = [(v, float(cum_prob)) - for v, cum_prob in sorted_items] - return sorted_items - - def integrate(self, expr, rvs=None): - rvs = rvs or self.values - expr = expr.subs(dict((rs, rs.symbol) for rs in rvs)) - return sum(expr.subs(dict(elem)) * self.prob_of(elem) - for elem in self.domain) - - def probability(self, condition): - cond_symbols = frozenset(rs.symbol for rs in random_symbols(condition)) - assert cond_symbols.issubset(self.symbols) - return sum(self.prob_of(elem) for elem in self.where(condition)) - - def conditional_space(self, condition): - domain = self.where(condition) - prob = self.probability(condition) - density = dict((key, val / prob) - for key, val in list(self._density.items()) if key in domain) - return FinitePSpace(domain, density) - - def sample(self): - """ - Internal sample method - - Returns dictionary mapping RandomSymbol to realization value. - """ - expr = Tuple(*self.values) - cdf = self.sorted_cdf(expr, python_float=True) - - x = random.uniform(0, 1) - # Find first occurence with cumulative probability less than x - # This should be replaced with binary search - for value, cum_prob in cdf: - if x < cum_prob: - # return dictionary mapping RandomSymbols to values - return dict(list(zip(expr, value))) - - assert False, "We should never have gotten to this point" - -
    -class SingleFinitePSpace(FinitePSpace, SinglePSpace): - """ - A single finite probability space - - Represents the probabilities of a set of random events that can be - attributed to a single variable/symbol. - - This class is implemented by many of the standard FiniteRV types such as - Die, Bernoulli, Coin, etc.... - """ - - @classmethod - def fromdict(cls, name, density): - symbol = Symbol(name) - domain = SingleFiniteDomain(symbol, frozenset(list(density.keys()))) - density = dict((frozenset(((symbol, val),)), prob) - for val, prob in list(density.items())) - density = Dict(density) - return FinitePSpace.__new__(cls, domain, density) - - -class ProductFinitePSpace(ProductPSpace, FinitePSpace): - """ - A collection of several independent finite probability spaces - """ - @property - def domain(self): - return ProductFiniteDomain(*[space.domain for space in self.spaces]) - - @property - @cacheit - def _density(self): - proditer = product(*[iter(space._density.items()) - for space in self.spaces]) - d = {} - for items in proditer: - elems, probs = list(zip(*items)) - elem = sumsets(elems) - prob = Mul(*probs) - d[elem] = d.get(elem, 0) + prob - return Dict(d) - - @property - @cacheit - def density(self): - return Dict(self._density) -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/stats/frv_types.html b/dev-py3k/_modules/sympy/stats/frv_types.html deleted file mode 100644 index 87c0340eb77..00000000000 --- a/dev-py3k/_modules/sympy/stats/frv_types.html +++ /dev/null @@ -1,441 +0,0 @@ - - - - - - - - - - sympy.stats.frv_types — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.stats.frv_types

    -"""
    -Finite Discrete Random Variables - Prebuilt variable types
    -
    -Contains
    -========
    -FiniteRV
    -DiscreteUniform
    -Die
    -Bernoulli
    -Coin
    -Binomial
    -Hypergeometric
    -"""
    -
    -from sympy.stats.frv import SingleFinitePSpace
    -from sympy import S, sympify, Rational, binomial
    -
    -__all__ = ['FiniteRV', 'DiscreteUniform', 'Die', 'Bernoulli', 'Coin',
    -        'Binomial', 'Hypergeometric']
    -
    -
    -def FiniteRV(name, density):
    -    """
    -    Create a Finite Random Variable given a dict representing the density.
    -
    -    Returns a RandomSymbol.
    -
    -    >>> from sympy.stats import FiniteRV, P, E
    -
    -    >>> density = {0: .1, 1: .2, 2: .3, 3: .4}
    -    >>> X = FiniteRV('X', density)
    -
    -    >>> E(X)
    -    2.00000000000000
    -    >>> P(X>=2)
    -    0.700000000000000
    -    """
    -    return SingleFinitePSpace.fromdict(name, density).value
    -
    -
    -class DiscreteUniformPSpace(SingleFinitePSpace):
    -    """
    -    Create a Finite Random Variable representing a discrete uniform
    -    distribution.
    -
    -    This class is for internal use.
    -
    -    Create DiscreteUniform Random Symbols using DiscreteUniform function
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.stats import DiscreteUniform, density
    -    >>> from sympy import symbols
    -
    -    >>> X = DiscreteUniform('X', symbols('a b c')) # equally likely over a, b, c
    -    >>> density(X)
    -    {a: 1/3, b: 1/3, c: 1/3}
    -
    -    >>> Y = DiscreteUniform('Y', list(range(5))) # distribution over a range
    -    >>> density(Y)
    -    {0: 1/5, 1: 1/5, 2: 1/5, 3: 1/5, 4: 1/5}
    -    """
    -    def __new__(cls, name, items):
    -        density = dict((sympify(item), Rational(1, len(items)))
    -                       for item in items)
    -        return cls.fromdict(name, density)
    -
    -
    -def DiscreteUniform(name, items):
    -    """
    -    Create a Finite Random Variable representing a uniform distribution over
    -    the input set.
    -
    -    Returns a RandomSymbol.
    -
    -    Examples
    -    ========
    -
    -    >>> from sympy.stats import DiscreteUniform, density
    -    >>> from sympy import symbols
    -
    -    >>> X = DiscreteUniform('X', symbols('a b c')) # equally likely over a, b, c
    -    >>> density(X)
    -    {a: 1/3, b: 1/3, c: 1/3}
    -
    -    >>> Y = DiscreteUniform('Y', list(range(5))) # distribution over a range
    -    >>> density(Y)
    -    {0: 1/5, 1: 1/5, 2: 1/5, 3: 1/5, 4: 1/5}
    -
    -    """
    -    return DiscreteUniformPSpace(name, items).value
    -
    -
    -
    [docs]class DiePSpace(DiscreteUniformPSpace): - """ - Create a Finite Random Variable representing a fair die. - - This class is for internal use. - - Create Dice Random Symbols using Die function - - >>> from sympy.stats import Die, density - - >>> D6 = Die('D6', 6) # Six sided Die - >>> density(D6) - {1: 1/6, 2: 1/6, 3: 1/6, 4: 1/6, 5: 1/6, 6: 1/6} - - >>> D4 = Die('D4', 4) # Four sided Die - >>> density(D4) - {1: 1/4, 2: 1/4, 3: 1/4, 4: 1/4} - """ - def __new__(cls, name, sides): - return DiscreteUniformPSpace.__new__(cls, name, list(range(1, sides + 1))) - -
    -def Die(name, sides=6): - """ - Create a Finite Random Variable representing a fair die. - - Returns a RandomSymbol. - - >>> from sympy.stats import Die, density - - >>> D6 = Die('D6', 6) # Six sided Die - >>> density(D6) - {1: 1/6, 2: 1/6, 3: 1/6, 4: 1/6, 5: 1/6, 6: 1/6} - - >>> D4 = Die('D4', 4) # Four sided Die - >>> density(D4) - {1: 1/4, 2: 1/4, 3: 1/4, 4: 1/4} - """ - - return DiePSpace(name, sides).value - - -class BernoulliPSpace(SingleFinitePSpace): - """ - Create a Finite Random Variable representing a Bernoulli process. - - Returns a RandomSymbol. - - This class is for internal use. - - Create Bernoulli Random Symbols using Bernoulli function. - - >>> from sympy.stats import Bernoulli, density - >>> from sympy import S - - >>> X = Bernoulli('X', S(3)/4) # 1-0 Bernoulli variable, probability = 3/4 - >>> density(X) - {0: 1/4, 1: 3/4} - - >>> X = Bernoulli('X', S.Half, 'Heads', 'Tails') # A fair coin toss - >>> density(X) - {Heads: 1/2, Tails: 1/2} - """ - - def __new__(cls, name, p, succ, fail): - succ, fail, p = list(map(sympify, (succ, fail, p))) - density = {succ: p, fail: (1 - p)} - return cls.fromdict(name, density) - - -def Bernoulli(name, p, succ=1, fail=0): - """ - Create a Finite Random Variable representing a Bernoulli process. - - Returns a RandomSymbol - - >>> from sympy.stats import Bernoulli, density - >>> from sympy import S - - >>> X = Bernoulli('X', S(3)/4) # 1-0 Bernoulli variable, probability = 3/4 - >>> density(X) - {0: 1/4, 1: 3/4} - - >>> X = Bernoulli('X', S.Half, 'Heads', 'Tails') # A fair coin toss - >>> density(X) - {Heads: 1/2, Tails: 1/2} - """ - - return BernoulliPSpace(name, p, succ, fail).value - - -class CoinPSpace(BernoulliPSpace): - """ - A probability space representing a coin toss. - - Probability p is the chance of gettings "Heads." Half by default - - This class is for internal use. - - Create Coin's using Coin function - - >>> from sympy.stats import Coin, density - >>> from sympy import Rational - - >>> C = Coin('C') # A fair coin toss - >>> density(C) - {H: 1/2, T: 1/2} - - >>> C2 = Coin('C2', Rational(3, 5)) # An unfair coin - >>> density(C2) - {H: 3/5, T: 2/5} - """ - def __new__(cls, name, p): - return BernoulliPSpace.__new__(cls, name, p, 'H', 'T') - - -def Coin(name, p=S.Half): - """ - Create a Finite Random Variable representing a Coin toss. - - Probability p is the chance of gettings "Heads." Half by default - - Returns a RandomSymbol. - - >>> from sympy.stats import Coin, density - >>> from sympy import Rational - - >>> C = Coin('C') # A fair coin toss - >>> density(C) - {H: 1/2, T: 1/2} - - >>> C2 = Coin('C2', Rational(3, 5)) # An unfair coin - >>> density(C2) - {H: 3/5, T: 2/5} - """ - return CoinPSpace(name, p).value - - -class BinomialPSpace(SingleFinitePSpace): - """ - Create a Finite Random Variable representing a binomial distribution. - - This class is for internal use. - - Create Binomial Random Symbols using Binomial function. - - Examples - ======== - - >>> from sympy.stats import Binomial, density - >>> from sympy import S - - >>> X = Binomial('X', 4, S.Half) # Four "coin flips" - >>> density(X) - {0: 1/16, 1: 1/4, 2: 3/8, 3: 1/4, 4: 1/16} - """ - - def __new__(cls, name, n, p, succ, fail): - n, p, succ, fail = list(map(sympify, (n, p, succ, fail))) - density = dict((k*succ + (n - k)*fail, - binomial(n, k) * p**k * (1 - p)**(n - k)) for k in range(0, n + 1)) - return cls.fromdict(name, density) - - -def Binomial(name, n, p, succ=1, fail=0): - """ - Create a Finite Random Variable representing a binomial distribution. - - Returns a RandomSymbol. - - Examples - ======== - - >>> from sympy.stats import Binomial, density - >>> from sympy import S - - >>> X = Binomial('X', 4, S.Half) # Four "coin flips" - >>> density(X) - {0: 1/16, 1: 1/4, 2: 3/8, 3: 1/4, 4: 1/16} - """ - - return BinomialPSpace(name, n, p, succ, fail).value - - -class HypergeometricPSpace(SingleFinitePSpace): - """ - Create a Finite Random Variable representing a hypergeometric distribution. - - This class is for internal use. - - Create Hypergeometric Random Symbols using Hypergeometric function. - - Examples - ======== - - >>> from sympy.stats import Hypergeometric, density - >>> from sympy import S - - >>> X = Hypergeometric('X', 10, 5, 3) # 10 marbles, 5 white (success), 3 draws - >>> density(X) - {0: 1/12, 1: 5/12, 2: 5/12, 3: 1/12} - """ - - def __new__(cls, name, N, m, n): - N, m, n = list(map(sympify, (N, m, n))) - density = dict((k, binomial(m, k) * binomial(N - m, n - k) / binomial(N, n)) - for k in range(max(0, n + m - N), min(m, n) + 1)) - return cls.fromdict(name, density) - - -def Hypergeometric(name, N, m, n): - """ - Create a Finite Random Variable representing a hypergeometric distribution. - - Returns a RandomSymbol. - - Examples - ======== - - >>> from sympy.stats import Hypergeometric, density - >>> from sympy import S - - >>> X = Hypergeometric('X', 10, 5, 3) # 10 marbles, 5 white (success), 3 draws - >>> density(X) - {0: 1/12, 1: 5/12, 2: 5/12, 3: 1/12} - """ - - return HypergeometricPSpace(name, N, m, n).value -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/stats/rv.html b/dev-py3k/_modules/sympy/stats/rv.html deleted file mode 100644 index 20234fb830c..00000000000 --- a/dev-py3k/_modules/sympy/stats/rv.html +++ /dev/null @@ -1,1012 +0,0 @@ - - - - - - - - - - sympy.stats.rv — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.stats.rv

    -"""
    -Main Random Variables Module
    -
    -Defines abstract random variable type.
    -Contains interfaces for probability space object (PSpace) as well as standard
    -operators, P, E, sample, density, where
    -
    -See Also
    -========
    -sympy.stats.crv
    -sympy.stats.frv
    -sympy.stats.rv_interface
    -"""
    -
    -from sympy import Basic, S, Expr, Symbol, Tuple, And, Add, Eq, lambdify
    -from sympy.core.sets import FiniteSet, ProductSet
    -from functools import reduce
    -
    -
    -
    [docs]class RandomDomain(Basic): - """ - Represents a set of variables and the values which they can take - - See Also - ======== - sympy.stats.crv.ContinuousDomain - sympy.stats.frv.FiniteDomain - """ - - is_ProductDomain = False - is_Finite = False - is_Continuous = False - - def __new__(cls, symbols, *args): - symbols = FiniteSet(*symbols) - return Basic.__new__(cls, symbols, *args) - - @property - def symbols(self): - return self.args[0] - - @property - def set(self): - return self.args[1] - - def __contains__(self, other): - raise NotImplementedError() - - def integrate(self, expr): - raise NotImplementedError() - -
    -
    [docs]class SingleDomain(RandomDomain): - """ - A single variable and its domain - - See Also - ======== - sympy.stats.crv.SingleContinuousDomain - sympy.stats.frv.SingleFiniteDomain - """ - def __new__(cls, symbol, set): - assert symbol.is_Symbol - symbols = FiniteSet(symbol) - return RandomDomain.__new__(cls, symbols, set) - - @property - def symbol(self): - return tuple(self.symbols)[0] - - def __contains__(self, other): - if len(other) != 1: - return False - sym, val = tuple(other)[0] - return self.symbol == sym and val in self.set - -
    -
    [docs]class ConditionalDomain(RandomDomain): - """ - A RandomDomain with an attached condition - - See Also - ======== - sympy.stats.crv.ConditionalContinuousDomain - sympy.stats.frv.ConditionalFiniteDomain - """ - def __new__(cls, fulldomain, condition): - condition = condition.subs(dict((rs, rs.symbol) - for rs in random_symbols(condition))) - return RandomDomain.__new__( - cls, fulldomain.symbols, fulldomain, condition) - - @property - def fulldomain(self): - return self.args[1] - - @property - def condition(self): - return self.args[2] - - @property - def set(self): - raise NotImplementedError("Set of Conditional Domain not Implemented") - - def as_boolean(self): - return And(self.fulldomain.as_boolean(), self.condition) - -
    -
    [docs]class PSpace(Basic): - """ - A Probability Space - - Probability Spaces encode processes that equal different values - probabalistically. These underly Random Symbols which occur in SymPy - expressions and contain the mechanics to evaluate statistical statements. - - See Also - ======== - sympy.stats.crv.ContinuousPSpace - sympy.stats.frv.FinitePSpace - """ - - is_Finite = None - is_Continuous = None - - @property - def domain(self): - return self.args[0] - - @property - def density(self): - return self.args[1] - - @property - def values(self): - return frozenset(RandomSymbol(self, sym) for sym in self.domain.symbols) - - @property - def symbols(self): - return self.domain.symbols - - def where(self, condition): - raise NotImplementedError() - - def compute_density(self, expr): - raise NotImplementedError() - - def sample(self): - raise NotImplementedError() - - def probability(self, condition): - raise NotImplementedError() - - def integrate(self, expr): - raise NotImplementedError() - -
    -
    [docs]class SinglePSpace(PSpace): - """ - Represents the probabilities of a set of random events that can be - attributed to a single variable/symbol. - """ - - @property - def value(self): - return tuple(self.values)[0] - -
    -
    [docs]class RandomSymbol(Symbol): - """ - Random Symbols represent ProbabilitySpaces in SymPy Expressions - In principle they can take on any value that their symbol can take on - within the associated PSpace with probability determined by the PSpace - Density. - - Random Symbols contain pspace and symbol properties. - The pspace property points to the represented Probability Space - The symbol is a standard SymPy Symbol that is used in that probability space - for example in defining a density. - - You can form normal SymPy expressions using RandomSymbols and operate on - those expressions with the Functions - - E - Expectation of a random expression - P - Probability of a condition - density - Probability Density of an expression - given - A new random expression (with new random symbols) given a condition - - An object of the RandomSymbol type should almost never be created by the - user. They tend to be created instead by the PSpace class's value method. - Traditionally a user doesn't even do this but instead calls one of the - convenience functions Normal, Exponential, Coin, Die, FiniteRV, etc.... - """ - - is_bounded = True - is_finite = True - - def __new__(cls, *args): - obj = Basic.__new__(cls) - obj.pspace = args[0] - obj.symbol = args[1] - return obj - - @property - def name(self): - return self.symbol.name - - @property - def is_commutative(self): - return self.symbol.is_commutative - - def _hashable_content(self): - return self.pspace, self.symbol - -
    -
    [docs]class ProductPSpace(PSpace): - """ - A probability space resulting from the merger of two independent probability - spaces. - - Often created using the function, pspace - """ - - def __new__(cls, *spaces): - rs_space_dict = {} - for space in spaces: - for value in space.values: - rs_space_dict[value] = space - - symbols = FiniteSet(val.symbol for val in list(rs_space_dict.keys())) - - # Overlapping symbols - if len(symbols) < sum(len(space.symbols) for space in spaces): - raise ValueError("Overlapping Random Variables") - - if all(space.is_Finite for space in spaces): - from sympy.stats.frv import ProductFinitePSpace - cls = ProductFinitePSpace - if all(space.is_Continuous for space in spaces): - from sympy.stats.crv import ProductContinuousPSpace - cls = ProductContinuousPSpace - - obj = Basic.__new__(cls, symbols, FiniteSet(*spaces)) - obj.rs_space_dict = rs_space_dict - - return obj - - @property - def spaces(self): - return self.args[1] - - @property - def values(self): - return sumsets(space.values for space in self.spaces) - - def integrate(self, expr, rvs=None, **kwargs): - rvs = rvs or self.values - rvs = frozenset(rvs) - for space in self.spaces: - expr = space.integrate(expr, rvs & space.values, **kwargs) - return expr - - @property - def domain(self): - return ProductDomain(*[space.domain for space in self.spaces]) - - @property - def density(self): - raise NotImplementedError("Density not available for ProductSpaces") - - def sample(self): - return dict([(k, v) for space in self.spaces - for k, v in list(space.sample().items())]) - -
    -
    [docs]class ProductDomain(RandomDomain): - """ - A domain resulting from the merger of two independent domains - - See Also - ======== - sympy.stats.crv.ProductContinuousDomain - sympy.stats.frv.ProductFiniteDomain - """ - is_ProductDomain = True - - def __new__(cls, *domains): - symbols = sumsets([domain.symbols for domain in domains]) - - # Flatten any product of products - domains2 = [] - for domain in domains: - if not domain.is_ProductDomain: - domains2.append(domain) - else: - domains2.extend(domain.domains) - domains2 = FiniteSet(domains2) - - sym_domain_dict = {} - for domain in domains2: - for symbol in domain.symbols: - sym_domain_dict[symbol] = domain - - if all(domain.is_Finite for domain in domains2): - from sympy.stats.frv import ProductFiniteDomain - cls = ProductFiniteDomain - if all(domain.is_Continuous for domain in domains2): - from sympy.stats.crv import ProductContinuousDomain - cls = ProductContinuousDomain - - obj = RandomDomain.__new__(cls, symbols, domains2) - obj.sym_domain_dict = sym_domain_dict - return obj - - @property - def domains(self): - return self.args[1] - - @property - def set(self): - return ProductSet(domain.set for domain in self.domains) - - def __contains__(self, other): - # Split event into each subdomain - for domain in self.domains: - # Collect the parts of this event which associate to this domain - elem = frozenset([item for item in other - if item[0] in domain.symbols]) - # Test this sub-event - if elem not in domain: - return False - # All subevents passed - return True - - def as_boolean(self): - return And(*[domain.as_boolean() for domain in self.domains]) - -
    -
    [docs]def random_symbols(expr): - """ - Returns all RandomSymbols within a SymPy Expression. - """ - try: - return list(expr.atoms(RandomSymbol)) - except AttributeError: - return [] - -
    -
    [docs]def pspace(expr): - """ - Returns the underlying Probability Space of a random expression. - - For internal use. - - Examples - ======== - - >>> from sympy.stats import pspace, Normal - >>> from sympy.stats.rv import ProductPSpace - >>> X = Normal('X', 0, 1) - >>> pspace(2*X + 1) == X.pspace - True - """ - - rvs = random_symbols(expr) - if not rvs: - return None - # If only one space present - if all(rv.pspace == rvs[0].pspace for rv in rvs): - return rvs[0].pspace - # Otherwise make a product space - return ProductPSpace(*[rv.pspace for rv in rvs]) - -
    -def sumsets(sets): - """ - Union of sets - """ - return reduce(frozenset.union, sets, frozenset()) - - -
    [docs]def rs_swap(a, b): - """ - Build a dictionary to swap RandomSymbols based on their underlying symbol. - - i.e. - if X = ('x', pspace1) - and Y = ('x', pspace2) - then X and Y match and the key, value pair - {X:Y} will appear in the result - - Inputs: collections a and b of random variables which share common symbols - Output: dict mapping RVs in a to RVs in b - """ - d = {} - for rsa in a: - d[rsa] = [rsb for rsb in b if rsa.symbol == rsb.symbol][0] - return d - -
    -def given(expr, condition=None, **kwargs): - """ - From a random expression and a condition on that expression creates a new - probability space from the condition and returns the same expression on that - conditional probability space. - - Examples - ======== - - >>> from sympy.stats import given, density, Die - >>> X = Die('X', 6) - >>> Y = given(X, X>3) - >>> density(Y) - {4: 1/3, 5: 1/3, 6: 1/3} - """ - - if not random_symbols(condition) or pspace_independent(expr, condition): - return expr - - # Get full probability space of both the expression and the condition - fullspace = pspace(Tuple(expr, condition)) - # Build new space given the condition - space = fullspace.conditional_space(condition, **kwargs) - # Dictionary to swap out RandomSymbols in expr with new RandomSymbols - # That point to the new conditional space - swapdict = rs_swap(fullspace.values, space.values) - # Swap random variables in the expression - expr = expr.subs(swapdict) - return expr - - -def expectation(expr, condition=None, numsamples=None, **kwargs): - """ - Returns the expected value of a random expression - - Parameters - ---------- - expr : Expr containing RandomSymbols - The expression of which you want to compute the expectation value - given : Expr containing RandomSymbols - A conditional expression. E(X, X>0) is expectation of X given X > 0 - numsamples : int - Enables sampling and approximates the expectation with this many samples - evalf : Bool (defaults to True) - If sampling return a number rather than a complex expression - evaluate : Bool (defaults to True) - In case of continuous systems return unevaluated integral - - Examples - ======== - - >>> from sympy.stats import E, Die - >>> X = Die('X', 6) - >>> E(X) - 7/2 - >>> E(2*X + 1) - 8 - - >>> E(X, X>3) # Expectation of X given that it is above 3 - 5 - """ - - if not random_symbols(expr): # expr isn't random? - return expr - if numsamples: # Computing by monte carlo sampling? - return sampling_E(expr, condition, numsamples=numsamples, **kwargs) - - # Create new expr and recompute E - if condition is not None: # If there is a condition - return expectation(given(expr, condition, **kwargs), **kwargs) - - # A few known statements for efficiency - - if expr.is_Add: # We know that E is Linear - return Add(*[expectation(arg, **kwargs) for arg in expr.args]) - - # Otherwise case is simple, pass work off to the ProbabilitySpace - return pspace(expr).integrate(expr, **kwargs) - - -def probability(condition, given_condition=None, numsamples=None, **kwargs): - """ - Probability that a condition is true, optionally given a second condition - - Parameters - ---------- - expr : Relational containing RandomSymbols - The condition of which you want to compute the probability - given_condition : Relational containing RandomSymbols - A conditional expression. P(X>1, X>0) is expectation of X>1 given X>0 - numsamples : int - Enables sampling and approximates the probability with this many samples - evalf : Bool (defaults to True) - If sampling return a number rather than a complex expression - evaluate : Bool (defaults to True) - In case of continuous systems return unevaluated integral - - Examples - ======== - - >>> from sympy.stats import P, Die - >>> from sympy import Eq - >>> X, Y = Die('X', 6), Die('Y', 6) - >>> P(X>3) - 1/2 - >>> P(Eq(X, 5), X>2) # Probability that X == 5 given that X > 2 - 1/4 - >>> P(X>Y) - 5/12 - """ - - if numsamples: - return sampling_P(condition, given_condition, numsamples=numsamples, - **kwargs) - if given_condition is not None: # If there is a condition - # Recompute on new conditional expr - return probability(given(condition, given_condition, **kwargs), **kwargs) - - # Otherwise pass work off to the ProbabilitySpace - return pspace(condition).probability(condition, **kwargs) - - -def density(expr, condition=None, **kwargs): - """ - Probability density of a random expression - - Optionally given a second condition - - This density will take on different forms for different types of - probability spaces. - Discrete variables produce Dicts. - Continuous variables produce Lambdas. - - Examples - ======== - - >>> from sympy.stats import density, Die, Normal - >>> from sympy import Symbol - - >>> D = Die('D', 6) - >>> X = Normal('x', 0, 1) - - >>> density(D) - {1: 1/6, 2: 1/6, 3: 1/6, 4: 1/6, 5: 1/6, 6: 1/6} - >>> density(2*D) - {2: 1/6, 4: 1/6, 6: 1/6, 8: 1/6, 10: 1/6, 12: 1/6} - >>> density(X) - Lambda(_x, sqrt(2)*exp(-_x**2/2)/(2*sqrt(pi))) - """ - if condition is not None: # If there is a condition - # Recompute on new conditional expr - return density(given(expr, condition, **kwargs), **kwargs) - - # Otherwise pass work off to the ProbabilitySpace - return pspace(expr).compute_density(expr, **kwargs) - - -def cdf(expr, condition=None, **kwargs): - """ - Cumulative Distribution Function of a random expression. - - optionally given a second condition - - This density will take on different forms for different types of - probability spaces. - Discrete variables produce Dicts. - Continuous variables produce Lambdas. - - Examples - ======== - - >>> from sympy.stats import density, Die, Normal, cdf - >>> from sympy import Symbol - - >>> D = Die('D', 6) - >>> X = Normal('X', 0, 1) - - >>> density(D) - {1: 1/6, 2: 1/6, 3: 1/6, 4: 1/6, 5: 1/6, 6: 1/6} - >>> cdf(D) - {1: 1/6, 2: 1/3, 3: 1/2, 4: 2/3, 5: 5/6, 6: 1} - >>> cdf(3*D, D>2) - {9: 1/4, 12: 1/2, 15: 3/4, 18: 1} - - >>> cdf(X) - Lambda(_z, erf(sqrt(2)*_z/2)/2 + 1/2) - """ - if condition is not None: # If there is a condition - # Recompute on new conditional expr - return cdf(given(expr, condition, **kwargs), **kwargs) - - # Otherwise pass work off to the ProbabilitySpace - return pspace(expr).compute_cdf(expr, **kwargs) - - -def where(condition, given_condition=None, **kwargs): - """ - Returns the domain where a condition is True. - - Examples - ======== - - >>> from sympy.stats import where, Die, Normal - >>> from sympy import symbols, And - - >>> D1, D2 = Die('a', 6), Die('b', 6) - >>> a, b = D1.symbol, D2.symbol - >>> X = Normal('x', 0, 1) - - >>> where(X**2<1) - Domain: And(-1 < x, x < 1) - - >>> where(X**2<1).set - (-1, 1) - - >>> where(And(D1<=D2 , D2<3)) - Domain: Or(And(a == 1, b == 1), And(a == 1, b == 2), And(a == 2, b == 2)) - """ - if given_condition is not None: # If there is a condition - # Recompute on new conditional expr - return where(given(condition, given_condition, **kwargs), **kwargs) - - # Otherwise pass work off to the ProbabilitySpace - return pspace(condition).where(condition, **kwargs) - - -def sample(expr, condition=None, **kwargs): - """ - A realization of the random expression - - Examples - ======== - - >>> from sympy.stats import Die, sample - >>> X, Y, Z = Die('X', 6), Die('Y', 6), Die('Z', 6) - - >>> die_roll = sample(X+Y+Z) # A random realization of three dice - """ - return next(sample_iter(expr, condition, numsamples=1)) - - -def sample_iter(expr, condition=None, numsamples=S.Infinity, **kwargs): - """ - Returns an iterator of realizations from the expression given a condition - - expr: Random expression to be realized - condition: A conditional expression (optional) - numsamples: Length of the iterator (defaults to infinity) - - Examples - -------- - >>> from sympy.stats import Normal, sample_iter - >>> X = Normal('X', 0, 1) - >>> expr = X*X + 3 - >>> iterator = sample_iter(expr, numsamples=3) - >>> list(iterator) # doctest: +SKIP - [12, 4, 7] - - See Also - ======== - Sample - sampling_P - sampling_E - sample_iter_lambdify - sample_iter_subs - """ - # lambdify is much faster but not as robust - try: - return sample_iter_lambdify(expr, condition, numsamples, **kwargs) - # use subs when lambdify fails - except TypeError: - return sample_iter_subs(expr, condition, numsamples, **kwargs) - - -def sample_iter_lambdify(expr, condition=None, numsamples=S.Infinity, **kwargs): - """ - See sample_iter - - Uses lambdify for computation. This is fast but does not always work. - """ - if condition: - ps = pspace(Tuple(expr, condition)) - else: - ps = pspace(expr) - - rvs = list(ps.values) - fn = lambdify(rvs, expr, **kwargs) - if condition: - given_fn = lambdify(rvs, condition, **kwargs) - - # Check that lambdify can handle the expression - # Some operations like Sum can prove difficult - try: - d = ps.sample() # a dictionary that maps RVs to values - args = [d[rv] for rv in rvs] - fn(*args) - if condition: - given_fn(*args) - except: - raise TypeError("Expr/condition too complex for lambdify") - - def return_generator(): - count = 0 - while count < numsamples: - d = ps.sample() # a dictionary that maps RVs to values - args = [d[rv] for rv in rvs] - - if condition: # Check that these values satisfy the condition - gd = given_fn(*args) - if not isinstance(gd, bool): - raise ValueError( - "Conditions must not contain free symbols") - if gd is False: # If the values don't satisfy then try again - continue - - yield fn(*args) - count += 1 - return return_generator() - - -def sample_iter_subs(expr, condition=None, numsamples=S.Infinity, **kwargs): - """ - See sample_iter - - Uses subs for computation. This is slow but almost always works. - """ - if condition is not None: - ps = pspace(Tuple(expr, condition)) - else: - ps = pspace(expr) - - count = 0 - - while count < numsamples: - d = ps.sample() # a dictionary that maps RVs to values - - if condition is not None: # Check that these values satisfy the condition - gd = condition.subs(d) - if not isinstance(gd, bool): - raise ValueError("Conditions must not contain free symbols") - if gd is False: # If the values don't satisfy then try again - continue - - yield expr.subs(d) - - count += 1 - - -def sampling_P(condition, given_condition=None, numsamples=1, - evalf=True, **kwargs): - """ - Sampling version of P - - See Also - ======== - P - sampling_E - """ - - count_true = 0 - count_false = 0 - - samples = sample_iter(condition, given_condition, - numsamples=numsamples, **kwargs) - - for x in samples: - if not isinstance(x, bool): - raise ValueError("Conditions must not contain free symbols") - - if x is True: - count_true += 1 - else: - count_false += 1 - - result = S(count_true) / numsamples - if evalf: - return result.evalf() - else: - return result - - -def sampling_E(condition, given_condition=None, numsamples=1, - evalf=True, **kwargs): - """ - Sampling version of E - - See Also - ======== - P - sampling_P - """ - - samples = sample_iter(condition, given_condition, - numsamples=numsamples, **kwargs) - - result = Add(*list(samples)) / numsamples - if evalf: - return result.evalf() - else: - return result - - -def dependent(a, b): - """ - Dependence of two random expressions - - Two expressions are independent if knowledge of one does not change - computations on the other. - - Examples - ======== - - >>> from sympy.stats import Normal, dependent, given - >>> from sympy import Tuple, Eq - - >>> X, Y = Normal('X', 0, 1), Normal('Y', 0, 1) - >>> dependent(X, Y) - False - >>> dependent(2*X + Y, -Y) - True - >>> X, Y = given(Tuple(X, Y), Eq(X+Y,3)) - >>> dependent(X, Y) - True - - See Also - ======== - independent - """ - if pspace_independent(a, b): - return False - - z = Symbol('z', real=True) - # Dependent if density is unchanged when one is given information about - # the other - return (density(a, Eq(b, z)) != density(a) or - density(b, Eq(a, z)) != density(b)) - - -def independent(a, b): - """ - Independence of two random expressions - - Two expressions are independent if knowledge of one does not change - computations on the other. - - Examples - ======== - - >>> from sympy.stats import Normal, independent, given - >>> from sympy import Tuple, Eq - - >>> X, Y = Normal('X', 0, 1), Normal('Y', 0, 1) - >>> independent(X, Y) - True - >>> independent(2*X + Y, -Y) - False - >>> X, Y = given(Tuple(X, Y), Eq(X+Y,3)) - >>> independent(X, Y) - False - - See Also - ======== - dependent - """ - return not dependent(a, b) - - -def pspace_independent(a, b): - """ - Tests for independence between a and b by checking if their PSpaces have - overlapping symbols. This is a sufficient but not necessary condition for - independence and is intended to be used internally. - Note: - pspace_independent(a,b) implies independent(a,b) - independent(a,b) does not imply pspace_independent(a,b) - """ - a_symbols = pspace(b).symbols - b_symbols = pspace(a).symbols - if len(a_symbols.intersect(b_symbols)) == 0: - return True - return None - - -def rv_subs(expr, symbols=None): - """ - Given a random expression replace all random variables with their symbols. - - If symbols keyword is given restrict the swap to only the symbols listed. - """ - if symbols is None: - symbols = random_symbols(expr) - swapdict = dict([(rv, rv.symbol) for rv in symbols]) - return expr.subs(swapdict) -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/tensor/index_methods.html b/dev-py3k/_modules/sympy/tensor/index_methods.html deleted file mode 100644 index 6e2585fa211..00000000000 --- a/dev-py3k/_modules/sympy/tensor/index_methods.html +++ /dev/null @@ -1,558 +0,0 @@ - - - - - - - - - - sympy.tensor.index_methods — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.tensor.index_methods

    -"""Module with functions operating on IndexedBase, Indexed and Idx objects
    -
    -    - Check shape conformance
    -    - Determine indices in resulting expression
    -
    -    etc.
    -
    -    Methods in this module could be implemented by calling methods on Expr
    -    objects instead.  When things stabilize this could be a useful
    -    refactoring.
    -"""
    -
    -from sympy.tensor.indexed import Idx, Indexed
    -from sympy.functions import exp
    -from sympy.core import C
    -
    -from sympy.core.compatibility import reduce
    -from functools import reduce
    -
    -
    -class IndexConformanceException(Exception):
    -    pass
    -
    -
    -def _remove_repeated(inds):
    -    """Removes repeated objects from sequences
    -
    -    Returns a set of the unique objects and a tuple of all that have been
    -    removed.
    -
    -    >>> from sympy.tensor.index_methods import _remove_repeated
    -    >>> l1 = [1, 2, 3, 2]
    -    >>> _remove_repeated(l1)
    -    (set([1, 3]), (2,))
    -
    -    """
    -    sum_index = {}
    -    for i in inds:
    -        if i in sum_index:
    -            sum_index[i] += 1
    -        else:
    -            sum_index[i] = 0
    -    inds = [x for x in inds if not sum_index[x]]
    -    return set(inds), tuple([ i for i in sum_index if sum_index[i] ])
    -
    -
    -def _get_indices_Mul(expr, return_dummies=False):
    -    """Determine the outer indices of a Mul object.
    -
    -    >>> from sympy.tensor.index_methods import _get_indices_Mul
    -    >>> from sympy.tensor.indexed import IndexedBase, Idx
    -    >>> i, j, k = list(map(Idx, ['i', 'j', 'k']))
    -    >>> x = IndexedBase('x')
    -    >>> y = IndexedBase('y')
    -    >>> _get_indices_Mul(x[i, k]*y[j, k])
    -    (set([i, j]), {})
    -    >>> _get_indices_Mul(x[i, k]*y[j, k], return_dummies=True)
    -    (set([i, j]), {}, (k,))
    -
    -    """
    -
    -    inds = list(map(get_indices, expr.args))
    -    inds, syms = list(zip(*inds))
    -
    -    inds = list(map(list, inds))
    -    inds = reduce(lambda x, y: x + y, inds)
    -    inds, dummies = _remove_repeated(inds)
    -
    -    symmetry = {}
    -    for s in syms:
    -        for pair in s:
    -            if pair in symmetry:
    -                symmetry[pair] *= s[pair]
    -            else:
    -                symmetry[pair] = s[pair]
    -
    -    if return_dummies:
    -        return inds, symmetry, dummies
    -    else:
    -        return inds, symmetry
    -
    -
    -def _get_indices_Pow(expr):
    -    """Determine outer indices of a power or an exponential.
    -
    -    A power is considered a universal function, so that the indices of a Pow is
    -    just the collection of indices present in the expression.  This may be
    -    viewed as a bit inconsistent in the special case:
    -
    -        x[i]**2 = x[i]*x[i]                                                      (1)
    -
    -    The above expression could have been interpreted as the contraction of x[i]
    -    with itself, but we choose instead to interpret it as a function
    -
    -        lambda y: y**2
    -
    -    applied to each element of x (a universal function in numpy terms).  In
    -    order to allow an interpretation of (1) as a contraction, we need
    -    contravariant and covariant Idx subclasses.  (FIXME: this is not yet
    -    implemented)
    -
    -    Expressions in the base or exponent are subject to contraction as usual,
    -    but an index that is present in the exponent, will not be considered
    -    contractable with its own base.  Note however, that indices in the same
    -    exponent can be contracted with each other.
    -
    -    >>> from sympy.tensor.index_methods import _get_indices_Pow
    -    >>> from sympy import Pow, exp, IndexedBase, Idx
    -    >>> A = IndexedBase('A')
    -    >>> x = IndexedBase('x')
    -    >>> i, j, k = list(map(Idx, ['i', 'j', 'k']))
    -    >>> _get_indices_Pow(exp(A[i, j]*x[j]))
    -    (set([i]), {})
    -    >>> _get_indices_Pow(Pow(x[i], x[i]))
    -    (set([i]), {})
    -    >>> _get_indices_Pow(Pow(A[i, j]*x[j], x[i]))
    -    (set([i]), {})
    -
    -    """
    -    base, exp = expr.as_base_exp()
    -    binds, bsyms = get_indices(base)
    -    einds, esyms = get_indices(exp)
    -
    -    inds = binds | einds
    -
    -    # FIXME: symmetries from power needs to check special cases, else nothing
    -    symmetries = {}
    -
    -    return inds, symmetries
    -
    -
    -def _get_indices_Add(expr):
    -    """Determine outer indices of an Add object.
    -
    -    In a sum, each term must have the same set of outer indices.  A valid
    -    expression could be
    -
    -        x(i)*y(j) - x(j)*y(i)
    -
    -    But we do not allow expressions like:
    -
    -        x(i)*y(j) - z(j)*z(j)
    -
    -    FIXME: Add support for Numpy broadcasting
    -
    -    >>> from sympy.tensor.index_methods import _get_indices_Add
    -    >>> from sympy.tensor.indexed import IndexedBase, Idx
    -    >>> i, j, k = list(map(Idx, ['i', 'j', 'k']))
    -    >>> x = IndexedBase('x')
    -    >>> y = IndexedBase('y')
    -    >>> _get_indices_Add(x[i] + x[k]*y[i, k])
    -    (set([i]), {})
    -
    -    """
    -
    -    inds = list(map(get_indices, expr.args))
    -    inds, syms = list(zip(*inds))
    -
    -    # allow broadcast of scalars
    -    non_scalars = [x for x in inds if x != set()]
    -    if not non_scalars:
    -        return set(), {}
    -
    -    if not all([x == non_scalars[0] for x in non_scalars[1:]]):
    -        raise IndexConformanceException("Indices are not consistent: %s" % expr)
    -    if not reduce(lambda x, y: x != y or y, syms):
    -        symmetries = syms[0]
    -    else:
    -        # FIXME: search for symmetries
    -        symmetries = {}
    -
    -    return non_scalars[0], symmetries
    -
    -
    -
    [docs]def get_indices(expr): - """Determine the outer indices of expression ``expr`` - - By *outer* we mean indices that are not summation indices. Returns a set - and a dict. The set contains outer indices and the dict contains - information about index symmetries. - - Examples - ======== - - >>> from sympy.tensor.index_methods import get_indices - >>> from sympy import symbols - >>> from sympy.tensor import IndexedBase, Idx - >>> x, y, A = list(map(IndexedBase, ['x', 'y', 'A'])) - >>> i, j, a, z = symbols('i j a z', integer=True) - - The indices of the total expression is determined, Repeated indices imply a - summation, for instance the trace of a matrix A: - - >>> get_indices(A[i, i]) - (set(), {}) - - In the case of many terms, the terms are required to have identical - outer indices. Else an IndexConformanceException is raised. - - >>> get_indices(x[i] + A[i, j]*y[j]) - (set([i]), {}) - - :Exceptions: - - An IndexConformanceException means that the terms ar not compatible, e.g. - - >>> get_indices(x[i] + y[j]) #doctest: +SKIP - (...) - IndexConformanceException: Indices are not consistent: x(i) + y(j) - - .. warning:: - The concept of *outer* indices applies recursively, starting on the deepest - level. This implies that dummies inside parenthesis are assumed to be - summed first, so that the following expression is handled gracefully: - - >>> get_indices((x[i] + A[i, j]*y[j])*x[j]) - (set([i, j]), {}) - - This is correct and may appear convenient, but you need to be careful - with this as SymPy will happily .expand() the product, if requested. The - resulting expression would mix the outer ``j`` with the dummies inside - the parenthesis, which makes it a different expression. To be on the - safe side, it is best to avoid such ambiguities by using unique indices - for all contractions that should be held separate. - - """ - # We call ourself recursively to determine indices of sub expressions. - - # break recursion - if isinstance(expr, Indexed): - c = expr.indices - inds, dummies = _remove_repeated(c) - return inds, {} - elif expr is None: - return set(), {} - elif expr.is_Atom: - return set(), {} - elif isinstance(expr, Idx): - return set([expr]), {} - - # recurse via specialized functions - else: - if expr.is_Mul: - return _get_indices_Mul(expr) - elif expr.is_Add: - return _get_indices_Add(expr) - elif expr.is_Pow or isinstance(expr, exp): - return _get_indices_Pow(expr) - - elif isinstance(expr, C.Piecewise): - # FIXME: No support for Piecewise yet - return set(), {} - elif isinstance(expr, C.Function): - # Support ufunc like behaviour by returning indices from arguments. - # Functions do not interpret repeated indices across argumnts - # as summation - ind0 = set() - for arg in expr.args: - ind, sym = get_indices(arg) - ind0 |= ind - return ind0, sym - - # this test is expensive, so it should be at the end - elif not expr.has(Indexed): - return set(), {} - raise NotImplementedError( - "FIXME: No specialized handling of type %s" % type(expr)) - -
    -
    [docs]def get_contraction_structure(expr): - """Determine dummy indices of ``expr`` and describe it's structure - - By *dummy* we mean indices that are summation indices. - - The stucture of the expression is determined and described as follows: - - 1) A conforming summation of Indexed objects is described with a dict where - the keys are summation indices and the corresponding values are sets - containing all terms for which the summation applies. All Add objects - in the SymPy expression tree are described like this. - - 2) For all nodes in the SymPy expression tree that are *not* of type Add, the - following applies: - - If a node discovers contractions in one of it's arguments, the node - itself will be stored as a key in the dict. For that key, the - corresponding value is a list of dicts, each of which is the result of a - recursive call to get_contraction_structure(). The list contains only - dicts for the non-trivial deeper contractions, ommitting dicts with None - as the one and only key. - - .. Note:: The presence of expressions among the dictinary keys indicates - multiple levels of index contractions. A nested dict displays nested - contractions and may itself contain dicts from a deeper level. In - practical calculations the summation in the deepest nested level must be - calculated first so that the outer expression can access the resulting - indexed object. - - Examples - ======== - - >>> from sympy.tensor.index_methods import get_contraction_structure - >>> from sympy import symbols, default_sort_key - >>> from sympy.tensor import IndexedBase, Idx - >>> x, y, A = list(map(IndexedBase, ['x', 'y', 'A'])) - >>> i, j, k, l = list(map(Idx, ['i', 'j', 'k', 'l'])) - >>> get_contraction_structure(x[i]*y[i] + A[j, j]) - {(i,): set([x[i]*y[i]]), (j,): set([A[j, j]])} - >>> get_contraction_structure(x[i]*y[j]) - {None: set([x[i]*y[j]])} - - A multiplication of contracted factors results in nested dicts representing - the internal contractions. - - >>> d = get_contraction_structure(x[i, i]*y[j, j]) - >>> sorted(list(d.keys()), key=default_sort_key) - [None, x[i, i]*y[j, j]] - - In this case, the product has no contractions: - - >>> d[None] - set([x[i, i]*y[j, j]]) - - Factors are contracted "first": - - >>> sorted(d[x[i, i]*y[j, j]], key=default_sort_key) - [{(i,): set([x[i, i]])}, {(j,): set([y[j, j]])}] - - A parenthesized Add object is also returned as a nested dictionary. The - term containing the parenthesis is a Mul with a contraction among the - arguments, so it will be found as a key in the result. It stores the - dictionary resulting from a recursive call on the Add expression. - - >>> d = get_contraction_structure(x[i]*(y[i] + A[i, j]*x[j])) - >>> sorted(list(d.keys()), key=default_sort_key) - [x[i]*(y[i] + A[i, j]*x[j]), (i,)] - >>> d[(i,)] - set([x[i]*(y[i] + A[i, j]*x[j])]) - >>> d[x[i]*(A[i, j]*x[j] + y[i])] - [{None: set([y[i]]), (j,): set([A[i, j]*x[j]])}] - - Powers with contractions in either base or exponent will also be found as - keys in the dictionary, mapping to a list of results from recursive calls: - - >>> d = get_contraction_structure(A[j, j]**A[i, i]) - >>> d[None] - set([A[j, j]**A[i, i]]) - >>> nested_contractions = d[A[j, j]**A[i, i]] - >>> nested_contractions[0] - {(j,): set([A[j, j]])} - >>> nested_contractions[1] - {(i,): set([A[i, i]])} - - The description of the contraction structure may appear complicated when - represented with a string in the above examples, but it is easy to iterate - over: - - >>> from sympy import Expr - >>> for key in d: - ... if isinstance(key, Expr): - ... continue - ... for term in d[key]: - ... if term in d: - ... # treat deepest contraction first - ... pass - ... # treat outermost contactions here - - """ - - # We call ourself recursively to inspect sub expressions. - - if isinstance(expr, Indexed): - junk, key = _remove_repeated(expr.indices) - return {key or None: set([expr])} - elif expr.is_Atom: - return {None: set([expr])} - elif expr.is_Mul: - junk, junk, key = _get_indices_Mul(expr, return_dummies=True) - result = {key or None: set([expr])} - # recurse on every factor - nested = [] - for fac in expr.args: - facd = get_contraction_structure(fac) - if not (None in facd and len(facd) == 1): - nested.append(facd) - if nested: - result[expr] = nested - return result - elif expr.is_Pow or isinstance(expr, exp): - # recurse in base and exp separately. If either has internal - # contractions we must include ourselves as a key in the returned dict - b, e = expr.as_base_exp() - dbase = get_contraction_structure(b) - dexp = get_contraction_structure(e) - - dicts = [] - for d in dbase, dexp: - if not (None in d and len(d) == 1): - dicts.append(d) - result = {None: set([expr])} - if dicts: - result[expr] = dicts - return result - elif expr.is_Add: - # Note: we just collect all terms with identical summation indices, We - # do nothing to identify equivalent terms here, as this would require - # substitutions or pattern matching in expressions of unknown - # complexity. - result = {} - for term in expr.args: - # recurse on every term - d = get_contraction_structure(term) - for key in d: - if key in result: - result[key] |= d[key] - else: - result[key] = d[key] - return result - - elif isinstance(expr, C.Piecewise): - # FIXME: No support for Piecewise yet - return {None: expr} - elif isinstance(expr, C.Function): - # Collect non-trivial contraction structures in each argument - # We do not report repeated indices in separate arguments as a - # contraction - deeplist = [] - for arg in expr.args: - deep = get_contraction_structure(arg) - if not (None in deep and len(deep) == 1): - deeplist.append(deep) - d = {None: set([expr])} - if deeplist: - d[expr] = deeplist - return d - - # this test is expensive, so it should be at the end - elif not expr.has(Indexed): - return {None: set([expr])} - raise NotImplementedError( - "FIXME: No specialized handling of type %s" % type(expr))
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/tensor/indexed.html b/dev-py3k/_modules/sympy/tensor/indexed.html deleted file mode 100644 index 40bb867fc55..00000000000 --- a/dev-py3k/_modules/sympy/tensor/indexed.html +++ /dev/null @@ -1,696 +0,0 @@ - - - - - - - - - - sympy.tensor.indexed — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.tensor.indexed

    -"""Module that defines indexed objects
    -
    -    The classes IndexedBase, Indexed and Idx would represent a matrix element
    -    M[i, j] as in the following graph::
    -
    -       1) The Indexed class represents the entire indexed object.
    -                  |
    -               ___|___
    -              '       '
    -               M[i, j]
    -              /   \__\______
    -              |             |
    -              |             |
    -              |     2) The Idx class represent indices and each Idx can
    -              |        optionally contain information about its range.
    -              |
    -        3) IndexedBase represents the `stem' of an indexed object, here `M'.
    -           The stem used by itself is usually taken to represent the entire
    -           array.
    -
    -    There can be any number of indices on an Indexed object.  No
    -    transformation properties are implemented in these Base objects, but
    -    implicit contraction of repeated indices is supported.
    -
    -    Note that the support for complicated (i.e. non-atomic) integer
    -    expressions as indices is limited.  (This should be improved in
    -    future releases.)
    -
    -    Examples
    -    ========
    -
    -    To express the above matrix element example you would write:
    -
    -    >>> from sympy.tensor import IndexedBase, Idx
    -    >>> from sympy import symbols
    -    >>> M = IndexedBase('M')
    -    >>> i, j = symbols('i j', cls=Idx)
    -    >>> M[i, j]
    -    M[i, j]
    -
    -    Repeated indices in a product implies a summation, so to express a
    -    matrix-vector product in terms of Indexed objects:
    -
    -    >>> x = IndexedBase('x')
    -    >>> M[i, j]*x[j]
    -    M[i, j]*x[j]
    -
    -    If the indexed objects will be converted to component based arrays, e.g.
    -    with the code printers or the autowrap framework, you also need to provide
    -    (symbolic or numerical) dimensions.  This can be done by passing an
    -    optional shape parameter to IndexedBase upon construction:
    -
    -    >>> dim1, dim2 = symbols('dim1 dim2', integer=True)
    -    >>> A = IndexedBase('A', shape=(dim1, 2*dim1, dim2))
    -    >>> A.shape
    -    (dim1, 2*dim1, dim2)
    -    >>> A[i, j, 3].shape
    -    (dim1, 2*dim1, dim2)
    -
    -    If an IndexedBase object has no shape information, it is assumed that the
    -    array is as large as the ranges of it's indices:
    -
    -    >>> n, m = symbols('n m', integer=True)
    -    >>> i = Idx('i', m)
    -    >>> j = Idx('j', n)
    -    >>> M[i, j].shape
    -    (m, n)
    -    >>> M[i, j].ranges
    -    [(0, m - 1), (0, n - 1)]
    -
    -    The above can be compared with the following:
    -
    -    >>> A[i, 2, j].shape
    -    (dim1, 2*dim1, dim2)
    -    >>> A[i, 2, j].ranges
    -    [(0, m - 1), None, (0, n - 1)]
    -
    -    To analyze the structure of indexed expressions, you can use the methods
    -    get_indices() and get_contraction_structure():
    -
    -    >>> from sympy.tensor import get_indices, get_contraction_structure
    -    >>> get_indices(A[i, j, j])
    -    (set([i]), {})
    -    >>> get_contraction_structure(A[i, j, j])
    -    {(j,): set([A[i, j, j]])}
    -
    -    See the appropriate docstrings for a detailed explanation of the output.
    -
    -"""
    -
    -#   TODO:  (some ideas for improvement)
    -#
    -#   o test and guarantee numpy compatibility
    -#      - implement full support for broadcasting
    -#      - strided arrays
    -#
    -#   o more functions to analyze indexed expressions
    -#      - identify standard constructs, e.g matrix-vector product in a subexpression
    -#
    -#   o functions to generate component based arrays (numpy and sympy.Matrix)
    -#      - generate a single array directly from Indexed
    -#      - convert simple sub-expressions
    -#
    -#   o sophisticated indexing (possibly in subclasses to preserve simplicity)
    -#      - Idx with range smaller than dimension of Indexed
    -#      - Idx with stepsize != 1
    -#      - Idx with step determined by function call
    -
    -from sympy.core import Expr, Tuple, Symbol, sympify, S
    -from sympy.core.compatibility import is_sequence
    -
    -
    -class IndexException(Exception):
    -    pass
    -
    -
    -
    [docs]class Indexed(Expr): - """Represents a mathematical object with indices. - - >>> from sympy.tensor import Indexed, IndexedBase, Idx - >>> from sympy import symbols - >>> i, j = symbols('i j', cls=Idx) - >>> Indexed('A', i, j) - A[i, j] - - It is recommended that Indexed objects are created via IndexedBase: - - >>> A = IndexedBase('A') - >>> Indexed('A', i, j) == A[i, j] - True - - """ - is_commutative = False - - def __new__(cls, base, *args, **kw_args): - from sympy.utilities.misc import filldedent - - if not args: - raise IndexException("Indexed needs at least one index.") - if isinstance(base, (str, Symbol)): - base = IndexedBase(base) - elif not isinstance(base, IndexedBase): - raise TypeError(filldedent(""" - Indexed expects string, Symbol or IndexedBase as base.""")) - args = list(map(sympify, args)) - return Expr.__new__(cls, base, *args, **kw_args) - - @property -
    [docs] def base(self): - """Returns the IndexedBase of the Indexed object. - - Examples - ======== - - >>> from sympy.tensor import Indexed, IndexedBase, Idx - >>> from sympy import symbols - >>> i, j = symbols('i j', cls=Idx) - >>> Indexed('A', i, j).base - A - >>> B = IndexedBase('B') - >>> B == B[i, j].base - True - - """ - return self.args[0] -
    - @property -
    [docs] def indices(self): - """ - Returns the indices of the Indexed object. - - Examples - ======== - - >>> from sympy.tensor import Indexed, Idx - >>> from sympy import symbols - >>> i, j = symbols('i j', cls=Idx) - >>> Indexed('A', i, j).indices - (i, j) - - """ - return self.args[1:] -
    - @property -
    [docs] def rank(self): - """ - Returns the rank of the Indexed object. - - Examples - ======== - - >>> from sympy.tensor import Indexed, Idx - >>> from sympy import symbols - >>> i, j, k, l, m = symbols('i:m', cls=Idx) - >>> Indexed('A', i, j).rank - 2 - >>> q = Indexed('A', i, j, k, l, m) - >>> q.rank - 5 - >>> q.rank == len(q.indices) - True - - """ - return len(self.args) - 1 -
    - @property -
    [docs] def shape(self): - """Returns a list with dimensions of each index. - - Dimensions is a property of the array, not of the indices. Still, if - the IndexedBase does not define a shape attribute, it is assumed that - the ranges of the indices correspond to the shape of the array. - - >>> from sympy.tensor.indexed import IndexedBase, Idx - >>> from sympy import symbols - >>> n, m = symbols('n m', integer=True) - >>> i = Idx('i', m) - >>> j = Idx('j', m) - >>> A = IndexedBase('A', shape=(n, n)) - >>> B = IndexedBase('B') - >>> A[i, j].shape - (n, n) - >>> B[i, j].shape - (m, m) - """ - from sympy.utilities.misc import filldedent - - if self.base.shape: - return self.base.shape - try: - return Tuple(*[i.upper - i.lower + 1 for i in self.indices]) - except AttributeError: - raise IndexException(filldedent(""" - Range is not defined for all indices in: %s""" % self)) - except TypeError: - raise IndexException(filldedent(""" - Shape cannot be inferred from Idx with - undefined range: %s""" % self)) -
    - @property -
    [docs] def ranges(self): - """Returns a list of tuples with lower and upper range of each index. - - If an index does not define the data members upper and lower, the - corresponding slot in the list contains ``None`` instead of a tuple. - - Examples - ======== - - >>> from sympy import Indexed,Idx, symbols - >>> Indexed('A', Idx('i', 2), Idx('j', 4), Idx('k', 8)).ranges - [(0, 1), (0, 3), (0, 7)] - >>> Indexed('A', Idx('i', 3), Idx('j', 3), Idx('k', 3)).ranges - [(0, 2), (0, 2), (0, 2)] - >>> x, y, z = symbols('x y z', integer=True) - >>> Indexed('A', x, y, z).ranges - [None, None, None] - - """ - ranges = [] - for i in self.indices: - try: - ranges.append(Tuple(i.lower, i.upper)) - except AttributeError: - ranges.append(None) - return ranges -
    - def _sympystr(self, p): - indices = list(map(p.doprint, self.indices)) - return "%s[%s]" % (p.doprint(self.base), ", ".join(indices)) - -
    -
    [docs]class IndexedBase(Expr): - """Represent the base or stem of an indexed object - - The IndexedBase class represent an array that contains elements. The main purpose - of this class is to allow the convenient creation of objects of the Indexed - class. The __getitem__ method of IndexedBase returns an instance of - Indexed. Alone, without indices, the IndexedBase class can be used as a - notation for e.g. matrix equations, resembling what you could do with the - Symbol class. But, the IndexedBase class adds functionality that is not - available for Symbol instances: - - - An IndexedBase object can optionally store shape information. This can - be used in to check array conformance and conditions for numpy - broadcasting. (TODO) - - An IndexedBase object implements syntactic sugar that allows easy symbolic - representation of array operations, using implicit summation of - repeated indices. - - The IndexedBase object symbolizes a mathematical structure equivalent - to arrays, and is recognized as such for code generation and automatic - compilation and wrapping. - - >>> from sympy.tensor import IndexedBase, Idx - >>> from sympy import symbols - >>> A = IndexedBase('A'); A - A - >>> type(A) - <class 'sympy.tensor.indexed.IndexedBase'> - - When an IndexedBase object receives indices, it returns an array with named - axes, represented by an Indexed object: - - >>> i, j = symbols('i j', integer=True) - >>> A[i, j, 2] - A[i, j, 2] - >>> type(A[i, j, 2]) - <class 'sympy.tensor.indexed.Indexed'> - - The IndexedBase constructor takes an optional shape argument. If given, - it overrides any shape information in the indices. (But not the index - ranges!) - - >>> m, n, o, p = symbols('m n o p', integer=True) - >>> i = Idx('i', m) - >>> j = Idx('j', n) - >>> A[i, j].shape - (m, n) - >>> B = IndexedBase('B', shape=(o, p)) - >>> B[i, j].shape - (o, p) - - """ - is_commutative = False - - def __new__(cls, label, shape=None, **kw_args): - if not isinstance(label, (str, Symbol)): - raise TypeError("Base label should be a string or Symbol.") - - label = sympify(label) - obj = Expr.__new__(cls, label, **kw_args) - if is_sequence(shape): - obj._shape = Tuple(*shape) - else: - obj._shape = sympify(shape) - return obj - - @property -
    [docs] def args(self): - """Returns the arguments used to create this IndexedBase object. - - Examples - ======== - - >>> from sympy import IndexedBase - >>> from sympy.abc import x, y - >>> IndexedBase('A', shape=(x, y)).args - (A, (x, y)) - - """ - if self._shape: - return self._args + (self._shape,) - else: - return self._args -
    - def _hashable_content(self): - return Expr._hashable_content(self) + (self._shape,) - - def __getitem__(self, indices, **kw_args): - if is_sequence(indices): - # Special case needed because M[*my_tuple] is a syntax error. - if self.shape and len(self.shape) != len(indices): - raise IndexException("Rank mismatch.") - return Indexed(self, *indices, **kw_args) - else: - if self.shape and len(self.shape) != 1: - raise IndexException("Rank mismatch.") - return Indexed(self, indices, **kw_args) - - @property -
    [docs] def shape(self): - """Returns the shape of the IndexedBase object. - - Examples - ======== - - >>> from sympy import IndexedBase, Idx, Symbol - >>> from sympy.abc import x, y - >>> IndexedBase('A', shape=(x, y)).shape - (x, y) - - Note: If the shape of the IndexedBase is specified, it will override - any shape information given by the indices. - - >>> A = IndexedBase('A', shape=(x, y)) - >>> B = IndexedBase('B') - >>> i = Idx('i', 2) - >>> j = Idx('j', 1) - >>> A[i, j].shape - (x, y) - >>> B[i, j].shape - (2, 1) - - """ - return self._shape -
    - @property -
    [docs] def label(self): - """Returns the label of the IndexedBase object. - - Examples - ======== - - >>> from sympy import IndexedBase - >>> from sympy.abc import x, y - >>> IndexedBase('A', shape=(x, y)).label - A - - """ - return self.args[0] -
    - def _sympystr(self, p): - return p.doprint(self.label) - -
    -
    [docs]class Idx(Expr): - """Represents an integer index as an Integer or integer expression. - - There are a number of ways to create an Idx object. The constructor - takes two arguments: - - ``label`` - An integer or a symbol that labels the index. - ``range`` - Optionally you can specify a range as either - - - Symbol or integer: This is interpreted as a dimension. Lower and - upper bounds are set to 0 and range - 1, respectively. - - tuple: The two elements are interpreted as the lower and upper - bounds of the range, respectively. - - Note: the Idx constructor is rather pedantic in that it only accepts - integer arguments. The only exception is that you can use oo and -oo to - specify an unbounded range. For all other cases, both label and bounds - must be declared as integers, e.g. if n is given as an argument then - n.is_integer must return True. - - For convenience, if the label is given as a string it is automatically - converted to an integer symbol. (Note: this conversion is not done for - range or dimension arguments.) - - Examples - ======== - - >>> from sympy.tensor import IndexedBase, Idx - >>> from sympy import symbols, oo - >>> n, i, L, U = symbols('n i L U', integer=True) - - If a string is given for the label an integer Symbol is created and the - bounds are both None: - - >>> idx = Idx('qwerty'); idx - qwerty - >>> idx.lower, idx.upper - (None, None) - - Both upper and lower bounds can be specified: - - >>> idx = Idx(i, (L, U)); idx - i - >>> idx.lower, idx.upper - (L, U) - - When only a single bound is given it is interpreted as the dimension - and the lower bound defaults to 0: - - >>> idx = Idx(i, n); idx.lower, idx.upper - (0, n - 1) - >>> idx = Idx(i, 4); idx.lower, idx.upper - (0, 3) - >>> idx = Idx(i, oo); idx.lower, idx.upper - (0, oo) - - The label can be a literal integer instead of a string/Symbol: - - >>> idx = Idx(2, n); idx.lower, idx.upper - (0, n - 1) - >>> idx.label - 2 - - """ - - is_integer = True - - def __new__(cls, label, range=None, **kw_args): - from sympy.utilities.misc import filldedent - - if isinstance(label, str): - label = Symbol(label, integer=True) - label, range = list(map(sympify, (label, range))) - - if not label.is_integer: - raise TypeError("Idx object requires an integer label.") - - elif is_sequence(range): - if len(range) != 2: - raise ValueError(filldedent(""" - Idx range tuple must have length 2, but got %s""" % len(range))) - for bound in range: - if not (bound.is_integer or abs(bound) is S.Infinity): - raise TypeError("Idx object requires integer bounds.") - args = label, Tuple(*range) - elif isinstance(range, Expr): - if not (range.is_integer or range is S.Infinity): - raise TypeError("Idx object requires an integer dimension.") - args = label, Tuple(0, range - 1) - elif range: - raise TypeError(filldedent(""" - The range must be an ordered iterable or - integer SymPy expression.""")) - else: - args = label, - - obj = Expr.__new__(cls, *args, **kw_args) - return obj - - @property -
    [docs] def label(self): - """Returns the label (Integer or integer expression) of the Idx object. - - Examples - ======== - - >>> from sympy import Idx, Symbol - >>> Idx(2).label - 2 - >>> j = Symbol('j', integer=True) - >>> Idx(j).label - j - >>> Idx(j + 1).label - j + 1 - - """ - return self.args[0] -
    - @property -
    [docs] def lower(self): - """Returns the lower bound of the Index. - - Examples - ======== - - >>> from sympy import Idx - >>> Idx('j', 2).lower - 0 - >>> Idx('j', 5).lower - 0 - >>> Idx('j').lower is None - True - - """ - try: - return self.args[1][0] - except IndexError: - return -
    - @property -
    [docs] def upper(self): - """Returns the upper bound of the Index. - - Examples - ======== - - >>> from sympy import Idx - >>> Idx('j', 2).upper - 1 - >>> Idx('j', 5).upper - 4 - >>> Idx('j').upper is None - True - - """ - try: - return self.args[1][1] - except IndexError: - return -
    - def _sympystr(self, p): - return p.doprint(self.label)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/utilities/autowrap.html b/dev-py3k/_modules/sympy/utilities/autowrap.html deleted file mode 100644 index 5bfed29313d..00000000000 --- a/dev-py3k/_modules/sympy/utilities/autowrap.html +++ /dev/null @@ -1,598 +0,0 @@ - - - - - - - - - - sympy.utilities.autowrap — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.utilities.autowrap

    -"""Module for compiling codegen output, and wrap the binary for use in
    -python.
    -
    -.. note:: To use the autowrap module it must first be imported
    -
    -   >>> from sympy.utilities.autowrap import autowrap
    -
    -This module provides a common interface for different external backends, such
    -as f2py, fwrap, Cython, SWIG(?) etc. (Currently only f2py and Cython are
    -implemented) The goal is to provide access to compiled binaries of acceptable
    -performance with a one-button user interface, i.e.
    -
    -    >>> from sympy.abc import x,y
    -    >>> expr = ((x - y)**(25)).expand()
    -    >>> binary_callable = autowrap(expr)           # doctest: +SKIP
    -    >>> binary_callable(1, 2)                      # doctest: +SKIP
    -    -1.0
    -
    -The callable returned from autowrap() is a binary python function, not a
    -SymPy object.  If it is desired to use the compiled function in symbolic
    -expressions, it is better to use binary_function() which returns a SymPy
    -Function object.  The binary callable is attached as the _imp_ attribute and
    -invoked when a numerical evaluation is requested with evalf(), or with
    -lambdify().
    -
    -    >>> from sympy.utilities.autowrap import binary_function
    -    >>> f = binary_function('f', expr)             # doctest: +SKIP
    -    >>> 2*f(x, y) + y                              # doctest: +SKIP
    -    y + 2*f(x, y)
    -    >>> (2*f(x, y) + y).evalf(2, subs={x: 1, y:2}) # doctest: +SKIP
    -    0.0
    -
    -The idea is that a SymPy user will primarily be interested in working with
    -mathematical expressions, and should not have to learn details about wrapping
    -tools in order to evaluate expressions numerically, even if they are
    -computationally expensive.
    -
    -When is this useful?
    -
    -    1) For computations on large arrays, Python iterations may be too slow,
    -       and depending on the mathematical expression, it may be difficult to
    -       exploit the advanced index operations provided by NumPy.
    -
    -    2) For *really* long expressions that will be called repeatedly, the
    -       compiled binary should be significantly faster than SymPy's .evalf()
    -
    -    3) If you are generating code with the codegen utility in order to use
    -       it in another project, the automatic python wrappers let you test the
    -       binaries immediately from within SymPy.
    -
    -    4) To create customized ufuncs for use with numpy arrays.
    -       See *ufuncify*.
    -
    -When is this module NOT the best approach?
    -
    -    1) If you are really concerned about speed or memory optimizations,
    -       you will probably get better results by working directly with the
    -       wrapper tools and the low level code.  However, the files generated
    -       by this utility may provide a useful starting point and reference
    -       code. Temporary files will be left intact if you supply the keyword
    -       tempdir="path/to/files/".
    -
    -    2) If the array computation can be handled easily by numpy, and you
    -       don't need the binaries for another project.
    -
    -"""
    -
    -
    -import sys
    -import os
    -import shutil
    -import tempfile
    -import subprocess
    -
    -from sympy.utilities.codegen import (
    -    get_code_generator, Routine, OutputArgument, InOutArgument,
    -    CodeGenArgumentListError, Result
    -)
    -from sympy.utilities.lambdify import implemented_function
    -from sympy import C
    -
    -
    -class CodeWrapError(Exception):
    -    pass
    -
    -
    -
    [docs]class CodeWrapper: - """Base Class for code wrappers""" - _filename = "wrapped_code" - _module_basename = "wrapper_module" - _module_counter = 0 - - @property - def filename(self): - return "%s_%s" % (self._filename, CodeWrapper._module_counter) - - @property - def module_name(self): - return "%s_%s" % (self._module_basename, CodeWrapper._module_counter) - - def __init__(self, generator, filepath=None, flags=[], verbose=False): - """ - generator -- the code generator to use - """ - self.generator = generator - self.filepath = filepath - self.flags = flags - self.quiet = not verbose - - @property - def include_header(self): - return bool(self.filepath) - - @property - def include_empty(self): - return bool(self.filepath) - - def _generate_code(self, main_routine, routines): - routines.append(main_routine) - self.generator.write( - routines, self.filename, True, self.include_header, - self.include_empty) - - def wrap_code(self, routine, helpers=[]): - - workdir = self.filepath or tempfile.mkdtemp("_sympy_compile") - if not os.access(workdir, os.F_OK): - os.mkdir(workdir) - oldwork = os.getcwd() - os.chdir(workdir) - try: - sys.path.append(workdir) - self._generate_code(routine, helpers) - self._prepare_files(routine) - self._process_files(routine) - mod = __import__(self.module_name) - finally: - sys.path.remove(workdir) - CodeWrapper._module_counter += 1 - os.chdir(oldwork) - if not self.filepath: - shutil.rmtree(workdir) - - return self._get_wrapped_function(mod) - - def _process_files(self, routine): - command = self.command - command.extend(self.flags) - null = open(os.devnull, 'w') - try: - if self.quiet: - retcode = subprocess.call( - command, stdout=null, stderr=subprocess.STDOUT) - else: - retcode = subprocess.call(command) - except OSError: - retcode = 1 - if retcode: - raise CodeWrapError( - "Error while executing command: %s" % " ".join(command)) - -
    -
    [docs]class DummyWrapper(CodeWrapper): - """Class used for testing independent of backends """ - - template = """# dummy module for testing of SymPy -def %(name)s(): - return "%(expr)s" -%(name)s.args = "%(args)s" -%(name)s.returns = "%(retvals)s" -""" - - def _prepare_files(self, routine): - return - - def _generate_code(self, routine, helpers): - with open('%s.py' % self.module_name, 'w') as f: - printed = ", ".join( - [str(res.expr) for res in routine.result_variables]) - # convert OutputArguments to return value like f2py - inargs = [x for x in routine.arguments if not isinstance( - x, OutputArgument)] - retvals = [] - for val in routine.result_variables: - if isinstance(val, Result): - retvals.append('nameless') - else: - retvals.append(val.result_var) - - print(DummyWrapper.template % { - 'name': routine.name, - 'expr': printed, - 'args': ", ".join([str(arg.name) for arg in inargs]), - 'retvals': ", ".join([str(val) for val in retvals]) - }, file=f) - - def _process_files(self, routine): - return - - @classmethod - def _get_wrapped_function(cls, mod): - return mod.autofunc - -
    -
    [docs]class CythonCodeWrapper(CodeWrapper): - """Wrapper that uses Cython""" - - setup_template = """ -from distutils.core import setup -from distutils.extension import Extension -from Cython.Distutils import build_ext - -setup( - cmdclass = {'build_ext': build_ext}, - ext_modules = [Extension(%(args)s)] - ) -""" - - @property - def command(self): - command = [sys.executable, "setup.py", "build_ext", "--inplace"] - return command - - def _prepare_files(self, routine): - pyxfilename = self.module_name + '.pyx' - codefilename = "%s.%s" % (self.filename, self.generator.code_extension) - - # pyx - with open(pyxfilename, 'w') as f: - self.dump_pyx([routine], f, self.filename, - self.include_header, self.include_empty) - - # setup.py - ext_args = [repr(self.module_name), repr([pyxfilename, codefilename])] - with open('setup.py', 'w') as f: - print(CythonCodeWrapper.setup_template % { - 'args': ", ".join(ext_args)}, file=f) - - @classmethod - def _get_wrapped_function(cls, mod): - return mod.autofunc_c - -
    [docs] def dump_pyx(self, routines, f, prefix, header=True, empty=True): - """Write a Cython file with python wrappers - - This file contains all the definitions of the routines in c code and - refers to the header file. - - :Arguments: - - routines - List of Routine instances - f - File-like object to write the file to - prefix - The filename prefix, used to refer to the proper header file. - Only the basename of the prefix is used. - empty - Optional. When True, empty lines are included to structure the - source files. [DEFAULT=True] - """ - for routine in routines: - prototype = self.generator.get_prototype(routine) - origname = routine.name - routine.name = "%s_c" % origname - prototype_c = self.generator.get_prototype(routine) - routine.name = origname - - # declare - print('cdef extern from "%s.h":' % prefix, file=f) - print(' %s' % prototype, file=f) - if empty: - print(file=f) - - # wrap - ret, args_py = self._split_retvals_inargs(routine.arguments) - args_c = ", ".join([str(a.name) for a in routine.arguments]) - print("def %s_c(%s):" % (routine.name, - ", ".join(self._declare_arg(arg) for arg in args_py)), file=f) - for r in ret: - if not r in args_py: - print(" cdef %s" % self._declare_arg(r), file=f) - rets = ", ".join([str(r.name) for r in ret]) - if routine.results: - call = ' return %s(%s)' % (routine.name, args_c) - if rets: - print(call + ', ' + rets, file=f) - else: - print(call, file=f) - else: - print(' %s(%s)' % (routine.name, args_c), file=f) - print(' return %s' % rets, file=f) - - if empty: - print(file=f)
    - dump_pyx.extension = "pyx" - - def _split_retvals_inargs(self, args): - """Determines arguments and return values for python wrapper""" - py_args = [] - py_returns = [] - for arg in args: - if isinstance(arg, OutputArgument): - py_returns.append(arg) - elif isinstance(arg, InOutArgument): - py_returns.append(arg) - py_args.append(arg) - else: - py_args.append(arg) - return py_returns, py_args - - def _declare_arg(self, arg): - t = arg.get_datatype('c') - if arg.dimensions: - return "%s *%s" % (t, str(arg.name)) - else: - return "%s %s" % (t, str(arg.name)) - -
    -
    [docs]class F2PyCodeWrapper(CodeWrapper): - """Wrapper that uses f2py""" - - @property - def command(self): - filename = self.filename + '.' + self.generator.code_extension - command = ["f2py", "-m", self.module_name, "-c", filename] - return command - - def _prepare_files(self, routine): - pass - - @classmethod - def _get_wrapped_function(cls, mod): - return mod.autofunc - -
    -def _get_code_wrapper_class(backend): - wrappers = { 'F2PY': F2PyCodeWrapper, 'CYTHON': CythonCodeWrapper, - 'DUMMY': DummyWrapper} - return wrappers[backend.upper()] - - -
    [docs]def autowrap( - expr, language='F95', backend='f2py', tempdir=None, args=None, flags=[], - verbose=False, helpers=[]): - """Generates python callable binaries based on the math expression. - - expr - The SymPy expression that should be wrapped as a binary routine - - :Optional arguments: - - language - The programming language to use, currently 'C' or 'F95' - backend - The wrapper backend to use, currently f2py or Cython - tempdir - Path to directory for temporary files. If this argument is supplied, - the generated code and the wrapper input files are left intact in the - specified path. - args - Sequence of the formal parameters of the generated code, if ommited the - function signature is determined by the code generator. - flags - Additional option flags that will be passed to the backend - verbose - If True, autowrap will not mute the command line backends. This can be - helpful for debugging. - helpers - Used to define auxillary expressions needed for the main expr. If the - main expression need to do call a specialized function it should be put - in the ``helpers`` list. Autowrap will then make sure that the compiled - main expression can link to the helper routine. Items should be tuples - with (<funtion_name>, <sympy_expression>, <arguments>). It is - mandatory to supply an argument sequence to helper routines. - - >>> from sympy.abc import x, y, z - >>> from sympy.utilities.autowrap import autowrap - >>> expr = ((x - y + z)**(13)).expand() - >>> binary_func = autowrap(expr) # doctest: +SKIP - >>> binary_func(1, 4, 2) # doctest: +SKIP - -1.0 - - """ - - code_generator = get_code_generator(language, "autowrap") - CodeWrapperClass = _get_code_wrapper_class(backend) - code_wrapper = CodeWrapperClass(code_generator, tempdir, flags, verbose) - try: - routine = Routine('autofunc', expr, args) - except CodeGenArgumentListError as e: - # if all missing arguments are for pure output, we simply attach them - # at the end and try again, because the wrappers will silently convert - # them to return values anyway. - new_args = [] - for missing in e.missing_args: - if not isinstance(missing, OutputArgument): - raise - new_args.append(missing.name) - routine = Routine('autofunc', expr, args + new_args) - - helps = [] - for name, expr, args in helpers: - helps.append(Routine(name, expr, args)) - - return code_wrapper.wrap_code(routine, helpers=helps) - -
    -
    [docs]def binary_function(symfunc, expr, **kwargs): - """Returns a sympy function with expr as binary implementation - - This is a convenience function that automates the steps needed to - autowrap the SymPy expression and attaching it to a Function object - with implemented_function(). - - >>> from sympy.abc import x, y, z - >>> from sympy.utilities.autowrap import binary_function - >>> expr = ((x - y)**(25)).expand() - >>> f = binary_function('f', expr) # doctest: +SKIP - >>> type(f) # doctest: +SKIP - <class 'sympy.core.function.FunctionClass'> - >>> 2*f(x, y) # doctest: +SKIP - 2*f(x, y) - >>> f(x, y).evalf(2, subs={x: 1, y: 2}) # doctest: +SKIP - -1.0 - """ - binary = autowrap(expr, **kwargs) - return implemented_function(symfunc, binary) - -
    -
    [docs]def ufuncify(args, expr, **kwargs): - """ - Generates a binary ufunc-like lambda function for numpy arrays - - ``args`` - Either a Symbol or a tuple of symbols. Specifies the argument sequence - for the ufunc-like function. - - ``expr`` - A SymPy expression that defines the element wise operation - - ``kwargs`` - Optional keyword arguments are forwarded to autowrap(). - - The returned function can only act on one array at a time, as only the - first argument accept arrays as input. - - .. Note:: a *proper* numpy ufunc is required to support broadcasting, type - casting and more. The function returned here, may not qualify for - numpy's definition of a ufunc. That why we use the term ufunc-like. - - References - ========== - [1] http://docs.scipy.org/doc/numpy/reference/ufuncs.html - - Examples - ======== - - >>> from sympy.utilities.autowrap import ufuncify - >>> from sympy.abc import x, y, z - >>> f = ufuncify([x, y], y + x**2) # doctest: +SKIP - >>> f([1, 2, 3], 2) # doctest: +SKIP - [2. 5. 10.] - - """ - y = C.IndexedBase(C.Dummy('y')) - x = C.IndexedBase(C.Dummy('x')) - m = C.Dummy('m', integer=True) - i = C.Dummy('i', integer=True) - i = C.Idx(i, m) - l = C.Lambda(args, expr) - f = implemented_function('f', l) - - if isinstance(args, C.Symbol): - args = [args] - else: - args = list(args) - - # first argument accepts an array - args[0] = x[i] - return autowrap(C.Equality(y[i], f(*args)), **kwargs)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/utilities/codegen.html b/dev-py3k/_modules/sympy/utilities/codegen.html deleted file mode 100644 index 35d0155317d..00000000000 --- a/dev-py3k/_modules/sympy/utilities/codegen.html +++ /dev/null @@ -1,1107 +0,0 @@ - - - - - - - - - - sympy.utilities.codegen — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.utilities.codegen

    -"""
    -module for generating C, C++, Fortran77, Fortran90 and python routines that
    -evaluate sympy expressions. This module is work in progress. Only the
    -milestones with a '+' character in the list below have been completed.
    -
    ---- How is sympy.utilities.codegen different from sympy.printing.ccode? ---
    -
    -We considered the idea to extend the printing routines for sympy functions in
    -such a way that it prints complete compilable code, but this leads to a few
    -unsurmountable issues that can only be tackled with dedicated code generator:
    -
    -- For C, one needs both a code and a header file, while the printing routines
    -  generate just one string. This code generator can be extended to support
    -  .pyf files for f2py.
    -
    -- SymPy functions are not concerned with programming-technical issues, such
    -  as input, output and input-output arguments. Other examples are contiguous
    -  or non-contiguous arrays, including headers of other libraries such as gsl
    -  or others.
    -
    -- It is highly interesting to evaluate several sympy functions in one C
    -  routine, eventually sharing common intermediate results with the help
    -  of the cse routine. This is more than just printing.
    -
    -- From the programming perspective, expressions with constants should be
    -  evaluated in the code generator as much as possible. This is different
    -  for printing.
    -
    ---- Basic assumptions ---
    -
    -* A generic Routine data structure describes the routine that must be
    -  translated into C/Fortran/... code. This data structure covers all
    -  features present in one or more of the supported languages.
    -
    -* Descendants from the CodeGen class transform multiple Routine instances
    -  into compilable code. Each derived class translates into a specific
    -  language.
    -
    -* In many cases, one wants a simple workflow. The friendly functions in the
    -  last part are a simple api on top of the Routine/CodeGen stuff. They are
    -  easier to use, but are less powerful.
    -
    ---- Milestones ---
    -
    -+ First working version with scalar input arguments, generating C code,
    -  tests
    -+ Friendly functions that are easier to use than the rigorous
    -  Routine/CodeGen workflow.
    -+ Integer and Real numbers as input and output
    -+ Output arguments
    -+ InputOutput arguments
    -+ Sort input/output arguments properly
    -+ Contiguous array arguments (numpy matrices)
    -+ Also generate .pyf code for f2py (in autowrap module)
    -+ Isolate constants and evaluate them beforehand in double precision
    -+ Fortran 90
    -
    -- Common Subexpression Elimination
    -- User defined comments in the generated code
    -- Optional extra include lines for libraries/objects that can eval special
    -  functions
    -- Test other C compilers and libraries: gcc, tcc, libtcc, gcc+gsl, ...
    -- Contiguous array arguments (sympy matrices)
    -- Non-contiguous array arguments (sympy matrices)
    -- ccode must raise an error when it encounters something that can not be
    -  translated into c. ccode(integrate(sin(x)/x, x)) does not make sense.
    -- Complex numbers as input and output
    -- A default complex datatype
    -- Include extra information in the header: date, user, hostname, sha1
    -  hash, ...
    -- Fortran 77
    -- C++
    -- Python
    -- ...
    -
    -"""
    -
    -
    -import os
    -from io import StringIO
    -
    -from sympy import __version__ as sympy_version
    -from sympy.core import Symbol, S, Expr, Tuple, Equality, Function
    -from sympy.core.compatibility import is_sequence
    -from sympy.printing.codeprinter import AssignmentError
    -from sympy.printing.ccode import ccode, CCodePrinter
    -from sympy.printing.fcode import fcode, FCodePrinter
    -from sympy.tensor import Idx, Indexed, IndexedBase
    -
    -
    -__all__ = [
    -    # description of routines
    -    "Routine", "DataType", "default_datatypes", "get_default_datatype",
    -    "Argument", "InputArgument", "Result",
    -    # routines -> code
    -    "CodeGen", "CCodeGen", "FCodeGen",
    -    # friendly functions
    -    "codegen",
    -]
    -
    -
    -#
    -# Description of routines
    -#
    -
    -
    -
    [docs]class Routine(object): - """Generic description of an evaluation routine for a set of sympy expressions. - - A CodeGen class can translate instances of this class into C/Fortran/... - code. The routine specification covers all the features present in these - languages. The CodeGen part must raise an exception when certain features - are not present in the target language. For example, multiple return - values are possible in Python, but not in C or Fortran. Another example: - Fortran and Python support complex numbers, while C does not. - """ - def __init__(self, name, expr, argument_sequence=None): - """Initialize a Routine instance. - - ``name`` - A string with the name of this routine in the generated code - ``expr`` - The sympy expression that the Routine instance will represent. If - given a list or tuple of expressions, the routine will be - considered to have multiple return values. - ``argument_sequence`` - Optional list/tuple containing arguments for the routine in a - preferred order. If omitted, arguments will be ordered - alphabetically, but with all input aguments first, and then output - or in-out arguments. - - A decision about whether to use output arguments or return values, - is made depending on the mathematical expressions. For an expression - of type Equality, the left hand side is made into an OutputArgument - (or an InOutArgument if appropriate). Else, the calculated - expression is the return values of the routine. - - A tuple of exressions can be used to create a routine with both - return value(s) and output argument(s). - - """ - arg_list = [] - - if is_sequence(expr): - if not expr: - raise ValueError("No expression given") - expressions = Tuple(*expr) - else: - expressions = Tuple(expr) - - # local variables - local_vars = set([i.label for i in expressions.atoms(Idx)]) - - # symbols that should be arguments - symbols = expressions.atoms(Symbol) - local_vars - - # Decide whether to use output argument or return value - return_val = [] - output_args = [] - for expr in expressions: - if isinstance(expr, Equality): - out_arg = expr.lhs - expr = expr.rhs - if isinstance(out_arg, Indexed): - dims = tuple([ (S.Zero, dim - 1) for dim in out_arg.shape]) - symbol = out_arg.base.label - elif isinstance(out_arg, Symbol): - dims = [] - symbol = out_arg - else: - raise CodeGenError( - "Only Indexed or Symbol can define output arguments") - - if expr.has(symbol): - output_args.append( - InOutArgument(symbol, out_arg, expr, dimensions=dims)) - else: - output_args.append(OutputArgument( - symbol, out_arg, expr, dimensions=dims)) - - # avoid duplicate arguments - symbols.remove(symbol) - else: - return_val.append(Result(expr)) - - # setup input argument list - array_symbols = {} - for array in expressions.atoms(Indexed): - array_symbols[array.base.label] = array - - for symbol in sorted(symbols, key=str): - if symbol in array_symbols: - dims = [] - array = array_symbols[symbol] - for dim in array.shape: - dims.append((S.Zero, dim - 1)) - metadata = {'dimensions': dims} - else: - metadata = {} - - arg_list.append(InputArgument(symbol, **metadata)) - - output_args.sort(key=lambda x: str(x.name)) - arg_list.extend(output_args) - - if argument_sequence is not None: - # if the user has supplied IndexedBase instances, we'll accept that - new_sequence = [] - for arg in argument_sequence: - if isinstance(arg, IndexedBase): - new_sequence.append(arg.label) - else: - new_sequence.append(arg) - argument_sequence = new_sequence - - missing = [x for x in arg_list if x.name not in argument_sequence] - if missing: - raise CodeGenArgumentListError("Argument list didn't specify: %s" % - ", ".join([str(m.name) for m in missing]), missing) - - # create redundant arguments to produce the requested sequence - name_arg_dict = dict([(x.name, x) for x in arg_list]) - new_args = [] - for symbol in argument_sequence: - try: - new_args.append(name_arg_dict[symbol]) - except KeyError: - new_args.append(InputArgument(symbol)) - arg_list = new_args - - self.name = name - self.arguments = arg_list - self.results = return_val - self.local_vars = local_vars - - @property -
    [docs] def variables(self): - """Returns a set containing all variables possibly used in this routine. - - For routines with unnamed return values, the dummies that may or may - not be used will be included in the set. - """ - v = set(self.local_vars) - for arg in self.arguments: - v.add(arg.name) - for res in self.results: - v.add(res.result_var) - return v -
    - @property -
    [docs] def result_variables(self): - """Returns a list of OutputArgument, InOutArgument and Result. - - If return values are present, they are at the end ot the list. - """ - args = [arg for arg in self.arguments if isinstance( - arg, (OutputArgument, InOutArgument))] - args.extend(self.results) - return args - -
    -
    [docs]class DataType(object): - """Holds strings for a certain datatype in different programming languages.""" - def __init__(self, cname, fname, pyname): - self.cname = cname - self.fname = fname - self.pyname = pyname - -
    -default_datatypes = { - "int": DataType("int", "INTEGER*4", "int"), - "float": DataType("double", "REAL*8", "float") -} - - -
    [docs]def get_default_datatype(expr): - """Derives a decent data type based on the assumptions on the expression.""" - if expr.is_integer: - return default_datatypes["int"] - else: - return default_datatypes["float"] - -
    -class Variable(object): - """Represents a typed variable.""" - - def __init__(self, name, datatype=None, dimensions=None, precision=None): - """Initializes a Variable instance - - name -- must be of class Symbol - datatype -- When not given, the data type will be guessed based - on the assumptions on the symbol argument. - dimension -- If present, the argument is interpreted as an array. - Dimensions must be a sequence containing tuples, i.e. - (lower, upper) bounds for each index of the array - precision -- FIXME - """ - if not isinstance(name, Symbol): - raise TypeError("The first argument must be a sympy symbol.") - if datatype is None: - datatype = get_default_datatype(name) - elif not isinstance(datatype, DataType): - raise TypeError("The (optional) `datatype' argument must be an instance of the DataType class.") - if dimensions and not isinstance(dimensions, (tuple, list)): - raise TypeError( - "The dimension argument must be a sequence of tuples") - - self._name = name - self._datatype = { - 'C': datatype.cname, - 'FORTRAN': datatype.fname, - 'PYTHON': datatype.pyname - } - self.dimensions = dimensions - self.precision = precision - - @property - def name(self): - return self._name - - def get_datatype(self, language): - """Returns the datatype string for the requested langage. - - >>> from sympy import Symbol - >>> from sympy.utilities.codegen import Variable - >>> x = Variable(Symbol('x')) - >>> x.get_datatype('c') - 'double' - >>> x.get_datatype('fortran') - 'REAL*8' - """ - try: - return self._datatype[language.upper()] - except KeyError: - raise CodeGenError("Has datatypes for languages: %s" % - ", ".join(self._datatype)) - - -
    [docs]class Argument(Variable): - """An abstract Argument data structure: a name and a data type. - - This structure is refined in the descendants below. - """ - - def __init__(self, name, datatype=None, dimensions=None, precision=None): - """ See docstring of Variable.__init__ - """ - - Variable.__init__(self, name, datatype, dimensions, precision) - -
    -class InputArgument(Argument): - pass - - -class ResultBase(object): - """Base class for all ``outgoing'' information from a routine - - Objects of this class stores a sympy expression, and a sympy object - representing a result variable that will be used in the generated code - only if necessary. - """ - def __init__(self, expr, result_var): - self.expr = expr - self.result_var = result_var - - -class OutputArgument(Argument, ResultBase): - """OutputArgument are always initialized in the routine - """ - def __init__(self, name, result_var, expr, datatype=None, dimensions=None, precision=None): - """ See docstring of Variable.__init__ - """ - Argument.__init__(self, name, datatype, dimensions, precision) - ResultBase.__init__(self, expr, result_var) - - -class InOutArgument(Argument, ResultBase): - """InOutArgument are never initialized in the routine - """ - - def __init__(self, name, result_var, expr, datatype=None, dimensions=None, precision=None): - """ See docstring of Variable.__init__ - """ - Argument.__init__(self, name, datatype, dimensions, precision) - ResultBase.__init__(self, expr, result_var) - - -
    [docs]class Result(ResultBase): - """An expression for a scalar return value. - - The name result is used to avoid conflicts with the reserved word - 'return' in the python language. It is also shorter than ReturnValue. - - """ - - def __init__(self, expr, datatype=None, precision=None): - """Initialize a (scalar) return value. - - The second argument is optional. When not given, the data type will - be guessed based on the assumptions on the expression argument. - """ - if not isinstance(expr, Expr): - raise TypeError("The first argument must be a sympy expression.") - - temp_var = Variable(Symbol('result_%s' % hash(expr)), - datatype=datatype, dimensions=None, precision=precision) - ResultBase.__init__(self, expr, temp_var.name) - self._temp_variable = temp_var - - def get_datatype(self, language): - return self._temp_variable.get_datatype(language) - - -# -# Transformation of routine objects into code -# -
    -
    [docs]class CodeGen(object): - """Abstract class for the code generators.""" - - def __init__(self, project="project"): - """Initialize a code generator. - - Derived classes will offer more options that affect the generated - code. - """ - self.project = project - -
    [docs] def write(self, routines, prefix, to_files=False, header=True, empty=True): - """Writes all the source code files for the given routines. - - The generate source is returned as a list of (filename, contents) - tuples, or is written to files (see options). Each filename consists - of the given prefix, appended with an appropriate extension. - - ``routines`` - A list of Routine instances to be written - ``prefix`` - The prefix for the output files - ``to_files`` - When True, the output is effectively written to files. - [DEFAULT=False] Otherwise, a list of (filename, contents) - tuples is returned. - ``header`` - When True, a header comment is included on top of each source - file. [DEFAULT=True] - ``empty`` - When True, empty lines are included to structure the source - files. [DEFAULT=True] - - """ - if to_files: - for dump_fn in self.dump_fns: - filename = "%s.%s" % (prefix, dump_fn.extension) - with open(filename, "w") as f: - dump_fn(self, routines, f, prefix, header, empty) - else: - result = [] - for dump_fn in self.dump_fns: - filename = "%s.%s" % (prefix, dump_fn.extension) - contents = StringIO() - dump_fn(self, routines, contents, prefix, header, empty) - result.append((filename, contents.getvalue())) - return result -
    -
    [docs] def dump_code(self, routines, f, prefix, header=True, empty=True): - """Write the code file by calling language specific methods in correct order - - The generated file contains all the definitions of the routines in - low-level code and refers to the header file if appropriate. - - :Arguments: - - routines - A list of Routine instances - f - A file-like object to write the file to - prefix - The filename prefix, used to refer to the proper header file. Only - the basename of the prefix is used. - - :Optional arguments: - - header - When True, a header comment is included on top of each source file. - [DEFAULT=True] - empty - When True, empty lines are included to structure the source files. - [DEFAULT=True] - """ - - code_lines = self._preprocessor_statements(prefix) - - for routine in routines: - if empty: - code_lines.append("\n") - code_lines.extend(self._get_routine_opening(routine)) - code_lines.extend(self._declare_arguments(routine)) - code_lines.extend(self._declare_locals(routine)) - if empty: - code_lines.append("\n") - code_lines.extend(self._call_printer(routine)) - if empty: - code_lines.append("\n") - code_lines.extend(self._get_routine_ending(routine)) - - code_lines = self._indent_code(''.join(code_lines)) - - if header: - code_lines = ''.join(self._get_header() + [code_lines]) - - if code_lines: - f.write(code_lines) - -
    -class CodeGenError(Exception): - pass - - -class CodeGenArgumentListError(Exception): - @property - def missing_args(self): - return self.args[1] - - -header_comment = """Code generated with sympy %(version)s - -See http://www.sympy.org/ for more information. - -This file is part of '%(project)s' -""" - - -
    [docs]class CCodeGen(CodeGen): - """ - Generator for C code - - The .write() method inherited from CodeGen will output a code file and an - inteface file, <prefix>.c and <prefix>.h respectively. - """ - - code_extension = "c" - interface_extension = "h" - - def _get_header(self): - """Writes a common header for the generated files.""" - code_lines = [] - code_lines.append("/" + "*"*78 + '\n') - tmp = header_comment % {"version": sympy_version, - "project": self.project} - for line in tmp.splitlines(): - code_lines.append(" *%s*\n" % line.center(76)) - code_lines.append(" " + "*"*78 + "/\n") - return code_lines - -
    [docs] def get_prototype(self, routine): - """Returns a string for the function prototype for the given routine. - - If the routine has multiple result objects, an CodeGenError is - raised. - - See: http://en.wikipedia.org/wiki/Function_prototype - """ - if len(routine.results) > 1: - raise CodeGenError("C only supports a single or no return value.") - elif len(routine.results) == 1: - ctype = routine.results[0].get_datatype('C') - else: - ctype = "void" - - type_args = [] - for arg in routine.arguments: - name = ccode(arg.name) - if arg.dimensions: - type_args.append((arg.get_datatype('C'), "*%s" % name)) - elif isinstance(arg, ResultBase): - type_args.append((arg.get_datatype('C'), "&%s" % name)) - else: - type_args.append((arg.get_datatype('C'), name)) - arguments = ", ".join([ "%s %s" % t for t in type_args]) - return "%s %s(%s)" % (ctype, routine.name, arguments) -
    - def _preprocessor_statements(self, prefix): - code_lines = [] - code_lines.append("#include \"%s.h\"\n" % os.path.basename(prefix)) - code_lines.append("#include <math.h>\n") - return code_lines - - def _get_routine_opening(self, routine): - prototype = self.get_prototype(routine) - return ["%s {\n" % prototype] - - def _declare_arguments(self, routine): - # arguments are declared in prototype - return [] - - def _declare_locals(self, routine): - # loop variables are declared in loop statement - return [] - - def _call_printer(self, routine): - code_lines = [] - for result in routine.result_variables: - if isinstance(result, Result): - assign_to = None - elif isinstance(result, (OutputArgument, InOutArgument)): - assign_to = result.result_var - - try: - constants, not_c, c_expr = ccode( - result.expr, assign_to=assign_to, human=False) - except AssignmentError: - assign_to = result.result_var - code_lines.append( - "%s %s;\n" % (result.get_datatype('c'), str(assign_to))) - constants, not_c, c_expr = ccode( - result.expr, assign_to=assign_to, human=False) - - for name, value in sorted(constants, key=str): - code_lines.append("double const %s = %s;\n" % (name, value)) - if assign_to: - code_lines.append("%s\n" % c_expr) - else: - code_lines.append(" return %s;\n" % c_expr) - return code_lines - - def _indent_code(self, codelines): - p = CCodePrinter() - return p.indent_code(codelines) - - def _get_routine_ending(self, routine): - return ["}\n"] - -
    [docs] def dump_c(self, routines, f, prefix, header=True, empty=True): - self.dump_code(routines, f, prefix, header, empty)
    - dump_c.extension = code_extension - dump_c.__doc__ = CodeGen.dump_code.__doc__ - -
    [docs] def dump_h(self, routines, f, prefix, header=True, empty=True): - """Writes the C header file. - - This file contains all the function declarations. - - :Arguments: - - routines - A list of Routine instances - f - A file-like object to write the file to - prefix - The filename prefix, used to construct the include guards. - - :Optional arguments: - - header - When True, a header comment is included on top of each source - file. [DEFAULT=True] - empty - When True, empty lines are included to structure the source - files. [DEFAULT=True] - """ - if header: - print(''.join(self._get_header()), file=f) - guard_name = "%s__%s__H" % (self.project.replace( - " ", "_").upper(), prefix.replace("/", "_").upper()) - # include guards - if empty: - print(file=f) - print("#ifndef %s" % guard_name, file=f) - print("#define %s" % guard_name, file=f) - if empty: - print(file=f) - # declaration of the function prototypes - for routine in routines: - prototype = self.get_prototype(routine) - print("%s;" % prototype, file=f) - # end if include guards - if empty: - print(file=f) - print("#endif", file=f) - if empty: - print(file=f)
    - dump_h.extension = interface_extension - - # This list of dump functions is used by CodeGen.write to know which dump - # functions it has to call. - dump_fns = [dump_c, dump_h] - -
    -
    [docs]class FCodeGen(CodeGen): - """ - Generator for Fortran 95 code - - The .write() method inherited from CodeGen will output a code file and an - inteface file, <prefix>.f90 and <prefix>.h respectively. - """ - - code_extension = "f90" - interface_extension = "h" - - def __init__(self, project='project'): - CodeGen.__init__(self, project) - - def _get_symbol(self, s): - """returns the symbol as fcode print it""" - return fcode(s).strip() - - def _get_header(self): - """Writes a common header for the generated files.""" - code_lines = [] - code_lines.append("!" + "*"*78 + '\n') - tmp = header_comment % {"version": sympy_version, - "project": self.project} - for line in tmp.splitlines(): - code_lines.append("!*%s*\n" % line.center(76)) - code_lines.append("!" + "*"*78 + '\n') - return code_lines - - def _preprocessor_statements(self, prefix): - return [] - - def _get_routine_opening(self, routine): - """ - Returns the opening statements of the fortran routine - """ - code_list = [] - if len(routine.results) > 1: - raise CodeGenError( - "Fortran only supports a single or no return value.") - elif len(routine.results) == 1: - result = routine.results[0] - code_list.append(result.get_datatype('fortran')) - code_list.append("function") - else: - code_list.append("subroutine") - - args = ", ".join("%s" % self._get_symbol(arg.name) - for arg in routine.arguments) - - # name of the routine + arguments - code_list.append("%s(%s)\n" % (routine.name, args)) - code_list = [ " ".join(code_list) ] - - code_list.append('implicit none\n') - return code_list - - def _declare_arguments(self, routine): - # argument type declarations - code_list = [] - array_list = [] - scalar_list = [] - for arg in routine.arguments: - - if isinstance(arg, InputArgument): - typeinfo = "%s, intent(in)" % arg.get_datatype('fortran') - elif isinstance(arg, InOutArgument): - typeinfo = "%s, intent(inout)" % arg.get_datatype('fortran') - elif isinstance(arg, OutputArgument): - typeinfo = "%s, intent(out)" % arg.get_datatype('fortran') - else: - raise CodeGenError("Unkown Argument type: %s" % type(arg)) - - fprint = self._get_symbol - - if arg.dimensions: - # fortran arrays start at 1 - dimstr = ", ".join(["%s:%s" % ( - fprint(dim[0] + 1), fprint(dim[1] + 1)) - for dim in arg.dimensions]) - typeinfo += ", dimension(%s)" % dimstr - array_list.append("%s :: %s\n" % (typeinfo, fprint(arg.name))) - else: - scalar_list.append("%s :: %s\n" % (typeinfo, fprint(arg.name))) - - # scalars first, because they can be used in array declarations - code_list.extend(scalar_list) - code_list.extend(array_list) - - return code_list - - def _declare_locals(self, routine): - code_list = [] - for var in sorted(routine.local_vars, key=str): - typeinfo = get_default_datatype(var) - code_list.append("%s :: %s\n" % ( - typeinfo.fname, self._get_symbol(var))) - return code_list - - def _get_routine_ending(self, routine): - """ - Returns the closing statements of the fortran routine - """ - if len(routine.results) == 1: - return ["end function\n"] - else: - return ["end subroutine\n"] - -
    [docs] def get_interface(self, routine): - """Returns a string for the function interface for the given routine and - a single result object, which can be None. - - If the routine has multiple result objects, a CodeGenError is - raised. - - See: http://en.wikipedia.org/wiki/Function_prototype - - """ - prototype = [ "interface\n" ] - prototype.extend(self._get_routine_opening(routine)) - prototype.extend(self._declare_arguments(routine)) - prototype.extend(self._get_routine_ending(routine)) - prototype.append("end interface\n") - - return "".join(prototype) -
    - def _call_printer(self, routine): - declarations = [] - code_lines = [] - for result in routine.result_variables: - if isinstance(result, Result): - assign_to = routine.name - elif isinstance(result, (OutputArgument, InOutArgument)): - assign_to = result.result_var - - constants, not_fortran, f_expr = fcode(result.expr, - assign_to=assign_to, source_format='free', human=False) - - for obj, v in sorted(constants, key=str): - t = get_default_datatype(obj) - declarations.append( - "%s, parameter :: %s = %s\n" % (t.fname, obj, v)) - for obj in sorted(not_fortran, key=str): - t = get_default_datatype(obj) - if isinstance(obj, Function): - name = obj.func - else: - name = obj - declarations.append("%s :: %s\n" % (t.fname, name)) - - code_lines.append("%s\n" % f_expr) - return declarations + code_lines - - def _indent_code(self, codelines): - p = FCodePrinter({'source_format': 'free', 'human': False}) - return p.indent_code(codelines) - -
    [docs] def dump_f95(self, routines, f, prefix, header=True, empty=True): - # check that symbols are unique with ignorecase - for r in routines: - lowercase = set([str(x).lower() for x in r.variables]) - orig_case = set([str(x) for x in r.variables]) - if len(lowercase) < len(orig_case): - raise CodeGenError("Fortran ignores case. Got symbols: %s" % - (", ".join([str(var) for var in r.variables]))) - self.dump_code(routines, f, prefix, header, empty)
    - dump_f95.extension = code_extension - dump_f95.__doc__ = CodeGen.dump_code.__doc__ - -
    [docs] def dump_h(self, routines, f, prefix, header=True, empty=True): - """Writes the interface to a header file. - - This file contains all the function declarations. - - :Arguments: - - routines - A list of Routine instances - f - A file-like object to write the file to - prefix - The filename prefix - - :Optional arguments: - - header - When True, a header comment is included on top of each source - file. [DEFAULT=True] - empty - When True, empty lines are included to structure the source - files. [DEFAULT=True] - """ - if header: - print(''.join(self._get_header()), file=f) - if empty: - print(file=f) - # declaration of the function prototypes - for routine in routines: - prototype = self.get_interface(routine) - f.write(prototype) - if empty: - print(file=f)
    - dump_h.extension = interface_extension - - # This list of dump functions is used by CodeGen.write to know which dump - # functions it has to call. - dump_fns = [dump_f95, dump_h] - -
    -def get_code_generator(language, project): - CodeGenClass = {"C": CCodeGen, "F95": FCodeGen}.get(language.upper()) - if CodeGenClass is None: - raise ValueError("Language '%s' is not supported." % language) - return CodeGenClass(project) - - -# -# Friendly functions -# - - -
    [docs]def codegen( - name_expr, language, prefix, project="project", to_files=False, header=True, empty=True, - argument_sequence=None): - """Write source code for the given expressions in the given language. - - :Mandatory Arguments: - - ``name_expr`` - A single (name, expression) tuple or a list of (name, expression) - tuples. Each tuple corresponds to a routine. If the expression is an - equality (an instance of class Equality) the left hand side is - considered an output argument. - ``language`` - A string that indicates the source code language. This is case - insensitive. For the moment, only 'C' and 'F95' is supported. - ``prefix`` - A prefix for the names of the files that contain the source code. - Proper (language dependent) suffixes will be appended. - - :Optional Arguments: - - ``project`` - A project name, used for making unique preprocessor instructions. - [DEFAULT="project"] - ``to_files`` - When True, the code will be written to one or more files with the given - prefix, otherwise strings with the names and contents of these files - are returned. [DEFAULT=False] - ``header`` - When True, a header is written on top of each source file. - [DEFAULT=True] - ``empty`` - When True, empty lines are used to structure the code. [DEFAULT=True] - ``argument_sequence`` - sequence of arguments for the routine in a preferred order. A - CodeGenError is raised if required arguments are missing. Redundant - arguments are used without warning. - - If omitted, arguments will be ordered alphabetically, but with all - input aguments first, and then output or in-out arguments. - - >>> from sympy import symbols - >>> from sympy.utilities.codegen import codegen - >>> from sympy.abc import x, y, z - >>> [(c_name, c_code), (h_name, c_header)] = codegen( - ... ("f", x+y*z), "C", "test", header=False, empty=False) - >>> print(c_name) - test.c - >>> print(c_code, end=' ') - #include "test.h" - #include <math.h> - double f(double x, double y, double z) { - return x + y*z; - } - >>> print(h_name) - test.h - >>> print(c_header, end=' ') - #ifndef PROJECT__TEST__H - #define PROJECT__TEST__H - double f(double x, double y, double z); - #endif - - """ - - # Initialize the code generator. - code_gen = get_code_generator(language, project) - - # Construct the routines based on the name_expression pairs. - # mainly the input arguments require some work - routines = [] - if isinstance(name_expr[0], str): - # single tuple is given, turn it into a singleton list with a tuple. - name_expr = [name_expr] - - for name, expr in name_expr: - routines.append(Routine(name, expr, argument_sequence)) - - # Write the code. - return code_gen.write(routines, prefix, to_files, header, empty)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/utilities/decorator.html b/dev-py3k/_modules/sympy/utilities/decorator.html deleted file mode 100644 index add722acd20..00000000000 --- a/dev-py3k/_modules/sympy/utilities/decorator.html +++ /dev/null @@ -1,201 +0,0 @@ - - - - - - - - - - sympy.utilities.decorator — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.utilities.decorator

    -from sympy.core.decorators import wraps
    -from sympy.core.compatibility import iterable
    -
    -
    -
    [docs]def threaded_factory(func, use_add): - """A factory for ``threaded`` decorators. """ - from sympy.core import sympify - from sympy.matrices import Matrix - - @wraps(func) - def threaded_func(expr, *args, **kwargs): - if isinstance(expr, Matrix): - return expr.applyfunc(lambda f: func(f, *args, **kwargs)) - elif iterable(expr): - return expr.__class__([ func(f, *args, **kwargs) for f in expr ]) - else: - expr = sympify(expr) - - if use_add and expr.is_Add: - return expr.__class__(*[ func(f, *args, **kwargs) for f in expr.args ]) - elif expr.is_Relational: - return expr.__class__(func(expr.lhs, *args, **kwargs), - func(expr.rhs, *args, **kwargs)) - else: - return func(expr, *args, **kwargs) - - return threaded_func - -
    -
    [docs]def threaded(func): - """Apply ``func`` to sub--elements of an object, including :class:`Add`. - - This decorator is intended to make it uniformly possible to apply a - function to all elements of composite objects, e.g. matrices, lists, tuples - and other iterable containers, or just expressions. - - This version of :func:`threaded` decorator allows threading over - elements of :class:`Add` class. If this behavior is not desirable - use :func:`xthreaded` decorator. - - Functions using this decorator must have the following signature:: - - @threaded - def function(expr, *args, **kwargs): - - """ - return threaded_factory(func, True) - -
    -
    [docs]def xthreaded(func): - """Apply ``func`` to sub--elements of an object, excluding :class:`Add`. - - This decorator is intended to make it uniformly possible to apply a - function to all elements of composite objects, e.g. matrices, lists, tuples - and other iterable containers, or just expressions. - - This version of :func:`threaded` decorator disallows threading over - elements of :class:`Add` class. If this behavior is not desirable - use :func:`threaded` decorator. - - Functions using this decorator must have the following signature:: - - @xthreaded - def function(expr, *args, **kwargs): - - """ - return threaded_factory(func, False) - -
    -
    [docs]def conserve_mpmath_dps(func): - """After the function finishes, resets the value of mpmath.mp.dps to - the value it had before the function was run.""" - import functools - from sympy import mpmath - - def func_wrapper(): - dps = mpmath.mp.dps - try: - func() - finally: - mpmath.mp.dps = dps - - func_wrapper = functools.update_wrapper(func_wrapper, func) - return func_wrapper
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/utilities/iterables.html b/dev-py3k/_modules/sympy/utilities/iterables.html deleted file mode 100644 index 605a9843fb1..00000000000 --- a/dev-py3k/_modules/sympy/utilities/iterables.html +++ /dev/null @@ -1,2149 +0,0 @@ - - - - - - - - - - sympy.utilities.iterables — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.utilities.iterables

    -from collections import defaultdict
    -import random
    -from operator import gt
    -
    -from sympy.core.decorators import deprecated
    -from sympy.core import Basic, C
    -from sympy.utilities.exceptions import SymPyDeprecationWarning
    -
    -# this is the logical location of these functions
    -from sympy.core.compatibility import (
    -    as_int, combinations, combinations_with_replacement,
    -    default_sort_key, is_sequence, iterable, permutations,
    -    product as cartes, ordered, next, bin
    -)
    -
    -
    -
    [docs]def flatten(iterable, levels=None, cls=None): - """ - Recursively denest iterable containers. - - >>> from sympy.utilities.iterables import flatten - - >>> flatten([1, 2, 3]) - [1, 2, 3] - >>> flatten([1, 2, [3]]) - [1, 2, 3] - >>> flatten([1, [2, 3], [4, 5]]) - [1, 2, 3, 4, 5] - >>> flatten([1.0, 2, (1, None)]) - [1.0, 2, 1, None] - - If you want to denest only a specified number of levels of - nested containers, then set ``levels`` flag to the desired - number of levels:: - - >>> ls = [[(-2, -1), (1, 2)], [(0, 0)]] - - >>> flatten(ls, levels=1) - [(-2, -1), (1, 2), (0, 0)] - - If cls argument is specified, it will only flatten instances of that - class, for example: - - >>> from sympy.core import Basic - >>> class MyOp(Basic): - ... pass - ... - >>> flatten([MyOp(1, MyOp(2, 3))], cls=MyOp) - [1, 2, 3] - - adapted from http://kogs-www.informatik.uni-hamburg.de/~meine/python_tricks - """ - if levels is not None: - if not levels: - return iterable - elif levels > 0: - levels -= 1 - else: - raise ValueError( - "expected non-negative number of levels, got %s" % levels) - - if cls is None: - reducible = lambda x: is_sequence(x, set) - else: - reducible = lambda x: isinstance(x, cls) - - result = [] - - for el in iterable: - if reducible(el): - if hasattr(el, 'args'): - el = el.args - result.extend(flatten(el, levels=levels, cls=cls)) - else: - result.append(el) - - return result - -
    -
    [docs]def unflatten(iter, n=2): - """Group ``iter`` into tuples of length ``n``. Raise an error if - the length of ``iter`` is not a multiple of ``n``. - """ - if n < 1 or len(iter) % n: - raise ValueError('iter length is not a multiple of %i' % n) - return list(zip(*(iter[i::n] for i in range(n)))) - -
    -
    [docs]def reshape(seq, how): - """Reshape the sequence according to the template in ``how``. - - Examples - ======== - - >>> from sympy.utilities import reshape - >>> seq = list(range(1, 9)) - - >>> reshape(seq, [4]) # lists of 4 - [[1, 2, 3, 4], [5, 6, 7, 8]] - - >>> reshape(seq, (4,)) # tuples of 4 - [(1, 2, 3, 4), (5, 6, 7, 8)] - - >>> reshape(seq, (2, 2)) # tuples of 4 - [(1, 2, 3, 4), (5, 6, 7, 8)] - - >>> reshape(seq, (2, [2])) # (i, i, [i, i]) - [(1, 2, [3, 4]), (5, 6, [7, 8])] - - >>> reshape(seq, ((2,), [2])) # etc.... - [((1, 2), [3, 4]), ((5, 6), [7, 8])] - - >>> reshape(seq, (1, [2], 1)) - [(1, [2, 3], 4), (5, [6, 7], 8)] - - >>> reshape(tuple(seq), ([[1], 1, (2,)],)) - (([[1], 2, (3, 4)],), ([[5], 6, (7, 8)],)) - - >>> reshape(tuple(seq), ([1], 1, (2,))) - (([1], 2, (3, 4)), ([5], 6, (7, 8))) - - >>> reshape(list(range(12)), [2, [3], set([2]), (1, (3,), 1)]) - [[0, 1, [2, 3, 4], set([5, 6]), (7, (8, 9, 10), 11)]] - - """ - m = sum(flatten(how)) - n, rem = divmod(len(seq), m) - if m < 0 or rem: - raise ValueError('template must sum to positive number ' - 'that divides the length of the sequence') - i = 0 - container = type(how) - rv = [None]*n - for k in range(len(rv)): - rv[k] = [] - for hi in how: - if type(hi) is int: - rv[k].extend(seq[i: i + hi]) - i += hi - else: - n = sum(flatten(hi)) - hi_type = type(hi) - rv[k].append(hi_type(reshape(seq[i: i + n], hi)[0])) - i += n - rv[k] = container(rv[k]) - return type(seq)(rv) - -
    -
    [docs]def group(seq, multiple=True): - """ - Splits a sequence into a list of lists of equal, adjacent elements. - - Examples - ======== - - >>> from sympy.utilities.iterables import group - - >>> group([1, 1, 1, 2, 2, 3]) - [[1, 1, 1], [2, 2], [3]] - >>> group([1, 1, 1, 2, 2, 3], multiple=False) - [(1, 3), (2, 2), (3, 1)] - >>> group([1, 1, 3, 2, 2, 1], multiple=False) - [(1, 2), (3, 1), (2, 2), (1, 1)] - - See Also - ======== - multiset - """ - if not seq: - return [] - - current, groups = [seq[0]], [] - - for elem in seq[1:]: - if elem == current[-1]: - current.append(elem) - else: - groups.append(current) - current = [elem] - - groups.append(current) - - if multiple: - return groups - - for i, current in enumerate(groups): - groups[i] = (current[0], len(current)) - - return groups - -
    -
    [docs]def multiset(seq): - """Return the hashable sequence in multiset form with values being the - multiplicity of the item in the sequence. - - Examples - ======== - - >>> from sympy.utilities.iterables import multiset, group - >>> multiset('mississippi') - {'i': 4, 'm': 1, 'p': 2, 's': 4} - - See Also - ======== - group - """ - rv = defaultdict(int) - for s in seq: - rv[s] += 1 - return dict(rv) - -
    -
    [docs]def postorder_traversal(node, keys=None): - """ - Do a postorder traversal of a tree. - - This generator recursively yields nodes that it has visited in a postorder - fashion. That is, it descends through the tree depth-first to yield all of - a node's children's postorder traversal before yielding the node itself. - - Parameters - ========== - - node : sympy expression - The expression to traverse. - keys : (default None) sort key(s) - The key(s) used to sort args of Basic objects. When None, args of Basic - objects are processed in arbitrary order. If key is defined, it will - be passed along to ordered() as the only key(s) to use to sort the - arguments; if ``key`` is simply True then the default keys of - ``ordered`` will be used (node count and default_sort_key). - - Yields - ====== - subtree : sympy expression - All of the subtrees in the tree. - - Examples - ======== - - >>> from sympy.utilities.iterables import postorder_traversal - >>> from sympy.abc import w, x, y, z - - The nodes are returned in the order that they are encountered unless key - is given; simply passing key=True will guarantee that the traversal is - unique. - - >>> list(postorder_traversal(w + (x + y)*z)) # doctest: +SKIP - [z, y, x, x + y, z*(x + y), w, w + z*(x + y)] - >>> list(postorder_traversal(w + (x + y)*z, keys=True)) - [w, z, x, y, x + y, z*(x + y), w + z*(x + y)] - - - """ - if isinstance(node, Basic): - args = node.args - if keys: - if keys != True: - args = ordered(args, keys, default=False) - else: - args = ordered(args) - for arg in args: - for subtree in postorder_traversal(arg, keys): - yield subtree - elif iterable(node): - for item in node: - for subtree in postorder_traversal(item, keys): - yield subtree - yield node - -
    -
    [docs]def interactive_traversal(expr): - """Traverse a tree asking a user which branch to choose. """ - from sympy.printing import pprint - - RED, BRED = '\033[0;31m', '\033[1;31m' - GREEN, BGREEN = '\033[0;32m', '\033[1;32m' - YELLOW, BYELLOW = '\033[0;33m', '\033[1;33m' - BLUE, BBLUE = '\033[0;34m', '\033[1;34m' - MAGENTA, BMAGENTA = '\033[0;35m', '\033[1;35m' - CYAN, BCYAN = '\033[0;36m', '\033[1;36m' - END = '\033[0m' - - def cprint(*args): - print("".join(map(str, args)) + END) - - def _interactive_traversal(expr, stage): - if stage > 0: - print() - - cprint("Current expression (stage ", BYELLOW, stage, END, "):") - print(BCYAN) - pprint(expr) - print(END) - - if isinstance(expr, Basic): - if expr.is_Add: - args = expr.as_ordered_terms() - elif expr.is_Mul: - args = expr.as_ordered_factors() - else: - args = expr.args - elif hasattr(expr, "__iter__"): - args = list(expr) - else: - return expr - - n_args = len(args) - - if not n_args: - return expr - - for i, arg in enumerate(args): - cprint(GREEN, "[", BGREEN, i, GREEN, "] ", BLUE, type(arg), END) - pprint(arg) - print() - - if n_args == 1: - choices = '0' - else: - choices = '0-%d' % (n_args - 1) - - try: - choice = input("Your choice [%s,f,l,r,d,?]: " % choices) - except EOFError: - result = expr - print() - else: - if choice == '?': - cprint(RED, "%s - select subexpression with the given index" % - choices) - cprint(RED, "f - select the first subexpression") - cprint(RED, "l - select the last subexpression") - cprint(RED, "r - select a random subexpression") - cprint(RED, "d - done\n") - - result = _interactive_traversal(expr, stage) - elif choice in ['d', '']: - result = expr - elif choice == 'f': - result = _interactive_traversal(args[0], stage + 1) - elif choice == 'l': - result = _interactive_traversal(args[-1], stage + 1) - elif choice == 'r': - result = _interactive_traversal(random.choice(args), stage + 1) - else: - try: - choice = int(choice) - except ValueError: - cprint(BRED, - "Choice must be a number in %s range\n" % choices) - result = _interactive_traversal(expr, stage) - else: - if choice < 0 or choice >= n_args: - cprint(BRED, "Choice must be in %s range\n" % choices) - result = _interactive_traversal(expr, stage) - else: - result = _interactive_traversal(args[choice], stage + 1) - - return result - - return _interactive_traversal(expr, 0) - -
    -
    [docs]def ibin(n, bits=0, str=False): - """Return a list of length ``bits`` corresponding to the binary value - of ``n`` with small bits to the right (last). If bits is omitted, the - length will be the number required to represent ``n``. If the bits are - desired in reversed order, use the [::-1] slice of the returned list. - - If a sequence of all bits-length lists starting from [0, 0,..., 0] - through [1, 1, ..., 1] are desired, pass a non-integer for bits, e.g. - 'all'. - - If the bit *string* is desired pass ``str=True``. - - Examples - ======== - - >>> from sympy.utilities.iterables import ibin, variations - >>> ibin(2) - [1, 0] - >>> ibin(2, 4) - [0, 0, 1, 0] - >>> ibin(2, 4)[::-1] - [0, 1, 0, 0] - - If all lists corresponding to 0 to 2**n - 1, pass a non-integer - for bits: - - >>> bits = 2 - >>> for i in ibin(2, 'all'): - ... print(i) - (0, 0) - (0, 1) - (1, 0) - (1, 1) - - If a bit string is desired of a given length, use str=True: - - >>> n = 123 - >>> bits = 10 - >>> ibin(n, bits, str=True) - '0001111011' - >>> ibin(n, bits, str=True)[::-1] # small bits left - '1101111000' - >>> list(ibin(3, 'all', str=True)) - ['000', '001', '010', '011', '100', '101', '110', '111'] - - """ - if not str: - try: - bits = as_int(bits) - return [1 if i == "1" else 0 for i in bin(n)[2:].rjust(bits, "0")] - except ValueError: - return variations(list(range(2)), n, repetition=True) - else: - try: - bits = as_int(bits) - return bin(n)[2:].rjust(bits, "0") - except ValueError: - return (bin(i)[2:].rjust(n, "0") for i in range(2**n)) - -
    -
    [docs]def variations(seq, n, repetition=False): - """Returns a generator of the n-sized variations of ``seq`` (size N). - ``repetition`` controls whether items in ``seq`` can appear more than once; - - Examples - ======== - - variations(seq, n) will return N! / (N - n)! permutations without - repetition of seq's elements: - - >>> from sympy.utilities.iterables import variations - >>> list(variations([1, 2], 2)) - [(1, 2), (2, 1)] - - variations(seq, n, True) will return the N**n permutations obtained - by allowing repetition of elements: - - >>> list(variations([1, 2], 2, repetition=True)) - [(1, 1), (1, 2), (2, 1), (2, 2)] - - If you ask for more items than are in the set you get the empty set unless - you allow repetitions: - - >>> list(variations([0, 1], 3, repetition=False)) - [] - >>> list(variations([0, 1], 3, repetition=True))[:4] - [(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1)] - - See Also - ======== - - sympy.core.compatibility.permutations - sympy.core.compatibility.product - """ - from sympy.core.compatibility import permutations, product - - if not repetition: - seq = tuple(seq) - if len(seq) < n: - return - for i in permutations(seq, n): - yield i - else: - if n == 0: - yield () - else: - for i in product(seq, repeat=n): - yield i - -
    -
    [docs]def subsets(seq, k=None, repetition=False): - """Generates all k-subsets (combinations) from an n-element set, seq. - - A k-subset of an n-element set is any subset of length exactly k. The - number of k-subsets of an n-element set is given by binomial(n, k), - whereas there are 2**n subsets all together. If k is None then all - 2**n subsets will be returned from shortest to longest. - - Examples - ======== - - >>> from sympy.utilities.iterables import subsets - - subsets(seq, k) will return the n!/k!/(n - k)! k-subsets (combinations) - without repetition, i.e. once an item has been removed, it can no - longer be "taken": - - >>> list(subsets([1, 2], 2)) - [(1, 2)] - >>> list(subsets([1, 2])) - [(), (1,), (2,), (1, 2)] - >>> list(subsets([1, 2, 3], 2)) - [(1, 2), (1, 3), (2, 3)] - - - subsets(seq, k, repetition=True) will return the (n - 1 + k)!/k!/(n - 1)! - combinations *with* repetition: - - >>> list(subsets([1, 2], 2, repetition=True)) - [(1, 1), (1, 2), (2, 2)] - - If you ask for more items than are in the set you get the empty set unless - you allow repetitions: - - >>> list(subsets([0, 1], 3, repetition=False)) - [] - >>> list(subsets([0, 1], 3, repetition=True)) - [(0, 0, 0), (0, 0, 1), (0, 1, 1), (1, 1, 1)] - - """ - if k is None: - for k in range(len(seq) + 1): - for i in subsets(seq, k, repetition): - yield i - else: - if not repetition: - for i in combinations(seq, k): - yield i - else: - for i in combinations_with_replacement(seq, k): - yield i - -
    -
    [docs]def numbered_symbols(prefix='x', cls=None, start=0, *args, **assumptions): - """ - Generate an infinite stream of Symbols consisting of a prefix and - increasing subscripts. - - Parameters - ========== - - prefix : str, optional - The prefix to use. By default, this function will generate symbols of - the form "x0", "x1", etc. - - cls : class, optional - The class to use. By default, it uses Symbol, but you can also use Wild or Dummy. - - start : int, optional - The start number. By default, it is 0. - - Returns - ======= - - sym : Symbol - The subscripted symbols. - """ - if 'dummy' in assumptions: - SymPyDeprecationWarning( - feature="'dummy=True' to create numbered Dummy symbols", - useinstead="cls=Dummy", - issue=3378, deprecated_since_version="0.7.0", - ).warn() - if assumptions.pop('dummy'): - cls = C.Dummy - - if cls is None: - # We can't just make the default cls=C.Symbol because it isn't - # imported yet. - cls = C.Symbol - - while True: - name = '%s%s' % (prefix, start) - yield cls(name, *args, **assumptions) - start += 1 - -
    -
    [docs]def capture(func): - """Return the printed output of func(). - - `func` should be a function without arguments that produces output with - print statements. - - >>> from sympy.utilities.iterables import capture - >>> from sympy import pprint - >>> from sympy.abc import x - >>> def foo(): - ... print('hello world!') - ... - >>> 'hello' in capture(foo) # foo, not foo() - True - >>> capture(lambda: pprint(2/x)) - '2\\n-\\nx\\n' - - """ - import io - import sys - - stdout = sys.stdout - sys.stdout = file = io.StringIO() - try: - func() - finally: - sys.stdout = stdout - return file.getvalue() - -
    -
    [docs]def sift(seq, keyfunc): - """ - Sift the sequence, ``seq`` into a dictionary according to keyfunc. - - OUTPUT: each element in expr is stored in a list keyed to the value - of keyfunc for the element. - - Examples - ======== - - >>> from sympy.utilities import sift - >>> from sympy.abc import x, y - >>> from sympy import sqrt, exp - - >>> sift(list(range(5)), lambda x: x % 2) - {0: [0, 2, 4], 1: [1, 3]} - - sift() returns a defaultdict() object, so any key that has no matches will - give []. - - >>> sift([x], lambda x: x.is_commutative) - {True: [x]} - >>> _[False] - [] - - Sometimes you won't know how many keys you will get: - - >>> sift([sqrt(x), exp(x), (y**x)**2], - ... lambda x: x.as_base_exp()[0]) - {E: [exp(x)], x: [sqrt(x)], y: [y**(2*x)]} - - If you need to sort the sifted items it might be better to use - ``ordered`` which can economically apply multiple sort keys - to a squence while sorting. - - See Also - ======== - ordered - """ - m = defaultdict(list) - for i in seq: - m[keyfunc(i)].append(i) - return m - -
    -
    [docs]def take(iter, n): - """Return ``n`` items from ``iter`` iterator. """ - return [ value for _, value in zip(range(n), iter) ] - -
    -
    [docs]def dict_merge(*dicts): - """Merge dictionaries into a single dictionary. """ - merged = {} - - for dict in dicts: - merged.update(dict) - - return merged - -
    -
    [docs]def common_prefix(*seqs): - """Return the subsequence that is a common start of sequences in ``seqs``. - - >>> from sympy.utilities.iterables import common_prefix - >>> common_prefix(list(range(3))) - [0, 1, 2] - >>> common_prefix(list(range(3)), list(range(4))) - [0, 1, 2] - >>> common_prefix([1, 2, 3], [1, 2, 5]) - [1, 2] - >>> common_prefix([1, 2, 3], [1, 3, 5]) - [1] - """ - if any(not s for s in seqs): - return [] - elif len(seqs) == 1: - return seqs[0] - i = 0 - for i in range(min(len(s) for s in seqs)): - if not all(seqs[j][i] == seqs[0][i] for j in range(len(seqs))): - break - else: - i += 1 - return seqs[0][:i] - -
    -
    [docs]def common_suffix(*seqs): - """Return the subsequence that is a common ending of sequences in ``seqs``. - - >>> from sympy.utilities.iterables import common_suffix - >>> common_suffix(list(range(3))) - [0, 1, 2] - >>> common_suffix(list(range(3)), list(range(4))) - [] - >>> common_suffix([1, 2, 3], [9, 2, 3]) - [2, 3] - >>> common_suffix([1, 2, 3], [9, 7, 3]) - [3] - """ - - if any(not s for s in seqs): - return [] - elif len(seqs) == 1: - return seqs[0] - i = 0 - for i in range(-1, -min(len(s) for s in seqs) - 1, -1): - if not all(seqs[j][i] == seqs[0][i] for j in range(len(seqs))): - break - else: - i -= 1 - if i == -1: - return [] - else: - return seqs[0][i + 1:] - -
    -
    [docs]def prefixes(seq): - """ - Generate all prefixes of a sequence. - - Examples - ======== - - >>> from sympy.utilities.iterables import prefixes - - >>> list(prefixes([1,2,3,4])) - [[1], [1, 2], [1, 2, 3], [1, 2, 3, 4]] - - """ - n = len(seq) - - for i in range(n): - yield seq[:i + 1] - -
    -
    [docs]def postfixes(seq): - """ - Generate all postfixes of a sequence. - - Examples - ======== - - >>> from sympy.utilities.iterables import postfixes - - >>> list(postfixes([1,2,3,4])) - [[4], [3, 4], [2, 3, 4], [1, 2, 3, 4]] - - """ - n = len(seq) - - for i in range(n): - yield seq[n - i - 1:] - -
    -
    [docs]def topological_sort(graph, key=None): - r""" - Topological sort of graph's vertices. - - Parameters - ========== - - ``graph`` : ``tuple[list, list[tuple[T, T]]`` - A tuple consisting of a list of vertices and a list of edges of - a graph to be sorted topologically. - - ``key`` : ``callable[T]`` (optional) - Ordering key for vertices on the same level. By default the natural - (e.g. lexicographic) ordering is used (in this case the base type - must implement ordering relations). - - Examples - ======== - - Consider a graph:: - - +---+ +---+ +---+ - | 7 |\ | 5 | | 3 | - +---+ \ +---+ +---+ - | _\___/ ____ _/ | - | / \___/ \ / | - V V V V | - +----+ +---+ | - | 11 | | 8 | | - +----+ +---+ | - | | \____ ___/ _ | - | \ \ / / \ | - V \ V V / V V - +---+ \ +---+ | +----+ - | 2 | | | 9 | | | 10 | - +---+ | +---+ | +----+ - \________/ - - where vertices are integers. This graph can be encoded using - elementary Python's data structures as follows:: - - >>> V = [2, 3, 5, 7, 8, 9, 10, 11] - >>> E = [(7, 11), (7, 8), (5, 11), (3, 8), (3, 10), - ... (11, 2), (11, 9), (11, 10), (8, 9)] - - To compute a topological sort for graph ``(V, E)`` issue:: - - >>> from sympy.utilities.iterables import topological_sort - - >>> topological_sort((V, E)) - [3, 5, 7, 8, 11, 2, 9, 10] - - If specific tie breaking approach is needed, use ``key`` parameter:: - - >>> topological_sort((V, E), key=lambda v: -v) - [7, 5, 11, 3, 10, 8, 9, 2] - - Only acyclic graphs can be sorted. If the input graph has a cycle, - then :py:exc:`ValueError` will be raised:: - - >>> topological_sort((V, E + [(10, 7)])) - Traceback (most recent call last): - ... - ValueError: cycle detected - - .. seealso:: http://en.wikipedia.org/wiki/Topological_sorting - - """ - V, E = graph - - L = [] - S = set(V) - E = list(E) - - for v, u in E: - S.discard(u) - - if key is None: - key = lambda value: value - - S = sorted(S, key=key, reverse=True) - - while S: - node = S.pop() - L.append(node) - - for u, v in list(E): - if u == node: - E.remove((u, v)) - - for _u, _v in E: - if v == _v: - break - else: - kv = key(v) - - for i, s in enumerate(S): - ks = key(s) - - if kv > ks: - S.insert(i, v) - break - else: - S.append(v) - - if E: - raise ValueError("cycle detected") - else: - return L - -
    -
    [docs]def rotate_left(x, y): - """ - Left rotates a list x by the number of steps specified - in y. - - Examples - ======== - - >>> from sympy.utilities.iterables import rotate_left - >>> a = [0, 1, 2] - >>> rotate_left(a, 1) - [1, 2, 0] - """ - if len(x) == 0: - return [] - y = y % len(x) - return x[y:] + x[:y] - -
    -
    [docs]def rotate_right(x, y): - """ - Left rotates a list x by the number of steps specified - in y. - - Examples - ======== - - >>> from sympy.utilities.iterables import rotate_right - >>> a = [0, 1, 2] - >>> rotate_right(a, 1) - [2, 0, 1] - """ - if len(x) == 0: - return [] - y = len(x) - y % len(x) - return x[y:] + x[:y] - -
    -
    [docs]def multiset_combinations(m, n, g=None): - """ - Return the unique combinations of size ``n`` from multiset ``m``. - - Examples - ======== - - >>> from sympy.utilities.iterables import multiset_combinations - >>> from sympy.core.compatibility import combinations - >>> [''.join(i) for i in multiset_combinations('baby', 3)] - ['abb', 'aby', 'bby'] - - >>> def count(f, s): return len(list(f(s, 3))) - - The number of combinations depends on the number of letters; the - number of unique combinations depends on how the letters are - repeated. - - >>> s1 = 'abracadabra' - >>> s2 = 'banana tree' - >>> count(combinations, s1), count(multiset_combinations, s1) - (165, 23) - >>> count(combinations, s2), count(multiset_combinations, s2) - (165, 54) - - """ - if g is None: - if type(m) is dict: - if n > sum(m.values()): - return - g = [[k, m[k]] for k in ordered(m)] - else: - m = list(m) - if n > len(m): - return - try: - m = multiset(m) - g = [(k, m[k]) for k in ordered(m)] - except TypeError: - m = list(ordered(m)) - g = [list(i) for i in group(m, multiple=False)] - del m - if sum(v for k, v in g) < n or not n: - yield [] - else: - for i, (k, v) in enumerate(g): - if v >= n: - yield [k]*n - v = n - 1 - for v in range(min(n, v), 0, -1): - for j in multiset_combinations(None, n - v, g[i + 1:]): - rv = [k]*v + j - if len(rv) == n: - yield rv - -
    -
    [docs]def multiset_permutations(m, k=None, g=None): - """ - Return the unique permutations of multiset ``m``. - - Examples - ======== - - >>> from sympy.utilities.iterables import multiset_permutations - >>> from sympy import factorial - >>> [''.join(i) for i in multiset_permutations('aab')] - ['aab', 'aba', 'baa'] - >>> factorial(len('banana')) - 720 - >>> len(list(multiset_permutations('banana'))) - 60 - """ - size = k - if g is None: - if type(m) is dict: - g = [[k, m[k]] for k in ordered(m)] - else: - m = list(ordered(m)) - g = [list(i) for i in group(m, multiple=False)] - del m - do = [gi for gi in g if gi[1] > 0] - SUM = sum([gi[1] for gi in do]) - if not do or size is not None and (size > SUM or size < 1): - return - elif size == 1: - for k, v in do: - yield [k] - elif len(do) == 1: - k, v = do[0] - v = v if size is None else (size if size <= v else 0) - yield [k for i in range(v)] - elif all(v == 1 for k, v in do): - for p in permutations([k for k, v in do], size): - yield list(p) - else: - size = size if size is not None else SUM - for i, (k, v) in enumerate(do): - do[i][1] -= 1 - for j in multiset_permutations(None, size - 1, do): - if j: - yield [k] + j - do[i][1] += 1 - -
    -def _partition(seq, vector, m=None): - """ - Return the partion of seq as specified by the partition vector. - - Examples - ======== - - >>> from sympy.utilities.iterables import _partition - >>> _partition('abcde', [1, 0, 1, 2, 0]) - [['b', 'e'], ['a', 'c'], ['d']] - - Specifying the number of bins in the partition is optional: - - >>> _partition('abcde', [1, 0, 1, 2, 0], 3) - [['b', 'e'], ['a', 'c'], ['d']] - - The output of _set_partitions can be passed as follows: - - >>> output = (3, [1, 0, 1, 2, 0]) - >>> _partition('abcde', *output) - [['b', 'e'], ['a', 'c'], ['d']] - - See Also - ======== - combinatorics.partitions.Partition.from_rgs() - - """ - if m is None: - m = max(vector) + 1 - elif type(vector) is int: # entered as m, vector - vector, m = m, vector - p = [[] for i in range(m)] - for i, v in enumerate(vector): - p[v].append(seq[i]) - return p - - -def _set_partitions(n): - """Cycle through all partions of n elements, yielding the - current number of partitions, ``m``, and a mutable list, ``q`` - such that element[i] is in part q[i] of the partition. - - NOTE: ``q`` is modified in place and generally should not be changed - between function calls. - - Examples - ======== - - >>> from sympy.utilities.iterables import _set_partitions, _partition - >>> for m, q in _set_partitions(3): - ... print(m, q, _partition('abc', q, m)) - 1 [0, 0, 0] [['a', 'b', 'c']] - 2 [0, 0, 1] [['a', 'b'], ['c']] - 2 [0, 1, 0] [['a', 'c'], ['b']] - 2 [0, 1, 1] [['a'], ['b', 'c']] - 3 [0, 1, 2] [['a'], ['b'], ['c']] - - Notes - ===== - - This algorithm is similar to, and solves the same problem as, - Algorithm 7.2.1.5H, from volume 4A of Knuth's The Art of Computer - Programming. Knuth uses the term "restricted growth string" where - this code refers to a "partition vector". In each case, the meaning is - the same: the value in the ith element of the vector specifies to - which part the ith set element is to be assigned. - - At the lowest level, this code implements an n-digit big-endian - counter (stored in the array q) which is incremented (with carries) to - get the next partition in the sequence. A special twist is that a - digit is constrained to be at most one greater than the maximum of all - the digits to the left of it. The array p maintains this maximum, so - that the code can efficiently decide when a digit can be incremented - in place or whether it needs to be reset to 0 and trigger a carry to - the next digit. The enumeration starts with all the digits 0 (which - corresponds to all the set elements being assigned to the same 0th - part), and ends with 0123...n, which corresponds to each set element - being assigned to a different, singleton, part. - - This routine was rewritten to use 0-based lists while trying to - preserve the beauty and efficiency of the original algorithm. - - Reference - ========= - - Nijenhuis, Albert and Wilf, Herbert. (1978) Combinatorial Algorithms, - 2nd Ed, p 91, algorithm "nexequ". Available online from - http://www.math.upenn.edu/~wilf/website/CombAlgDownld.html (viewed - November 17, 2012). - - """ - p = [0]*n - q = [0]*n - nc = 1 - yield nc, q - while nc != n: - m = n - while 1: - m -= 1 - i = q[m] - if p[i] != 1: - break - q[m] = 0 - i += 1 - q[m] = i - m += 1 - nc += m - n - p[0] += n - m - if i == nc: - p[nc] = 0 - nc += 1 - p[i - 1] -= 1 - p[i] += 1 - yield nc, q - - -
    [docs]def multiset_partitions(multiset, m=None): - """ - Return unique partitions of the given multiset (in list form). - If ``m`` is None, all multisets will be returned, otherwise only - partitions with ``m`` parts will be returned. - - If ``multiset`` is an integer, a range [0, 1, ..., multiset - 1] - will be supplied. - - Examples - ======== - - >>> from sympy.utilities.iterables import multiset_partitions - >>> list(multiset_partitions([1, 2, 3, 4], 2)) - [[[1, 2, 3], [4]], [[1, 2, 4], [3]], [[1, 2], [3, 4]], - [[1, 3, 4], [2]], [[1, 3], [2, 4]], [[1, 4], [2, 3]], - [[1], [2, 3, 4]]] - >>> list(multiset_partitions([1, 2, 3, 4], 1)) - [[[1, 2, 3, 4]]] - - Only unique partitions are returned and these will be returned in a - canonical order regardless of the order of the input: - - >>> a = [1, 2, 2, 1] - >>> ans = list(multiset_partitions(a, 2)) - >>> a.sort() - >>> list(multiset_partitions(a, 2)) == ans - True - >>> a = list(range(3, 1, -1)) - >>> (list(multiset_partitions(a)) == - ... list(multiset_partitions(sorted(a)))) - True - - If m is omitted then all partitions will be returned: - - >>> list(multiset_partitions([1, 1, 2])) - [[[1, 1, 2]], [[1, 1], [2]], [[1, 2], [1]], [[1], [1], [2]]] - >>> list(multiset_partitions([1]*3)) - [[[1, 1, 1]], [[1], [1, 1]], [[1], [1], [1]]] - - Counting - ======== - - The number of partitions of a set is given by the bell number: - - >>> from sympy import bell - >>> len(list(multiset_partitions(5))) == bell(5) == 52 - True - - The number of partitions of length k from a set of size n is given by the - Stirling Number of the 2nd kind: - - >>> def S2(n, k): - ... from sympy import Dummy, binomial, factorial, Sum - ... if k > n: - ... return 0 - ... j = Dummy() - ... arg = (-1)**(k-j)*j**n*binomial(k,j) - ... return 1/factorial(k)*Sum(arg,(j,0,k)).doit() - ... - >>> S2(5, 2) == len(list(multiset_partitions(5, 2))) == 15 - True - - These comments on counting apply to *sets*, not multisets. - - Notes - ===== - - When all the elements are the same in the multiset, the order - of the returned partitions is determined by the ``partitions`` - routine. - - See Also - ======== - partitions - sympy.combinatorics.partitions.Partition - sympy.combinatorics.partitions.IntegerPartition - """ - - if type(multiset) is int: - n = multiset - if m and m > n: - return - multiset = list(range(multiset)) - if m == 1: - yield [multiset[:]] - return - for nc, q in _set_partitions(n): - if m is None or nc == m: - rv = [[] for i in range(nc)] - for i in range(n): - rv[q[i]].append(multiset[i]) - yield rv - return - - if len(multiset) == 1 and type(multiset) is str: - multiset = [multiset] - - if not has_variety(multiset): - n = len(multiset) - if m and m > n: - return - if m == 1: - yield [multiset[:]] - return - x = multiset[:1] - for size, p in partitions(n, m, size=True): - if m is None or size == m: - rv = [] - for k in sorted(p): - rv.extend([x*k]*p[k]) - yield rv - else: - multiset = list(ordered(multiset)) - n = len(multiset) - if m and m > n: - return - if m == 1: - yield [multiset[:]] - return - - # if there are repeated elements, sort them and define the - # canon dictionary that will be used to create the cache key - # in case elements of the multiset are not hashable - cache = set() - canon = {} # {physical position: position where it appeared first} - for i, mi in enumerate(multiset): - canon.setdefault(i, canon.get(i, multiset.index(mi))) - if len(set(canon.values())) != n: - canon = {} - for i, mi in enumerate(multiset): - canon.setdefault(i, canon.get(i, multiset.index(mi))) - else: - canon = None - - for nc, q in _set_partitions(n): - if m is None or nc == m: - rv = [[] for i in range(nc)] - for i in range(n): - rv[q[i]].append(i) - if canon: - canonical = tuple( - sorted([tuple([canon[i] for i in j]) for j in rv])) - if canonical in cache: - continue - cache.add(canonical) - - yield [[multiset[j] for j in i] for i in rv] - -
    -
    [docs]def partitions(n, m=None, k=None, size=False): - """Generate all partitions of integer n (>= 0). - - Parameters - ========== - - ``m`` : integer (default gives partitions of all sizes) - limits number of parts in parition (mnemonic: m, maximum parts) - ``k`` : integer (default gives partitions number from 1 through n) - limits the numbers that are kept in the partition (mnemonic: k, keys) - ``size`` : bool (default False, only partition is returned) - when ``True`` then (M, P) is returned where M is the sum of the - multiplicities and P is the generated partition. - - Each partition is represented as a dictionary, mapping an integer - to the number of copies of that integer in the partition. For example, - the first partition of 4 returned is {4: 1}, "4: one of them". - - Examples - ======== - - >>> from sympy.utilities.iterables import partitions - - The numbers appearing in the partition (the key of the returned dict) - are limited with k: - - >>> for p in partitions(6, k=2): - ... print(p) - {2: 3} - {1: 2, 2: 2} - {1: 4, 2: 1} - {1: 6} - - The maximum number of parts in the partion (the sum of the values in - the returned dict) are limited with m: - - >>> for p in partitions(6, m=2): - ... print(p) - ... - {6: 1} - {1: 1, 5: 1} - {2: 1, 4: 1} - {3: 2} - - Note that the _same_ dictionary object is returned each time. - This is for speed: generating each partition goes quickly, - taking constant time, independent of n. - - >>> [p for p in partitions(6, k=2)] - [{1: 6}, {1: 6}, {1: 6}, {1: 6}] - - If you want to build a list of the returned dictionaries then - make a copy of them: - - >>> [p.copy() for p in partitions(6, k=2)] - [{2: 3}, {1: 2, 2: 2}, {1: 4, 2: 1}, {1: 6}] - >>> [(M, p.copy()) for M, p in partitions(6, k=2, size=True)] - [(3, {2: 3}), (4, {1: 2, 2: 2}), (5, {1: 4, 2: 1}), (6, {1: 6})] - - Reference: - modified from Tim Peter's version to allow for k and m values: - code.activestate.com/recipes/218332-generator-for-integer-partitions/ - - See Also - ======== - sympy.combinatorics.partitions.Partition - sympy.combinatorics.partitions.IntegerPartition - - """ - if n < 0: - raise ValueError("n must be >= 0") - if m == 0: - raise ValueError("m must be > 0") - m = min(m or n, n) - if m < 1: - raise ValueError("maximum numbers in partition, m, must be > 0") - k = min(k or n, n) - if k < 1: - raise ValueError("maximum value in partition, k, must be > 0") - - if m*k < n: - return - - n, m, k = as_int(n), as_int(m), as_int(k) - q, r = divmod(n, k) - ms = {k: q} - keys = [k] # ms.keys(), from largest to smallest - if r: - ms[r] = 1 - keys.append(r) - room = m - q - bool(r) - if size: - yield sum(ms.values()), ms - else: - yield ms - - while keys != [1]: - # Reuse any 1's. - if keys[-1] == 1: - del keys[-1] - reuse = ms.pop(1) - room += reuse - else: - reuse = 0 - - while 1: - # Let i be the smallest key larger than 1. Reuse one - # instance of i. - i = keys[-1] - newcount = ms[i] = ms[i] - 1 - reuse += i - if newcount == 0: - del keys[-1], ms[i] - room += 1 - - # Break the remainder into pieces of size i-1. - i -= 1 - q, r = divmod(reuse, i) - need = q + bool(r) - if need > room: - if not keys: - return - continue - - ms[i] = q - keys.append(i) - if r: - ms[r] = 1 - keys.append(r) - break - room -= need - if size: - yield sum(ms.values()), ms - else: - yield ms - -
    -
    [docs]def binary_partitions(n): - """ - Generates the binary partition of n. - - A binary partition consists only of numbers that are - powers of two. Each step reduces a 2**(k+1) to 2**k and - 2**k. Thus 16 is converted to 8 and 8. - - Reference: TAOCP 4, section 7.2.1.5, problem 64 - - Examples - ======== - - >>> from sympy.utilities.iterables import binary_partitions - >>> for i in binary_partitions(5): - ... print(i) - ... - [4, 1] - [2, 2, 1] - [2, 1, 1, 1] - [1, 1, 1, 1, 1] - """ - from math import ceil, log - pow = int(2**(ceil(log(n, 2)))) - sum = 0 - partition = [] - while pow: - if sum + pow <= n: - partition.append(pow) - sum += pow - pow >>= 1 - - last_num = len(partition) - 1 - (n & 1) - while last_num >= 0: - yield partition - if partition[last_num] == 2: - partition[last_num] = 1 - partition.append(1) - last_num -= 1 - continue - partition.append(1) - partition[last_num] >>= 1 - x = partition[last_num + 1] = partition[last_num] - last_num += 1 - while x > 1: - if x <= len(partition) - last_num - 1: - del partition[-x + 1:] - last_num += 1 - partition[last_num] = x - else: - x >>= 1 - yield [1]*n - -
    -
    [docs]def has_dups(seq): - """Return True if there are any duplicate elements in ``seq``. - - Examples - ======== - - >>> from sympy.utilities.iterables import has_dups - >>> from sympy import Dict, Set - - >>> has_dups((1, 2, 1)) - True - >>> has_dups(list(range(3))) - False - >>> all(has_dups(c) is False for c in (set(), Set(), dict(), Dict())) - True - """ - if isinstance(seq, (dict, set, C.Dict, C.Set)): - return False - uniq = set() - return any(True for s in seq if s in uniq or uniq.add(s)) - -
    -
    [docs]def has_variety(seq): - """Return True if there are any different elements in ``seq``. - - Examples - ======== - - >>> from sympy.utilities.iterables import has_variety - >>> from sympy import Dict, Set - - >>> has_variety((1, 2, 1)) - True - >>> has_variety((1, 1, 1)) - False - """ - for i, s in enumerate(seq): - if i == 0: - sentinel = s - else: - if s != sentinel: - return True - return False - -
    -
    [docs]def uniq(seq): - """ - Yield unique elements from ``seq`` as an iterator. - - Examples - ======== - - >>> from sympy.utilities.iterables import uniq - >>> dat = [1, 4, 1, 5, 4, 2, 1, 2] - >>> type(uniq(dat)) in (list, tuple) - False - - >>> list(uniq(dat)) - [1, 4, 5, 2] - >>> list(uniq(x for x in dat)) - [1, 4, 5, 2] - >>> list(uniq([[1], [2, 1], [1]])) - [[1], [2, 1], [1]] - - """ - from sympy.core.function import Tuple - - if not hasattr(seq, '__getitem__'): - container = list - else: - container = type(seq) - - try: - seen = set() - result = [] - for s in seq: - if not (s in seen or seen.add(s)): - yield s - except TypeError: - # something was unhashable - if not hasattr(seq, '__getitem__'): - yield s - result = [s] - else: - result = [] - for s in seq: - for r in result: - if s == r: - break - else: - yield s - -
    -
    [docs]def generate_bell(n): - """ - Generates the bell permutations of length ``n``. - - Examples - ======== - - >>> from sympy.utilities.iterables import generate_bell - >>> from sympy import zeros, Matrix, factorial, pprint - >>> from sympy.core.compatibility import permutations - - This is the sort of permutation used in the ringing of physical bells, - and does not produce permutations in lexicographical order. Rather, the - permutations differ from each other by exactly one inversion, and the - position at which the swapping occurs varies periodically in a simple - fashion. Consider the first few permutations of 4 elements generated - by ``permutations`` and ``generate_bell``: - - >>> list(permutations(list(range(4))))[:5] - [(0, 1, 2, 3), (0, 1, 3, 2), (0, 2, 1, 3), (0, 2, 3, 1), (0, 3, 1, 2)] - >>> list(generate_bell(4))[:5] - [(0, 1, 2, 3), (1, 0, 2, 3), (1, 2, 0, 3), (1, 2, 3, 0), (2, 1, 3, 0)] - - Notice how the 2nd and 3rd lexicographical permutations have 3 elements - out of place whereas each bell permutations always has only two - elements out of place relative to the previous permutation. - - How the position of inversion varies across the elements can be seen - by tracing out where the 0 appears in the permutations: - - >>> m = zeros(4, 24) - >>> for i, p in enumerate(generate_bell(4)): - ... m[:, i] = Matrix(list(p)) - >>> m.print_nonzero('X') - [ XXXXXX XXXXXX XXXXXX ] - [X XXXX XX XXXX XX XXXX X] - [XX XX XXXX XX XXXX XX XX] - [XXX XXXXXX XXXXXX XXX] - - References - ========== - - * http://en.wikipedia.org/wiki/Method_ringing - * http://stackoverflow.com/questions/4856615/recursive-permutation/4857018 - * http://programminggeeks.com/bell-algorithm-for-permutation/ - * http://en.wikipedia.org/wiki/ - Steinhaus%E2%80%93Johnson%E2%80%93Trotter_algorithm - * Generating involutions, derangements, and relatives by ECO - Vincent Vajnovszki, DMTCS vol 1 issue 12, 2010 - - """ - from sympy.functions.combinatorial.factorials import factorial - pos = dir = 1 - do = factorial(n) - p = list(range(n)) - yield tuple(p) - do -= 1 - while do: - if pos >= n: - dir = -dir - p[0], p[1] = p[1], p[0] - elif pos < 1: - dir = -dir - p[-2], p[-1] = p[-1], p[-2] - else: - p[pos - 1], p[pos] = p[pos], p[pos - 1] - pos += dir - yield tuple(p) - do -= 1 - -
    -
    [docs]def generate_involutions(n): - """ - Generates involutions. - - An involution is a permutation that when multiplied - by itself equals the identity permutation. In this - implementation the involutions are generated using - Fixed Points. - - Alternatively, an involution can be considered as - a permutation that does not contain any cycles with - a length that is greater than two. - - Reference: - http://mathworld.wolfram.com/PermutationInvolution.html - - Examples - ======== - - >>> from sympy.utilities.iterables import generate_involutions - >>> list(generate_involutions(3)) - [(0, 1, 2), (0, 2, 1), (1, 0, 2), (2, 1, 0)] - >>> len(list(generate_involutions(4))) - 10 - """ - idx = list(range(n)) - for p in permutations(idx): - for i in idx: - if p[p[i]] != i: - break - else: - yield p - -
    -
    [docs]def generate_derangements(perm): - """ - Routine to generate unique derangements. - - TODO: This will be rewritten to use the - ECO operator approach once the permutations - branch is in master. - - Examples - ======== - - >>> from sympy.utilities.iterables import generate_derangements - >>> list(generate_derangements([0, 1, 2])) - [[1, 2, 0], [2, 0, 1]] - >>> list(generate_derangements([0, 1, 2, 3])) - [[1, 0, 3, 2], [1, 2, 3, 0], [1, 3, 0, 2], [2, 0, 3, 1], \ - [2, 3, 0, 1], [2, 3, 1, 0], [3, 0, 1, 2], [3, 2, 0, 1], \ - [3, 2, 1, 0]] - >>> list(generate_derangements([0, 1, 1])) - [] - - See Also - ======== - sympy.functions.combinatorial.factorials.subfactorial - """ - p = multiset_permutations(perm) - indices = list(range(len(perm))) - p0 = next(p) - for pi in p: - if all(pi[i] != p0[i] for i in indices): - yield pi - -
    -@deprecated( - useinstead="bracelets", deprecated_since_version="0.7.3") -
    [docs]def unrestricted_necklace(n, k): - """Wrapper to necklaces to return a free (unrestricted) necklace.""" - return necklaces(n, k, free=True) - -
    -
    [docs]def necklaces(n, k, free=False): - """ - A routine to generate necklaces that may (free=True) or may not - (free=False) be turned over to be viewed. The "necklaces" returned - are comprised of ``n`` integers (beads) with ``k`` different - values (colors). Only unique necklaces are returned. - - Examples - ======== - - >>> from sympy.utilities.iterables import necklaces, bracelets - >>> def show(s, i): - ... return ''.join(s[j] for j in i) - - The "unrestricted necklace" is sometimes also referred to as a - "bracelet" (an object that can be turned over, a sequence that can - be reversed) and the term "necklace" is used to imply a sequence - that cannot be reversed. So ACB == ABC for a bracelet (rotate and - reverse) while the two are different for a necklace since rotation - alone cannot make the two sequences the same. - - (mnemonic: Bracelets can be viewed Backwards, but Not Necklaces.) - - >>> B = [show('ABC', i) for i in bracelets(3, 3)] - >>> N = [show('ABC', i) for i in necklaces(3, 3)] - >>> set(N) - set(B) - set(['ACB']) - - >>> list(necklaces(4, 2)) - [(0, 0, 0, 0), (0, 0, 0, 1), (0, 0, 1, 1), - (0, 1, 0, 1), (0, 1, 1, 1), (1, 1, 1, 1)] - - >>> [show('.o', i) for i in bracelets(4, 2)] - ['....', '...o', '..oo', '.o.o', '.ooo', 'oooo'] - - References - ========== - - http://mathworld.wolfram.com/Necklace.html - - """ - return uniq(minlex(i, directed=not free) for i in - variations(list(range(k)), n, repetition=True)) - -
    -
    [docs]def bracelets(n, k): - """Wrapper to necklaces to return a free (unrestricted) necklace.""" - return necklaces(n, k, free=True) - -
    -
    [docs]def generate_oriented_forest(n): - """ - This algorithm generates oriented forests. - - An oriented graph is a directed graph having no symmetric pair of directed - edges. A forest is an acyclic graph, i.e., it has no cycles. A forest can - also be described as a disjoint union of trees, which are graphs in which - any two vertices are connected by exactly one simple path. - - Reference: - [1] T. Beyer and S.M. Hedetniemi: constant time generation of \ - rooted trees, SIAM J. Computing Vol. 9, No. 4, November 1980 - [2] http://stackoverflow.com/questions/1633833/oriented-forest-taocp-algorithm-in-python - - Examples - ======== - - >>> from sympy.utilities.iterables import generate_oriented_forest - >>> list(generate_oriented_forest(4)) - [[0, 1, 2, 3], [0, 1, 2, 2], [0, 1, 2, 1], [0, 1, 2, 0], \ - [0, 1, 1, 1], [0, 1, 1, 0], [0, 1, 0, 1], [0, 1, 0, 0], [0, 0, 0, 0]] - """ - P = list(range(-1, n)) - while True: - yield P[1:] - if P[n] > 0: - P[n] = P[P[n]] - else: - for p in range(n - 1, 0, -1): - if P[p] != 0: - target = P[p] - 1 - for q in range(p - 1, 0, -1): - if P[q] == target: - break - offset = p - q - for i in range(p, n + 1): - P[i] = P[i - offset] - break - else: - break - -
    -
    [docs]def minlex(seq, directed=True, is_set=False, small=None): - """ - Return a tuple where the smallest element appears first; if - ``directed`` is True (default) then the order is preserved, otherwise - the sequence will be reversed if that gives a smaller ordering. - - If every element appears only once then is_set can be set to True - for more efficient processing. - - If the smallest element is known at the time of calling, it can be - passed and the calculation of the smallest element will be omitted. - - Examples - ======== - - >>> from sympy.combinatorics.polyhedron import minlex - >>> minlex((1, 2, 0)) - (0, 1, 2) - >>> minlex((1, 0, 2)) - (0, 2, 1) - >>> minlex((1, 0, 2), directed=False) - (0, 1, 2) - - >>> minlex('11010011000', directed=True) - '00011010011' - >>> minlex('11010011000', directed=False) - '00011001011' - - """ - is_str = isinstance(seq, str) - seq = list(seq) - if small is None: - small = min(seq) - if is_set: - i = seq.index(small) - if not directed: - n = len(seq) - p = (i + 1) % n - m = (i - 1) % n - if seq[p] > seq[m]: - seq = list(reversed(seq)) - i = n - i - 1 - if i: - seq = rotate_left(seq, i) - best = seq - else: - count = seq.count(small) - if count == 1 and directed: - best = rotate_left(seq, seq.index(small)) - else: - # if not directed, and not a set, we can't just - # pass this off to minlex with is_set True since - # peeking at the neighbor may not be sufficient to - # make the decision so we continue... - best = seq - for i in range(count): - seq = rotate_left(seq, seq.index(small, count != 1)) - if seq < best: - best = seq - # it's cheaper to rotate now rather than search - # again for these in reversed order so we test - # the reverse now - if not directed: - seq = rotate_left(seq, 1) - seq = list(reversed(seq)) - if seq < best: - best = seq - seq = list(reversed(seq)) - seq = rotate_right(seq, 1) - # common return - if is_str: - return ''.join(best) - return tuple(best) - -
    -
    [docs]def runs(seq, op=gt): - """Group the sequence into lists in which successive elements - all compare the same with the comparison operator, ``op``: - op(seq[i + 1], seq[i]) is True from all elements in a run. - - Examples - ======== - - >>> from sympy.utilities.iterables import runs - >>> from operator import ge - >>> runs([0, 1, 2, 2, 1, 4, 3, 2, 2]) - [[0, 1, 2], [2], [1, 4], [3], [2], [2]] - >>> runs([0, 1, 2, 2, 1, 4, 3, 2, 2], op=ge) - [[0, 1, 2, 2], [1, 4], [3], [2, 2]] - """ - cycles = [] - seq = iter(seq) - try: - run = [next(seq)] - except StopIteration: - return [] - while True: - try: - ei = next(seq) - except StopIteration: - break - if op(ei, run[-1]): - run.append(ei) - continue - else: - cycles.append(run) - run = [ei] - if run: - cycles.append(run) - return cycles - -
    -
    [docs]def kbins(l, k, ordered=None): - """ - Return sequence ``l`` partitioned into ``k`` bins. - - Examples - ======== - - >>> from sympy.utilities.iterables import kbins - - The default is to give the items in the same order, but grouped - into k partitions without any reordering: - - >>> for p in kbins(list(range(5)), 2): - ... print(p) - ... - [[0], [1, 2, 3, 4]] - [[0, 1], [2, 3, 4]] - [[0, 1, 2], [3, 4]] - [[0, 1, 2, 3], [4]] - - The ``ordered`` flag which is either None (to give the simple partition - of the the elements) or is a 2 digit integer indicating whether the order of - the bins and the order of the items in the bins matters. Given:: - - A = [[0], [1, 2]] - B = [[1, 2], [0]] - C = [[2, 1], [0]] - D = [[0], [2, 1]] - - the following values for ``ordered`` have the shown meanings:: - - 00 means A == B == C == D - 01 means A == B - 10 means A == D - 11 means A == A - - >>> for ordered in [None, 0, 1, 10, 11]: - ... print('ordered =', ordered) - ... for p in kbins(list(range(3)), 2, ordered=ordered): - ... print(' ', p) - ... - ordered = None - [[0], [1, 2]] - [[0, 1], [2]] - ordered = 0 - [[0, 1], [2]] - [[0, 2], [1]] - [[0], [1, 2]] - ordered = 1 - [[0], [1, 2]] - [[0], [2, 1]] - [[1], [0, 2]] - [[1], [2, 0]] - [[2], [0, 1]] - [[2], [1, 0]] - ordered = 10 - [[0, 1], [2]] - [[2], [0, 1]] - [[0, 2], [1]] - [[1], [0, 2]] - [[0], [1, 2]] - [[1, 2], [0]] - ordered = 11 - [[0], [1, 2]] - [[0, 1], [2]] - [[0], [2, 1]] - [[0, 2], [1]] - [[1], [0, 2]] - [[1, 0], [2]] - [[1], [2, 0]] - [[1, 2], [0]] - [[2], [0, 1]] - [[2, 0], [1]] - [[2], [1, 0]] - [[2, 1], [0]] - - See Also - ======== - partitions, multiset_partitions - - """ - def partition(lista, bins): - # EnricoGiampieri's partition generator from - # http://stackoverflow.com/questions/13131491/ - # partition-n-items-into-k-bins-in-python-lazily - if len(lista) == 1 or bins == 1: - yield [lista] - elif len(lista) > 1 and bins > 1: - for i in range(1, len(lista)): - for part in partition(lista[i:], bins - 1): - if len([lista[:i]] + part) == bins: - yield [lista[:i]] + part - - if ordered is None: - for p in partition(l, k): - yield p - elif ordered == 11: - for pl in multiset_permutations(l): - pl = list(pl) - for p in partition(pl, k): - yield p - elif ordered == 00: - for p in multiset_partitions(l, k): - yield p - elif ordered == 10: - for p in multiset_partitions(l, k): - for perm in permutations(p): - yield list(perm) - elif ordered == 0o1: - for kgot, p in partitions(len(l), k, size=True): - if kgot != k: - continue - for li in multiset_permutations(l): - rv = [] - i = j = 0 - li = list(li) - for size, multiplicity in sorted(p.items()): - for m in range(multiplicity): - j = i + size - rv.append(li[i: j]) - i = j - yield rv - else: - raise ValueError( - 'ordered must be one of 00, 01, 10 or 11, not %s' % ordered)
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/utilities/lambdify.html b/dev-py3k/_modules/sympy/utilities/lambdify.html deleted file mode 100644 index b76aa394004..00000000000 --- a/dev-py3k/_modules/sympy/utilities/lambdify.html +++ /dev/null @@ -1,555 +0,0 @@ - - - - - - - - - - sympy.utilities.lambdify — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.utilities.lambdify

    -"""
    -This module provides convenient functions to transform sympy expressions to
    -lambda functions which can be used to calculate numerical values very fast.
    -"""
    -
    -
    -from sympy.external import import_module
    -from sympy.core.compatibility import is_sequence, iterable
    -
    -import inspect
    -
    -# These are the namespaces the lambda functions will use.
    -MATH = {}
    -MPMATH = {}
    -NUMPY = {}
    -SYMPY = {}
    -
    -# Default namespaces, letting us define translations that can't be defined
    -# by simple variable maps, like I => 1j
    -# These are separate from the names above because the above names are modified
    -# throughout this file, whereas these should remain unmodified.
    -MATH_DEFAULT = {}
    -MPMATH_DEFAULT = {}
    -NUMPY_DEFAULT = {"I": 1j}
    -SYMPY_DEFAULT = {}
    -
    -# Mappings between sympy and other modules function names.
    -MATH_TRANSLATIONS = {
    -    "Abs": "fabs",
    -    "ceiling": "ceil",
    -    "E": "e",
    -    "ln": "log",
    -}
    -
    -MPMATH_TRANSLATIONS = {
    -    "ceiling": "ceil",
    -    "chebyshevt": "chebyt",
    -    "chebyshevu": "chebyu",
    -    "E": "e",
    -    "I": "j",
    -    "ln": "log",
    -    #"lowergamma":"lower_gamma",
    -    "oo": "inf",
    -    #"uppergamma":"upper_gamma",
    -    "LambertW": "lambertw",
    -    "Matrix": "matrix",
    -    "MutableDenseMatrix": "matrix",
    -    "conjugate": "conj",
    -    "dirichlet_eta": "altzeta",
    -    "Ei": "ei",
    -    "Shi": "shi",
    -    "Chi": "chi",
    -    "Si": "si",
    -    "Ci": "ci"
    -}
    -
    -NUMPY_TRANSLATIONS = {
    -    "Abs": "abs",
    -    "acos": "arccos",
    -    "acosh": "arccosh",
    -    "arg": "angle",
    -    "asin": "arcsin",
    -    "asinh": "arcsinh",
    -    "atan": "arctan",
    -    "atan2": "arctan2",
    -    "atanh": "arctanh",
    -    "ceiling": "ceil",
    -    "E": "e",
    -    "im": "imag",
    -    "ln": "log",
    -    "Matrix": "matrix",
    -    "MutableDenseMatrix": "matrix",
    -    "Max": "amax",
    -    "Min": "amin",
    -    "oo": "inf",
    -    "re": "real",
    -}
    -
    -# Available modules:
    -MODULES = {
    -    "math": (MATH, MATH_DEFAULT, MATH_TRANSLATIONS, ("from math import *",)),
    -    "mpmath": (MPMATH, MPMATH_DEFAULT, MPMATH_TRANSLATIONS, ("from sympy.mpmath import *",)),
    -    "numpy": (NUMPY, NUMPY_DEFAULT, NUMPY_TRANSLATIONS, ("import_module('numpy')",)),
    -    "sympy": (SYMPY, SYMPY_DEFAULT, {}, (
    -        "from sympy.functions import *",
    -        "from sympy.matrices import *",
    -        "from sympy import Integral, pi, oo, nan, zoo, E, I",)),
    -}
    -
    -
    -def _import(module, reload="False"):
    -    """
    -    Creates a global translation dictionary for module.
    -
    -    The argument module has to be one of the following strings: "math",
    -    "mpmath", "numpy", "sympy".
    -    These dictionaries map names of python functions to their equivalent in
    -    other modules.
    -    """
    -    try:
    -        namespace, namespace_default, translations, import_commands = MODULES[
    -            module]
    -    except KeyError:
    -        raise NameError(
    -            "'%s' module can't be used for lambdification" % module)
    -
    -    # Clear namespace or exit
    -    if namespace != namespace_default:
    -        # The namespace was already generated, don't do it again if not forced.
    -        if reload:
    -            namespace.clear()
    -            namespace.update(namespace_default)
    -        else:
    -            return
    -
    -    for import_command in import_commands:
    -        if import_command.startswith('import_module'):
    -            module = eval(import_command)
    -
    -            if module is not None:
    -                namespace.update(module.__dict__)
    -                continue
    -        else:
    -            try:
    -                exec(import_command, {}, namespace)
    -                continue
    -            except ImportError:
    -                pass
    -
    -        raise ImportError(
    -            "can't import '%s' with '%s' command" % (module, import_command))
    -
    -    # Add translated names to namespace
    -    for sympyname, translation in translations.items():
    -        namespace[sympyname] = namespace[translation]
    -
    -
    -
    [docs]def lambdify(args, expr, modules=None, printer=None, use_imps=True): - """ - Returns a lambda function for fast calculation of numerical values. - - If not specified differently by the user, SymPy functions are replaced as - far as possible by either python-math, numpy (if available) or mpmath - functions - exactly in this order. To change this behavior, the "modules" - argument can be used. It accepts: - - - the strings "math", "mpmath", "numpy", "sympy" - - any modules (e.g. math) - - dictionaries that map names of sympy functions to arbitrary functions - - lists that contain a mix of the arguments above, with higher priority - given to entries appearing first. - - Usage - ===== - - (1) Use one of the provided modules: - - >> f = lambdify(x, sin(x), "math") - - Attention: Functions that are not in the math module will throw a name - error when the lambda function is evaluated! So this would - be better: - - >> f = lambdify(x, sin(x)*gamma(x), ("math", "mpmath", "sympy")) - - (2) Use some other module: - - >> import numpy - >> f = lambdify((x,y), tan(x*y), numpy) - - Attention: There are naming differences between numpy and sympy. So if - you simply take the numpy module, e.g. sympy.atan will not be - translated to numpy.arctan. Use the modified module instead - by passing the string "numpy": - - >> f = lambdify((x,y), tan(x*y), "numpy") - >> f(1, 2) - -2.18503986326 - >> from numpy import array - >> f(array([1, 2, 3]), array([2, 3, 5])) - [-2.18503986 -0.29100619 -0.8559934 ] - - (3) Use own dictionaries: - - >> def my_cool_function(x): ... - >> dic = {"sin" : my_cool_function} - >> f = lambdify(x, sin(x), dic) - - Now f would look like: - - >> lambda x: my_cool_function(x) - - Examples - ======== - - >>> from sympy.utilities.lambdify import implemented_function, lambdify - >>> from sympy import sqrt, sin, Matrix - >>> from sympy import Function - >>> from sympy.abc import x, y, z - - >>> f = lambdify(x, x**2) - >>> f(2) - 4 - >>> f = lambdify((x, y, z), [z, y, x]) - >>> f(1,2,3) - [3, 2, 1] - >>> f = lambdify(x, sqrt(x)) - >>> f(4) - 2.0 - >>> f = lambdify((x, y), sin(x*y)**2) - >>> f(0, 5) - 0.0 - >>> f = lambdify((x, y), Matrix((x, x + y)).T, modules='sympy') - >>> f(1, 2) - [1, 3] - - Functions present in `expr` can also carry their own numerical - implementations, in a callable attached to the ``_imp_`` - attribute. Usually you attach this using the - ``implemented_function`` factory: - - >>> f = implemented_function(Function('f'), lambda x: x+1) - >>> func = lambdify(x, f(x)) - >>> func(4) - 5 - - ``lambdify`` always prefers ``_imp_`` implementations to implementations - in other namespaces, unless the ``use_imps`` input parameter is False. - """ - from sympy.core.symbol import Symbol - - # If the user hasn't specified any modules, use what is available. - if modules is None: - # Use either numpy (if available) or python.math where possible. - # XXX: This leads to different behaviour on different systems and - # might be the reason for irreproducible errors. - modules = ["math", "mpmath", "sympy"] - - try: - _import("numpy") - except ImportError: - pass - else: - modules.insert(1, "numpy") - - # Get the needed namespaces. - namespaces = [] - # First find any function implementations - if use_imps: - namespaces.append(_imp_namespace(expr)) - # Check for dict before iterating - if isinstance(modules, (dict, str)) or not hasattr(modules, '__iter__'): - namespaces.append(modules) - else: - namespaces += list(modules) - # fill namespace with first having highest priority - namespace = {} - for m in namespaces[::-1]: - buf = _get_namespace(m) - namespace.update(buf) - - if hasattr(expr, "atoms"): - #Try if you can extract symbols from the expression. - #Move on if expr.atoms in not implemented. - syms = expr.atoms(Symbol) - for term in syms: - namespace.update({str(term): term}) - - # Create lambda function. - lstr = lambdastr(args, expr, printer=printer) - return eval(lstr, namespace) - -
    -def _get_namespace(m): - """ - This is used by _lambdify to parse its arguments. - """ - if isinstance(m, str): - _import(m) - return MODULES[m][0] - elif isinstance(m, dict): - return m - elif hasattr(m, "__dict__"): - return m.__dict__ - else: - raise TypeError("Argument must be either a string, dict or module but it is: %s" % m) - - -
    [docs]def lambdastr(args, expr, printer=None): - """ - Returns a string that can be evaluated to a lambda function. - - >>> from sympy.abc import x, y, z - >>> from sympy.utilities.lambdify import lambdastr - >>> lambdastr(x, x**2) - 'lambda x: (x**2)' - >>> lambdastr((x,y,z), [z,y,x]) - 'lambda x,y,z: ([z, y, x])' - - """ - if printer is not None: - if inspect.isfunction(printer): - lambdarepr = printer - else: - if inspect.isclass(printer): - lambdarepr = lambda expr: printer().doprint(expr) - else: - lambdarepr = lambda expr: printer.doprint(expr) - else: - #XXX: This has to be done here because of circular imports - from sympy.printing.lambdarepr import lambdarepr - - # Transform everything to strings. - from sympy.matrices import DeferredVector - expr = lambdarepr(expr) - if isinstance(args, str): - pass - elif iterable(args, exclude=DeferredVector): - args = ",".join(str(a) for a in args) - else: - args = str(args) - - return "lambda %s: (%s)" % (args, expr) - -
    -def _imp_namespace(expr, namespace=None): - """ Return namespace dict with function implementations - - We need to search for functions in anything that can be thrown at - us - that is - anything that could be passed as `expr`. Examples - include sympy expressions, as well as tuples, lists and dicts that may - contain sympy expressions. - - Parameters - ---------- - expr : object - Something passed to lambdify, that will generate valid code from - ``str(expr)``. - namespace : None or mapping - Namespace to fill. None results in new empty dict - - Returns - ------- - namespace : dict - dict with keys of implemented function names within `expr` and - corresponding values being the numerical implementation of - function - - Examples - -------- - >>> from sympy.abc import x, y, z - >>> from sympy.utilities.lambdify import implemented_function, _imp_namespace - >>> from sympy import Function - >>> f = implemented_function(Function('f'), lambda x: x+1) - >>> g = implemented_function(Function('g'), lambda x: x*10) - >>> namespace = _imp_namespace(f(g(x))) - >>> sorted(namespace.keys()) - ['f', 'g'] - """ - # Delayed import to avoid circular imports - from sympy.core.function import FunctionClass - if namespace is None: - namespace = {} - # tuples, lists, dicts are valid expressions - if is_sequence(expr): - for arg in expr: - _imp_namespace(arg, namespace) - return namespace - elif isinstance(expr, dict): - for key, val in list(expr.items()): - # functions can be in dictionary keys - _imp_namespace(key, namespace) - _imp_namespace(val, namespace) - return namespace - # sympy expressions may be Functions themselves - func = getattr(expr, 'func', None) - if isinstance(func, FunctionClass): - imp = getattr(func, '_imp_', None) - if imp is not None: - name = expr.func.__name__ - if name in namespace and namespace[name] != imp: - raise ValueError('We found more than one ' - 'implementation with name ' - '"%s"' % name) - namespace[name] = imp - # and / or they may take Functions as arguments - if hasattr(expr, 'args'): - for arg in expr.args: - _imp_namespace(arg, namespace) - return namespace - - -
    [docs]def implemented_function(symfunc, implementation): - """ Add numerical ``implementation`` to function ``symfunc``. - - ``symfunc`` can be an ``UndefinedFunction`` instance, or a name string. - In the latter case we create an ``UndefinedFunction`` instance with that - name. - - Be aware that this is a quick workaround, not a general method to create - special symbolic functions. If you want to create a symbolic function to be - used by all the machinery of sympy you should subclass the ``Function`` - class. - - Parameters - ---------- - symfunc : ``str`` or ``UndefinedFunction`` instance - If ``str``, then create new ``UndefinedFunction`` with this as - name. If `symfunc` is a sympy function, attach implementation to it. - implementation : callable - numerical implementation to be called by ``evalf()`` or ``lambdify`` - - Returns - ------- - afunc : sympy.FunctionClass instance - function with attached implementation - - Examples - -------- - >>> from sympy.abc import x, y, z - >>> from sympy.utilities.lambdify import lambdify, implemented_function - >>> from sympy import Function - >>> f = implemented_function(Function('f'), lambda x: x+1) - >>> lam_f = lambdify(x, f(x)) - >>> lam_f(4) - 5 - """ - # Delayed import to avoid circular imports - from sympy.core.function import UndefinedFunction - # if name, create function to hold implementation - if isinstance(symfunc, str): - symfunc = UndefinedFunction(symfunc) - elif not isinstance(symfunc, UndefinedFunction): - raise ValueError('symfunc should be either a string or' - ' an UndefinedFunction instance.') - # We need to attach as a method because symfunc will be a class - symfunc._imp_ = staticmethod(implementation) - return symfunc
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/utilities/memoization.html b/dev-py3k/_modules/sympy/utilities/memoization.html deleted file mode 100644 index 71d5a1cd380..00000000000 --- a/dev-py3k/_modules/sympy/utilities/memoization.html +++ /dev/null @@ -1,171 +0,0 @@ - - - - - - - - - - sympy.utilities.memoization — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.utilities.memoization

    -
    [docs]def recurrence_memo(initial): - """ - Memo decorator for sequences defined by recurrence - - See usage examples e.g. in the specfun/combinatorial module - """ - cache = initial - - def decorator(f): - def g(n): - L = len(cache) - if n <= L - 1: - return cache[n] - for i in range(L, n + 1): - cache.append(f(i, cache)) - return cache[-1] - return g - return decorator - -
    -
    [docs]def assoc_recurrence_memo(base_seq): - """ - Memo decorator for associated sequences defined by recurrence starting from base - - base_seq(n) -- callable to get base sequence elements - - XXX works only for Pn0 = base_seq(0) cases - XXX works only for m <= n cases - """ - - cache = [] - - def decorator(f): - def g(n, m): - L = len(cache) - if n < L: - return cache[n][m] - - for i in range(L, n + 1): - # get base sequence - F_i0 = base_seq(i) - F_i_cache = [F_i0] - cache.append(F_i_cache) - - # XXX only works for m <= n cases - # generate assoc sequence - for j in range(1, i + 1): - F_ij = f(i, j, cache) - F_i_cache.append(F_ij) - - return cache[n][m] - - return g - return decorator
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/utilities/misc.html b/dev-py3k/_modules/sympy/utilities/misc.html deleted file mode 100644 index 7da9cf3cca6..00000000000 --- a/dev-py3k/_modules/sympy/utilities/misc.html +++ /dev/null @@ -1,232 +0,0 @@ - - - - - - - - - - sympy.utilities.misc — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.utilities.misc

    -"""Miscellaneous stuff that doesn't really fit anywhere else."""
    -
    -from textwrap import fill, dedent
    -
    -# if you use
    -# filldedent('''
    -#             the text''')
    -# a space will be put before the first line because dedent will
    -# put a \n as the first line and fill replaces \n with spaces
    -# so we strip off any leading and trailing \n since printed wrapped
    -# text should not have leading or trailing spaces.
    -filldedent = lambda s, w=70: '\n' + fill(dedent(str(s)).strip('\n'), width=w)
    -
    -
    -
    [docs]def rawlines(s): - """Return a cut-and-pastable string that, when printed, is equivalent - to the input. The string returned is formatted so it can be indented - nicely within tests; in some cases it is wrapped in the dedent - function which has to be imported from textwrap. - - Examples - ======== - - Note: because there are characters in the examples below that need - to be escaped because they are themselves within a triple quoted - docstring, expressions below look more complicated than they would - be if they were printed in an interpreter window. - - >>> from sympy.utilities.misc import rawlines - >>> from sympy import TableForm - >>> s = str(TableForm([[1, 10]], headings=(None, ['a', 'bee']))) - >>> print(rawlines(s)) # the \\ appears as \ when printed - ( - 'a bee\\n' - '-----\\n' - '1 10 ' - ) - >>> print(rawlines('''this - ... that''')) - dedent('''\\ - this - that''') - - >>> print(rawlines('''this - ... that - ... ''')) - dedent('''\\ - this - that - ''') - - >>> s = \"\"\"this - ... is a triple ''' - ... \"\"\" - >>> print(rawlines(s)) - dedent(\"\"\"\\ - this - is a triple ''' - \"\"\") - - >>> print(rawlines('''this - ... that - ... ''')) - ( - 'this\\n' - 'that\\n' - ' ' - ) - """ - lines = s.split('\n') - if len(lines) == 1: - return repr(lines[0]) - triple = ["'''" in s, '"""' in s] - if any(li.endswith(' ') for li in lines) or '\\' in s or all(triple): - rv = ["("] - # add on the newlines - trailing = s.endswith('\n') - last = len(lines) - 1 - for i, li in enumerate(lines): - if i != last or trailing: - rv.append(repr(li)[:-1] + '\\n\'') - else: - rv.append(repr(li)) - return '\n '.join(rv) + '\n)' - else: - rv = '\n '.join(lines) - if triple[0]: - return 'dedent("""\\\n %s""")' % rv - else: - return "dedent('''\\\n %s''')" % rv -
    -import sys -size = getattr(sys, "maxint", None) -if size is None: # Python 3 doesn't have maxint - size = sys.maxsize -if size > 2**32: - ARCH = "64-bit" -else: - ARCH = "32-bit" - -# Python 2.5 does not have sys.flags (it doesn't have hash randomization either) -HASH_RANDOMIZATION = hasattr(sys, 'flags') and getattr(sys.flags, - 'hash_randomization', - False) - - -
    [docs]def debug(*args): - """ - Print ``*args`` if SYMPY_DEBUG is True, else do nothing. - """ - from sympy import SYMPY_DEBUG - if SYMPY_DEBUG: - for a in args: - print(a, end=' ') - print()
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/utilities/pkgdata.html b/dev-py3k/_modules/sympy/utilities/pkgdata.html deleted file mode 100644 index 136e8913db7..00000000000 --- a/dev-py3k/_modules/sympy/utilities/pkgdata.html +++ /dev/null @@ -1,173 +0,0 @@ - - - - - - - - - - sympy.utilities.pkgdata — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.utilities.pkgdata

    -"""
    -pkgdata is a simple, extensible way for a package to acquire data file
    -resources.
    -
    -The getResource function is equivalent to the standard idioms, such as
    -the following minimal implementation::
    -
    -    import sys, os
    -
    -    def getResource(identifier, pkgname=__name__):
    -        pkgpath = os.path.dirname(sys.modules[pkgname].__file__)
    -        path = os.path.join(pkgpath, identifier)
    -        return file(os.path.normpath(path), mode='rb')
    -
    -When a __loader__ is present on the module given by __name__, it will defer
    -getResource to its get_data implementation and return it as a file-like
    -object (such as StringIO).
    -"""
    -
    -import sys
    -import os
    -from io import StringIO
    -
    -
    -
    [docs]def get_resource(identifier, pkgname=__name__): - """ - Acquire a readable object for a given package name and identifier. - An IOError will be raised if the resource can not be found. - - For example:: - - mydata = get_esource('mypkgdata.jpg').read() - - Note that the package name must be fully qualified, if given, such - that it would be found in sys.modules. - - In some cases, getResource will return a real file object. In that - case, it may be useful to use its name attribute to get the path - rather than use it as a file-like object. For example, you may - be handing data off to a C API. - """ - - mod = sys.modules[pkgname] - fn = getattr(mod, '__file__', None) - if fn is None: - raise IOError("%r has no __file__!") - path = os.path.join(os.path.dirname(fn), identifier) - loader = getattr(mod, '__loader__', None) - if loader is not None: - try: - data = loader.get_data(path) - except IOError: - pass - else: - return StringIO(data) - return file(os.path.normpath(path), 'rb')
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/utilities/pytest.html b/dev-py3k/_modules/sympy/utilities/pytest.html deleted file mode 100644 index 4475bb475ee..00000000000 --- a/dev-py3k/_modules/sympy/utilities/pytest.html +++ /dev/null @@ -1,284 +0,0 @@ - - - - - - - - - - sympy.utilities.pytest — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.utilities.pytest

    -"""py.test hacks to support XFAIL/XPASS"""
    -
    -import sys
    -import functools
    -import collections
    -
    -try:
    -    import py
    -    from py.test import skip, raises
    -    USE_PYTEST = getattr(sys, '_running_pytest', False)
    -except ImportError:
    -    USE_PYTEST = False
    -
    -if not USE_PYTEST:
    -
    [docs] def raises(expectedException, code=None): - """ - Tests that ``code`` raises the exception ``expectedException``. - - ``code`` may be a callable, such as a lambda expression or function - name. - - If ``code`` is not given or None, ``raises`` will return a context - manager for use in ``with`` statements; the code to execute then - comes from the scope of the ``with``. The calling module must have - ``from __future__ import with_statement`` as its first code line - for compatibility with Python 2.5 in that case. - - raises does nothing if the callable raises the expected exception, - otherwise it raises an AssertionError. - - Examples - ======== - - >>> from sympy.utilities.pytest import raises - - >>> raises(ZeroDivisionError, lambda: 1/0) - >>> raises(ZeroDivisionError, lambda: 1/2) - Traceback (most recent call last): - ... - AssertionError: DID NOT RAISE - - (Python 2.5's doctest cannot support with statements due to a bug in - its __import__ handling. That's why the following examples are - excluded from doctesting; the doctest: annotations can go once SymPy - stops Python 2.5 support.) - - >>> with raises(ZeroDivisionError): # doctest: +SKIP - ... n = 1/0 - >>> with raises(ZeroDivisionError): # doctest: +SKIP - ... n = 1/2 - Traceback (most recent call last): - ... - AssertionError: DID NOT RAISE - - Note that you cannot test multiple statements via - ``with raises``: - - >>> with raises(ZeroDivisionError): # doctest: +SKIP - ... n = 1/0 # will execute and raise, aborting the ``with`` - ... n = 9999/0 # never executed - - This is just what ``with`` is supposed to do: abort the - contained statement sequence at the first exception and let - the context manager deal with the exception. - - To test multiple statements, you'll need a separate ``with`` - for each: - - >>> with raises(ZeroDivisionError): # doctest: +SKIP - ... n = 1/0 # will execute and raise - ... with raises(ZeroDivisionError): - ... n = 9999/0 # will also execute and raise - - """ - if code is None: - return RaisesContext(expectedException) - elif isinstance(code, collections.Callable): - try: - code() - except expectedException: - return - raise AssertionError("DID NOT RAISE") - elif isinstance(code, str): - raise TypeError( - '\'raises(xxx, "code")\' has been phased out; ' - 'change \'raises(xxx, "expression")\' ' - 'to \'raises(xxx, lambda: expression)\', ' - '\'raises(xxx, "statement")\' ' - 'to \'with raises(xxx): statement\'') - else: - raise TypeError( - 'raises() expects a callable for the 2nd argument.') -
    - class RaisesContext(object): - def __init__(self, expectedException): - self.expectedException = expectedException - - def __enter__(self): - return None - - def __exit__(self, exc_type, exc_value, traceback): - if exc_type is None: - raise AssertionError("DID NOT RAISE") - return issubclass(exc_type, self.expectedException) - - class XFail(Exception): - pass - - class XPass(Exception): - pass - - class Skipped(Exception): - pass - - def XFAIL(func): - def wrapper(): - try: - func() - except Exception as e: - if sys.version_info[:2] < (2, 6): - message = getattr(e, 'message', '') - else: - message = str(e) - if message != "Timeout": - raise XFail(func.__name__) - else: - raise Skipped("Timeout") - raise XPass(func.__name__) - - wrapper = functools.update_wrapper(wrapper, func) - return wrapper - - def skip(str): - raise Skipped(str) - - def SKIP(reason): - """Similar to :func:`skip`, but this is a decorator. """ - def wrapper(func): - def func_wrapper(): - raise Skipped(reason) - - func_wrapper = functools.update_wrapper(func_wrapper, func) - return func_wrapper - - return wrapper - - def slow(func): - func._slow = True - - def func_wrapper(): - func() - - func_wrapper = functools.update_wrapper(func_wrapper, func) - return func_wrapper - -else: - XFAIL = py.test.mark.xfail - slow = py.test.mark.slow - - -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/utilities/randtest.html b/dev-py3k/_modules/sympy/utilities/randtest.html deleted file mode 100644 index ff4b6d838bd..00000000000 --- a/dev-py3k/_modules/sympy/utilities/randtest.html +++ /dev/null @@ -1,308 +0,0 @@ - - - - - - - - - - sympy.utilities.randtest — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.utilities.randtest

    -""" Helpers for randomized testing """
    -
    -from random import uniform
    -import random
    -
    -from sympy import I, nsimplify, Tuple
    -from sympy.core.compatibility import is_sequence, as_int
    -
    -
    -
    [docs]def random_complex_number(a=2, b=-1, c=3, d=1, rational=False): - """ - Return a random complex number. - - To reduce chance of hitting branch cuts or anything, we guarantee - b <= Im z <= d, a <= Re z <= c - """ - A, B = uniform(a, c), uniform(b, d) - if not rational: - return A + I*B - return nsimplify(A, rational=True) + I*nsimplify(B, rational=True) - -
    -
    [docs]def comp(z1, z2, tol): - """Return a bool indicating whether the error between z1 and z2 is <= tol. - - If z2 is non-zero and ``|z1| > 1`` the error is normalized by ``|z1|``, so - if you want the absolute error, call this as ``comp(z1 - z2, 0, tol)``. - """ - if not z1: - z1, z2 = z2, z1 - if not z1: - return True - diff = abs(z1 - z2) - az1 = abs(z1) - if z2 and az1 > 1: - return diff/az1 <= tol - else: - return diff <= tol - -
    -
    [docs]def test_numerically(f, g, z=None, tol=1.0e-6, a=2, b=-1, c=3, d=1): - """ - Test numerically that f and g agree when evaluated in the argument z. - - If z is None, all symbols will be tested. This routine does not test - whether there are Floats present with precision higher than 15 digits - so if there are, your results may not be what you expect due to round- - off errors. - - Examples - ======== - - >>> from sympy import sin, cos, S - >>> from sympy.abc import x - >>> from sympy.utilities.randtest import test_numerically as tn - >>> tn(sin(x)**2 + cos(x)**2, 1, x) - True - """ - f, g, z = Tuple(f, g, z) - z = [z] if z else (f.free_symbols | g.free_symbols) - reps = list(zip(z, [random_complex_number(a, b, c, d) for zi in z])) - z1 = f.subs(reps).n() - z2 = g.subs(reps).n() - return comp(z1, z2, tol) - -
    -
    [docs]def test_derivative_numerically(f, z, tol=1.0e-6, a=2, b=-1, c=3, d=1): - """ - Test numerically that the symbolically computed derivative of f - with respect to z is correct. - - This routine does not test whether there are Floats present with - precision higher than 15 digits so if there are, your results may - not be what you expect due to round-off errors. - - Examples - ======== - - >>> from sympy import sin, cos - >>> from sympy.abc import x - >>> from sympy.utilities.randtest import test_derivative_numerically as td - >>> td(sin(x), x) - True - """ - from sympy.core.function import Derivative - z0 = random_complex_number(a, b, c, d) - f1 = f.diff(z).subs(z, z0) - f2 = Derivative(f, z).doit_numerically(z0) - return comp(f1.n(), f2.n(), tol) -
    -import random - - -def _randrange(seed=None): - """Return a randrange generator. ``seed`` can be - o None - return randomly seeded generator - o int - return a generator seeded with the int - o list - the values to be returned will be taken from the list - in the order given; the provided list is not modified. - - Examples - ======== - - >>> from sympy.utilities.randtest import _randrange - >>> rr = _randrange() - >>> rr(1000) # doctest: +SKIP - 999 - >>> rr = _randrange(3) - >>> rr(1000) # doctest: +SKIP - 238 - >>> rr = _randrange([0, 5, 1, 3, 4]) - >>> rr(3), rr(3) - (0, 1) - """ - if seed is None: - return random.randrange - elif isinstance(seed, int): - return random.Random(seed).randrange - elif is_sequence(seed): - seed = list(seed) # make a copy - seed.reverse() - - def give(a, b=None, seq=seed): - if b is None: - a, b = 0, a - a, b = as_int(a), as_int(b) - w = b - a - if w < 1: - raise ValueError('_randrange got empty range') - try: - x = seq.pop() - except AttributeError: - raise ValueError('_randrange expects a list-like sequence') - except IndexError: - raise ValueError('_randrange sequence was too short') - if a <= x < b: - return x - else: - return give(a, b, seq) - return give - else: - raise ValueError('_randrange got an unexpected seed') - - -def _randint(seed=None): - """Return a randint generator. ``seed`` can be - o None - return randomly seeded generator - o int - return a generator seeded with the int - o list - the values to be returned will be taken from the list - in the order given; the provided list is not modified. - - Examples - ======== - - >>> from sympy.utilities.randtest import _randint - >>> ri = _randint() - >>> ri(1, 1000) # doctest: +SKIP - 999 - >>> ri = _randint(3) - >>> ri(1, 1000) # doctest: +SKIP - 238 - >>> ri = _randint([0, 5, 1, 2, 4]) - >>> ri(1, 3), ri(1, 3) - (1, 2) - """ - if seed is None: - return random.randint - elif isinstance(seed, int): - return random.Random(seed).randint - elif is_sequence(seed): - seed = list(seed) # make a copy - seed.reverse() - - def give(a, b, seq=seed): - a, b = as_int(a), as_int(b) - w = b - a - if w < 0: - raise ValueError('_randint got empty range') - try: - x = seq.pop() - except AttributeError: - raise ValueError('_randint expects a list-like sequence') - except IndexError: - raise ValueError('_randint sequence was too short') - if a <= x <= b: - return x - else: - return give(a, b, seq) - return give - else: - raise ValueError('_randint got an unexpected seed') -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/utilities/runtests.html b/dev-py3k/_modules/sympy/utilities/runtests.html deleted file mode 100644 index bc02324f1a2..00000000000 --- a/dev-py3k/_modules/sympy/utilities/runtests.html +++ /dev/null @@ -1,1847 +0,0 @@ - - - - - - - - - - sympy.utilities.runtests — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.utilities.runtests

    -"""
    -This is our testing framework.
    -
    -Goals:
    -
    -* it should be compatible with py.test and operate very similarly
    -  (or identically)
    -* doesn't require any external dependencies
    -* preferably all the functionality should be in this file only
    -* no magic, just import the test file and execute the test functions, that's it
    -* portable
    -
    -"""
    -
    -import os
    -import sys
    -import inspect
    -import traceback
    -import pdb
    -import re
    -import linecache
    -from fnmatch import fnmatch
    -from timeit import default_timer as clock
    -import doctest as pdoctest  # avoid clashing with our doctest() function
    -from doctest import DocTestFinder, DocTestRunner
    -import re as pre
    -import random
    -import subprocess
    -import signal
    -
    -from sympy.core.cache import clear_cache
    -
    -# Use sys.stdout encoding for ouput.
    -# This was only added to Python's doctest in Python 2.6, so we must duplicate
    -# it here to make utf8 files work in Python 2.5.
    -pdoctest._encoding = getattr(sys.__stdout__, 'encoding', None) or 'utf-8'
    -
    -IS_PYTHON_3 = (sys.version_info[0] == 3)
    -IS_WINDOWS = (os.name == 'nt')
    -
    -
    -class Skipped(Exception):
    -    pass
    -
    -
    -def _indent(s, indent=4):
    -    """
    -    Add the given number of space characters to the beginning of
    -    every non-blank line in ``s``, and return the result.
    -    If the string ``s`` is Unicode, it is encoded using the stdout
    -    encoding and the ``backslashreplace`` error handler.
    -    """
    -    # After a 2to3 run the below code is bogus, so wrap it with a version check
    -    if sys.version_info[0] < 3:
    -        if isinstance(s, str):
    -            s = s.encode(pdoctest._encoding, 'backslashreplace')
    -    # This regexp matches the start of non-blank lines:
    -    return re.sub('(?m)^(?!$)', indent*' ', s)
    -
    -pdoctest._indent = _indent
    -
    -# ovverride reporter to maintain windows and python3
    -
    -
    -def _report_failure(self, out, test, example, got):
    -    """
    -    Report that the given example failed.
    -    """
    -    s = self._checker.output_difference(example, got, self.optionflags)
    -    s = s.encode('raw_unicode_escape').decode('utf8', 'ignore')
    -    out(self._failure_header(test, example) + s)
    -
    -if IS_PYTHON_3 and IS_WINDOWS:
    -    DocTestRunner.report_failure = _report_failure
    -
    -
    -
    [docs]def convert_to_native_paths(lst): - """ - Converts a list of '/' separated paths into a list of - native (os.sep separated) paths and converts to lowercase - if the system is case insensitive. - """ - newlst = [] - for i, rv in enumerate(lst): - rv = os.path.join(*rv.split("/")) - # on windows the slash after the colon is dropped - if sys.platform == "win32": - pos = rv.find(':') - if pos != -1: - if rv[pos + 1] != '\\': - rv = rv[:pos + 1] + '\\' + rv[pos + 1:] - newlst.append(sys_normcase(rv)) - return newlst - -
    -
    [docs]def get_sympy_dir(): - """ - Returns the root sympy directory and set the global value - indicating whether the system is case sensitive or not. - """ - global sys_case_insensitive - - this_file = os.path.abspath(__file__) - sympy_dir = os.path.join(os.path.dirname(this_file), "..", "..") - sympy_dir = os.path.normpath(sympy_dir) - sys_case_insensitive = (os.path.isdir(sympy_dir) and - os.path.isdir(sympy_dir.lower()) and - os.path.isdir(sympy_dir.upper())) - return sys_normcase(sympy_dir) - -
    -def sys_normcase(f): - if sys_case_insensitive: # global defined after call to get_sympy_dir() - return f.lower() - return f - - -
    [docs]def isgeneratorfunction(object): - """ - Return True if the object is a user-defined generator function. - - Generator function objects provides same attributes as functions. - - See isfunction.__doc__ for attributes listing. - - Adapted from Python 2.6. - """ - CO_GENERATOR = 0x20 - if (inspect.isfunction(object) or inspect.ismethod(object)) and \ - object.__code__.co_flags & CO_GENERATOR: - return True - return False - -
    -def setup_pprint(): - from sympy import pprint_use_unicode, init_printing - - # force pprint to be in ascii mode in doctests - pprint_use_unicode(False) - - # hook our nice, hash-stable strprinter - init_printing(pretty_print=False) - - -
    [docs]def run_in_subprocess_with_hash_randomization(function, function_args=(), - function_kwargs={}, command=sys.executable, - module='sympy.utilities.runtests', force=False): - """ - Run a function in a Python subprocess with hash randomization enabled. - - If hash randomization is not supported by the version of Python given, it - returns False. Otherwise, it returns the exit value of the command. The - function is passed to sys.exit(), so the return value of the function will - be the return value. - - The environment variable PYTHONHASHSEED is used to seed Python's hash - randomization. If it is set, this function will return False, because - starting a new subprocess is unnecessary in that case. If it is not set, - one is set at random, and the tests are run. Note that if this - environment variable is set when Python starts, hash randomization is - automatically enabled. To force a subprocess to be created even if - PYTHONHASHSEED is set, pass ``force=True``. This flag will not force a - subprocess in Python versions that do not support hash randomization (see - below), because those versions of Python do not support the ``-R`` flag. - - ``function`` should be a string name of a function that is importable from - the module ``module``, like "_test". The default for ``module`` is - "sympy.utilities.runtests". ``function_args`` and ``function_kwargs`` - should be a repr-able tuple and dict, respectively. The default Python - command is sys.executable, which is the currently running Python command. - - This function is necessary because the seed for hash randomization must be - set by the environment variable before Python starts. Hence, in order to - use a predetermined seed for tests, we must start Python in a separate - subprocess. - - Hash randomization was added in the minor Python versions 2.6.8, 2.7.3, - 3.1.5, and 3.2.3, and is enabled by default in all Python versions after - and including 3.3.0. - - Examples - ======== - - >>> from sympy.utilities.runtests import ( - ... run_in_subprocess_with_hash_randomization) - >>> # run the core tests in verbose mode - >>> run_in_subprocess_with_hash_randomization("_test", - ... function_args=("core",), - ... function_kwargs={'verbose': True}) # doctest: +SKIP - # Will return 0 if sys.executable supports hash randomization and tests - # pass, 1 if they fail, and False if it does not support hash - # randomization. - - """ - # Note, we must return False everywhere, not None, as subprocess.call will - # sometimes return None. - - # First check if the Python version supports hash randomization - # If it doesn't have this support, it won't reconize the -R flag - p = subprocess.Popen([command, "-RV"], stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - p.communicate() - if p.returncode != 0: - return False - - hash_seed = os.getenv("PYTHONHASHSEED") - if not hash_seed: - os.environ["PYTHONHASHSEED"] = str(random.randrange(2**32)) - else: - if not force: - return False - # Now run the command - commandstring = ("import sys; from %s import %s;sys.exit(%s(*%s, **%s))" % - (module, function, function, repr(function_args), - repr(function_kwargs))) - - try: - return subprocess.call([command, "-R", "-c", commandstring]) - finally: - # Put the environment variable back, so that it reads correctly for - # the current Python process. - if hash_seed is None: - del os.environ["PYTHONHASHSEED"] - else: - os.environ["PYTHONHASHSEED"] = hash_seed - -
    -
    [docs]def run_all_tests(test_args=(), test_kwargs={}, doctest_args=(), - doctest_kwargs={}, examples_args=(), examples_kwargs={'quiet': True}): - """ - Run all tests. - - Right now, this runs the regular tests (bin/test), the doctests - (bin/doctest), the examples (examples/all.py), and the sage tests (see - sympy/external/tests/test_sage.py). - - This is what ``setup.py test`` uses. - - You can pass arguments and keyword arguments to the test functions that - support them (for now, test, doctest, and the examples). See the - docstrings of those functions for a description of the available options. - - For example, to run the solvers tests with colors turned off: - - >>> from sympy.utilities.runtests import run_all_tests - >>> run_all_tests(test_args=("solvers",), - ... test_kwargs={"colors:False"}) # doctest: +SKIP - - """ - tests_successful = True - - try: - # Regular tests - if not test(*test_args, **test_kwargs): - # some regular test fails, so set the tests_successful - # flag to false and continue running the doctests - tests_successful = False - - # Doctests - print() - if not doctest(*doctest_args, **doctest_kwargs): - tests_successful = False - - # Examples - print() - sys.path.append("examples") - from all import run_examples # examples/all.py - if not run_examples(*examples_args, **examples_kwargs): - tests_successful = False - - # Sage tests - if not (sys.platform == "win32" or sys.version_info[0] == 3): - # run Sage tests; Sage currently doesn't support Windows or Python 3 - dev_null = open(os.devnull, 'w') - if subprocess.call("sage -v", shell=True, stdout=dev_null, - stderr=dev_null) == 0: - if subprocess.call("sage -python bin/test " - "sympy/external/tests/test_sage.py", shell=True) != 0: - tests_successful = False - - if tests_successful: - return - else: - # Return nonzero exit code - sys.exit(1) - except KeyboardInterrupt: - print() - print("DO *NOT* COMMIT!") - sys.exit(1) - -
    -
    [docs]def test(*paths, **kwargs): - """ - Run tests in the specified test_*.py files. - - Tests in a particular test_*.py file are run if any of the given strings - in ``paths`` matches a part of the test file's path. If ``paths=[]``, - tests in all test_*.py files are run. - - Notes: - - - If sort=False, tests are run in random order (not default). - - Paths can be entered in native system format or in unix, - forward-slash format. - - **Explanation of test results** - - ====== =============================================================== - Output Meaning - ====== =============================================================== - . passed - F failed - X XPassed (expected to fail but passed) - f XFAILed (expected to fail and indeed failed) - s skipped - w slow - T timeout (e.g., when ``--timeout`` is used) - K KeyboardInterrupt (when running the slow tests with ``--slow``, - you can interrupt one of them without killing the test runner) - ====== =============================================================== - - - Colors have no additional meaning and are used just to facilitate - interpreting the output. - - Examples - ======== - - >>> import sympy - - Run all tests: - - >>> sympy.test() # doctest: +SKIP - - Run one file: - - >>> sympy.test("sympy/core/tests/test_basic.py") # doctest: +SKIP - >>> sympy.test("_basic") # doctest: +SKIP - - Run all tests in sympy/functions/ and some particular file: - - >>> sympy.test("sympy/core/tests/test_basic.py", - ... "sympy/functions") # doctest: +SKIP - - Run all tests in sympy/core and sympy/utilities: - - >>> sympy.test("/core", "/util") # doctest: +SKIP - - Run specific test from a file: - - >>> sympy.test("sympy/core/tests/test_basic.py", - ... kw="test_equality") # doctest: +SKIP - - Run specific test from any file: - - >>> sympy.test(kw="subs") # doctest: +SKIP - - Run the tests with verbose mode on: - - >>> sympy.test(verbose=True) # doctest: +SKIP - - Don't sort the test output: - - >>> sympy.test(sort=False) # doctest: +SKIP - - Turn on post-mortem pdb: - - >>> sympy.test(pdb=True) # doctest: +SKIP - - Turn off colors: - - >>> sympy.test(colors=False) # doctest: +SKIP - - Force colors, even when the output is not to a terminal (this is useful, - e.g., if you are piping to ``less -r`` and you still want colors) - - >>> sympy.test(force_colors=False) # doctest: +SKIP - - The traceback verboseness can be set to "short" or "no" (default is - "short") - - >>> sympy.test(tb='no') # doctest: +SKIP - - You can disable running the tests in a separate subprocess using - ``subprocess=False``. This is done to support seeding hash randomization, - which is enabled by default in the Python versions where it is supported. - If subprocess=False, hash randomization is enabled/disabled according to - whether it has been enabled or not in the calling Python process. - However, even if it is enabled, the seed cannot be printed unless it is - called from a new Python process. - - Hash randomization was added in the minor Python versions 2.6.8, 2.7.3, - 3.1.5, and 3.2.3, and is enabled by default in all Python versions after - and including 3.3.0. - - If hash randomization is not supported ``subprocess=False`` is used - automatically. - - >>> sympy.test(subprocess=False) # doctest: +SKIP - - To set the hash randomization seed, set the environment variable - ``PYTHONHASHSEED`` before running the tests. This can be done from within - Python using - - >>> import os - >>> os.environ['PYTHONHASHSEED'] = 42 # doctest: +SKIP - - Or from the command line using - - $ PYTHONHASHSEED=42 ./bin/test - - If the seed is not set, a random seed will be chosen. - - Note that to reproduce the same hash values, you must use both the same as - well as the same architecture (32-bit vs. 64-bit). - - """ - subprocess = kwargs.pop("subprocess", True) - if subprocess: - ret = run_in_subprocess_with_hash_randomization("_test", - function_args=paths, function_kwargs=kwargs) - if ret is not False: - return not bool(ret) - return not bool(_test(*paths, **kwargs)) - -
    -def _test(*paths, **kwargs): - """ - Internal function that actually runs the tests. - - All keyword arguments from ``test()`` are passed to this function except for - ``subprocess``. - - Returns 0 if tests passed and 1 if they failed. See the docstring of - ``test()`` for more information. - """ - verbose = kwargs.get("verbose", False) - tb = kwargs.get("tb", "short") - kw = kwargs.get("kw", "") - post_mortem = kwargs.get("pdb", False) - colors = kwargs.get("colors", True) - force_colors = kwargs.get("force_colors", False) - sort = kwargs.get("sort", True) - seed = kwargs.get("seed", None) - if seed is None: - seed = random.randrange(100000000) - timeout = kwargs.get("timeout", False) - slow = kwargs.get("slow", False) - r = PyTestReporter(verbose=verbose, tb=tb, colors=colors, - force_colors=force_colors) - t = SymPyTests(r, kw, post_mortem, seed) - - # Disable warnings for external modules - import sympy.external - sympy.external.importtools.WARN_OLD_VERSION = False - sympy.external.importtools.WARN_NOT_INSTALLED = False - - test_files = t.get_test_files('sympy') - - if len(paths) == 0: - t._testfiles.extend(test_files) - else: - paths = convert_to_native_paths(paths) - matched = [] - for f in test_files: - basename = os.path.basename(f) - for p in paths: - if p in f or fnmatch(basename, p): - matched.append(f) - break - t._testfiles.extend(matched) - - return int(not t.test(sort=sort, timeout=timeout, slow=slow)) - - -
    [docs]def doctest(*paths, **kwargs): - """ - Runs doctests in all \*.py files in the sympy directory which match - any of the given strings in ``paths`` or all tests if paths=[]. - - Notes: - - - Paths can be entered in native system format or in unix, - forward-slash format. - - Files that are on the blacklist can be tested by providing - their path; they are only excluded if no paths are given. - - Examples - ======== - - >>> import sympy - - Run all tests: - - >>> sympy.doctest() # doctest: +SKIP - - Run one file: - - >>> sympy.doctest("sympy/core/basic.py") # doctest: +SKIP - >>> sympy.doctest("polynomial.rst") # doctest: +SKIP - - Run all tests in sympy/functions/ and some particular file: - - >>> sympy.doctest("/functions", "basic.py") # doctest: +SKIP - - Run any file having polynomial in its name, doc/src/modules/polynomial.rst, - sympy/functions/special/polynomials.py, and sympy/polys/polynomial.py: - - >>> sympy.doctest("polynomial") # doctest: +SKIP - - The ``subprocess`` and ``verbose`` options are the same as with the function - ``test()``. See the docstring of that function for more information. - """ - subprocess = kwargs.pop("subprocess", True) - if subprocess: - ret = run_in_subprocess_with_hash_randomization("_doctest", - function_args=paths, function_kwargs=kwargs) - if ret is not False: - return not bool(ret) - return not bool(_doctest(*paths, **kwargs)) - -
    -def _doctest(*paths, **kwargs): - """ - Internal function that actually runs the doctests. - - All keyword arguments from ``doctest()`` are passed to this function - except for ``subprocess``. - - Returns 0 if tests passed and 1 if they failed. See the docstrings of - ``doctest()`` and ``test()`` for more information. - """ - normal = kwargs.get("normal", False) - verbose = kwargs.get("verbose", False) - blacklist = kwargs.get("blacklist", []) - blacklist.extend([ - "doc/src/modules/mpmath", # needs to be fixed upstream - "sympy/mpmath", # needs to be fixed upstream - "doc/src/modules/plotting.rst", # generates live plots - # "sympy/plotting", # generates live plots - "sympy/plotting/pygletplot", # generates live plots - "sympy/statistics", # prints a deprecation - "doc/src/modules/statistics.rst", # warning (the module is deprecated) - "sympy/utilities/compilef.py", # needs tcc - "sympy/utilities/autowrap.py", # needs installed compiler - "sympy/galgebra/GA.py", # needs numpy - "sympy/galgebra/latex_ex.py", # needs numpy - "sympy/conftest.py", # needs py.test - "sympy/utilities/benchmarking.py", # needs py.test - "examples/advanced/autowrap_integrators.py", # needs numpy - "examples/advanced/autowrap_ufuncify.py", # needs numpy - "examples/intermediate/mplot2d.py", - # needs numpy and matplotlib - "examples/intermediate/mplot3d.py", - # needs numpy and matplotlib - "examples/intermediate/sample.py", # needs numpy - ]) - blacklist = convert_to_native_paths(blacklist) - - # Disable warnings for external modules - import sympy.external - sympy.external.importtools.WARN_OLD_VERSION = False - sympy.external.importtools.WARN_NOT_INSTALLED = False - - r = PyTestReporter(verbose) - t = SymPyDocTests(r, normal) - - test_files = t.get_test_files('sympy') - test_files.extend(t.get_test_files('examples', init_only=False)) - - not_blacklisted = [f for f in test_files - if not any(b in f for b in blacklist)] - if len(paths) == 0: - t._testfiles.extend(not_blacklisted) - else: - # take only what was requested...but not blacklisted items - # and allow for partial match anywhere or fnmatch of name - paths = convert_to_native_paths(paths) - matched = [] - for f in not_blacklisted: - basename = os.path.basename(f) - for p in paths: - if p in f or fnmatch(basename, p): - matched.append(f) - break - t._testfiles.extend(matched) - - # run the tests and record the result for this *py portion of the tests - if t._testfiles: - failed = not t.test() - else: - failed = False - - # N.B. - # -------------------------------------------------------------------- - # Here we test *.rst files at or below doc/src. Code from these must - # be self supporting in terms of imports since there is no importing - # of necessary modules by doctest.testfile. If you try to pass *.py - # files through this they might fail because they will lack the needed - # imports and smarter parsing that can be done with source code. - # - test_files = t.get_test_files('doc/src', '*.rst', init_only=False) - test_files.sort() - - not_blacklisted = [f for f in test_files - if not any(b in f for b in blacklist)] - - if len(paths) == 0: - matched = not_blacklisted - else: - # Take only what was requested as long as it's not on the blacklist. - # Paths were already made native in *py tests so don't repeat here. - # There's no chance of having a *py file slip through since we - # only have *rst files in test_files. - matched = [] - for f in not_blacklisted: - basename = os.path.basename(f) - for p in paths: - if p in f or fnmatch(basename, p): - matched.append(f) - break - - setup_pprint() - first_report = True - for rst_file in matched: - if not os.path.isfile(rst_file): - continue - old_displayhook = sys.displayhook - try: - # out = pdoctest.testfile( - # rst_file, module_relative=False, encoding='utf-8', - # optionflags=pdoctest.ELLIPSIS | pdoctest.NORMALIZE_WHITESPACE) - out = sympytestfile( - rst_file, module_relative=False, encoding='utf-8', - optionflags=pdoctest.ELLIPSIS | pdoctest.NORMALIZE_WHITESPACE | - pdoctest.IGNORE_EXCEPTION_DETAIL) - finally: - # make sure we return to the original displayhook in case some - # doctest has changed that - sys.displayhook = old_displayhook - - rstfailed, tested = out - if tested: - failed = rstfailed or failed - if first_report: - first_report = False - msg = 'rst doctests start' - if not t._testfiles: - r.start(msg=msg) - else: - r.write_center(msg) - print() - # use as the id, everything past the first 'sympy' - file_id = rst_file[rst_file.find('sympy') + len('sympy') + 1:] - print(file_id, end=' ') - # get at least the name out so it is know who is being tested - wid = r.terminal_width - len(file_id) - 1 # update width - test_file = '[%s]' % (tested) - report = '[%s]' % (rstfailed or 'OK') - print(''.join( - [test_file, ' '*(wid - len(test_file) - len(report)), report])) - - # the doctests for *py will have printed this message already if there was - # a failure, so now only print it if there was intervening reporting by - # testing the *rst as evidenced by first_report no longer being True. - if not first_report and failed: - print() - print("DO *NOT* COMMIT!") - - return int(failed) - -# The Python 2.5 doctest runner uses a tuple, but in 2.6+, it uses a namedtuple -# (which doesn't exist in 2.5-) -if sys.version_info[:2] > (2, 5): - from collections import namedtuple - SymPyTestResults = namedtuple('TestResults', 'failed attempted') -else: - SymPyTestResults = lambda a, b: (a, b) - - -
    [docs]def sympytestfile(filename, module_relative=True, name=None, package=None, - globs=None, verbose=None, report=True, optionflags=0, - extraglobs=None, raise_on_error=False, - parser=pdoctest.DocTestParser(), encoding=None): - """ - Test examples in the given file. Return (#failures, #tests). - - Optional keyword arg ``module_relative`` specifies how filenames - should be interpreted: - - - If ``module_relative`` is True (the default), then ``filename`` - specifies a module-relative path. By default, this path is - relative to the calling module's directory; but if the - ``package`` argument is specified, then it is relative to that - package. To ensure os-independence, ``filename`` should use - "/" characters to separate path segments, and should not - be an absolute path (i.e., it may not begin with "/"). - - - If ``module_relative`` is False, then ``filename`` specifies an - os-specific path. The path may be absolute or relative (to - the current working directory). - - Optional keyword arg ``name`` gives the name of the test; by default - use the file's basename. - - Optional keyword argument ``package`` is a Python package or the - name of a Python package whose directory should be used as the - base directory for a module relative filename. If no package is - specified, then the calling module's directory is used as the base - directory for module relative filenames. It is an error to - specify ``package`` if ``module_relative`` is False. - - Optional keyword arg ``globs`` gives a dict to be used as the globals - when executing examples; by default, use {}. A copy of this dict - is actually used for each docstring, so that each docstring's - examples start with a clean slate. - - Optional keyword arg ``extraglobs`` gives a dictionary that should be - merged into the globals that are used to execute examples. By - default, no extra globals are used. - - Optional keyword arg ``verbose`` prints lots of stuff if true, prints - only failures if false; by default, it's true iff "-v" is in sys.argv. - - Optional keyword arg ``report`` prints a summary at the end when true, - else prints nothing at the end. In verbose mode, the summary is - detailed, else very brief (in fact, empty if all tests passed). - - Optional keyword arg ``optionflags`` or's together module constants, - and defaults to 0. Possible values (see the docs for details): - - - DONT_ACCEPT_TRUE_FOR_1 - - DONT_ACCEPT_BLANKLINE - - NORMALIZE_WHITESPACE - - ELLIPSIS - - SKIP - - IGNORE_EXCEPTION_DETAIL - - REPORT_UDIFF - - REPORT_CDIFF - - REPORT_NDIFF - - REPORT_ONLY_FIRST_FAILURE - - Optional keyword arg ``raise_on_error`` raises an exception on the - first unexpected exception or failure. This allows failures to be - post-mortem debugged. - - Optional keyword arg ``parser`` specifies a DocTestParser (or - subclass) that should be used to extract tests from the files. - - Optional keyword arg ``encoding`` specifies an encoding that should - be used to convert the file to unicode. - - Advanced tomfoolery: testmod runs methods of a local instance of - class doctest.Tester, then merges the results into (or creates) - global Tester instance doctest.master. Methods of doctest.master - can be called directly too, if you want to do something unusual. - Passing report=0 to testmod is especially useful then, to delay - displaying a summary. Invoke doctest.master.summarize(verbose) - when you're done fiddling. - """ - if package and not module_relative: - raise ValueError("Package may only be specified for module-" - "relative paths.") - - # Relativize the path - if not IS_PYTHON_3: - text, filename = pdoctest._load_testfile( - filename, package, module_relative) - if encoding is not None: - text = text.decode(encoding) - else: - text, filename = pdoctest._load_testfile( - filename, package, module_relative, encoding) - - # If no name was given, then use the file's name. - if name is None: - name = os.path.basename(filename) - - # Assemble the globals. - if globs is None: - globs = {} - else: - globs = globs.copy() - if extraglobs is not None: - globs.update(extraglobs) - if '__name__' not in globs: - globs['__name__'] = '__main__' - - if raise_on_error: - runner = pdoctest.DebugRunner(verbose=verbose, optionflags=optionflags) - else: - runner = SymPyDocTestRunner(verbose=verbose, optionflags=optionflags) - - # Read the file, convert it to a test, and run it. - test = parser.get_doctest(text, globs, name, filename, 0) - runner.run(test) - - if report: - runner.summarize() - - if pdoctest.master is None: - pdoctest.master = runner - else: - pdoctest.master.merge(runner) - - return SymPyTestResults(runner.failures, runner.tries) - -
    -class SymPyTests(object): - - def __init__(self, reporter, kw="", post_mortem=False, - seed=random.random()): - self._post_mortem = post_mortem - self._kw = kw - self._count = 0 - self._root_dir = sympy_dir - self._reporter = reporter - self._reporter.root_dir(self._root_dir) - self._testfiles = [] - self._seed = seed - - def test(self, sort=False, timeout=False, slow=False): - """ - Runs the tests returning True if all tests pass, otherwise False. - - If sort=False run tests in random order. - """ - if sort: - self._testfiles.sort() - else: - from random import shuffle - random.seed(self._seed) - shuffle(self._testfiles) - self._reporter.start(self._seed) - for f in self._testfiles: - try: - self.test_file(f, sort, timeout, slow) - except KeyboardInterrupt: - print(" interrupted by user") - self._reporter.finish() - raise - return self._reporter.finish() - - def test_file(self, filename, sort=True, timeout=False, slow=False): - clear_cache() - self._count += 1 - gl = {'__file__': filename} - random.seed(self._seed) - try: - if IS_PYTHON_3: - with open(filename, encoding="utf8") as f: - source = f.read() - c = compile(source, filename, 'exec') - exec(c, gl) - else: - exec(compile(open(filename).read(), filename, 'exec'), gl) - except (SystemExit, KeyboardInterrupt): - raise - except ImportError: - self._reporter.import_error(filename, sys.exc_info()) - return - pytestfile = "" - if "XFAIL" in gl: - pytestfile = inspect.getsourcefile(gl["XFAIL"]) - pytestfile2 = "" - if "SLOW" in gl: - pytestfile2 = inspect.getsourcefile(gl["SLOW"]) - disabled = gl.get("disabled", False) - if disabled: - funcs = [] - else: - # we need to filter only those functions that begin with 'test_' - # that are defined in the testing file or in the file where - # is defined the XFAIL decorator - funcs = [gl[f] for f in list(gl.keys()) if f.startswith("test_") and - (inspect.isfunction(gl[f]) or inspect.ismethod(gl[f])) and - (inspect.getsourcefile(gl[f]) == filename or - inspect.getsourcefile(gl[f]) == pytestfile or - inspect.getsourcefile(gl[f]) == pytestfile2)] - if slow: - funcs = [f for f in funcs if getattr(f, '_slow', False)] - # Sorting of XFAILed functions isn't fixed yet :-( - funcs.sort(key=lambda x: inspect.getsourcelines(x)[1]) - i = 0 - while i < len(funcs): - if isgeneratorfunction(funcs[i]): - # some tests can be generators, that return the actual - # test functions. We unpack it below: - f = funcs.pop(i) - for fg in f(): - func = fg[0] - args = fg[1:] - fgw = lambda: func(*args) - funcs.insert(i, fgw) - i += 1 - else: - i += 1 - # drop functions that are not selected with the keyword expression: - funcs = [x for x in funcs if self.matches(x)] - - if not funcs: - return - self._reporter.entering_filename(filename, len(funcs)) - if not sort: - random.shuffle(funcs) - for f in funcs: - self._reporter.entering_test(f) - try: - if getattr(f, '_slow', False) and not slow: - raise Skipped("Slow") - if timeout: - self._timeout(f, timeout) - else: - random.seed(self._seed) - f() - except KeyboardInterrupt: - if getattr(f, '_slow', False): - self._reporter.test_skip("KeyboardInterrupt") - else: - raise - except Exception: - if timeout: - signal.alarm(0) # Disable the alarm. It could not be handled before. - t, v, tr = sys.exc_info() - if t is AssertionError: - self._reporter.test_fail((t, v, tr)) - if self._post_mortem: - pdb.post_mortem(tr) - elif t.__name__ == "Skipped": - self._reporter.test_skip(v) - elif t.__name__ == "XFail": - self._reporter.test_xfail() - elif t.__name__ == "XPass": - self._reporter.test_xpass(v) - else: - self._reporter.test_exception((t, v, tr)) - if self._post_mortem: - pdb.post_mortem(tr) - else: - self._reporter.test_pass() - self._reporter.leaving_filename() - - def _timeout(self, function, timeout): - def callback(x, y): - signal.alarm(0) - raise Skipped("Timeout") - signal.signal(signal.SIGALRM, callback) - signal.alarm(timeout) # Set an alarm with a given timeout - function() - signal.alarm(0) # Disable the alarm - - def matches(self, x): - """ - Does the keyword expression self._kw match "x"? Returns True/False. - - Always returns True if self._kw is "". - """ - if self._kw == "": - return True - return x.__name__.find(self._kw) != -1 - - def get_test_files(self, dir, pat='test_*.py'): - """ - Returns the list of test_*.py (default) files at or below directory - ``dir`` relative to the sympy home directory. - """ - dir = os.path.join(self._root_dir, convert_to_native_paths([dir])[0]) - - g = [] - for path, folders, files in os.walk(dir): - g.extend([os.path.join(path, f) for f in files if fnmatch(f, pat)]) - - return [sys_normcase(gi) for gi in g] - - -class SymPyDocTests(object): - - def __init__(self, reporter, normal): - self._count = 0 - self._root_dir = sympy_dir - self._reporter = reporter - self._reporter.root_dir(self._root_dir) - self._normal = normal - - self._testfiles = [] - - def test(self): - """ - Runs the tests and returns True if all tests pass, otherwise False. - """ - self._reporter.start() - for f in self._testfiles: - try: - self.test_file(f) - except KeyboardInterrupt: - print(" interrupted by user") - self._reporter.finish() - raise - return self._reporter.finish() - - def test_file(self, filename): - clear_cache() - - from io import StringIO - - rel_name = filename[len(self._root_dir) + 1:] - dirname, file = os.path.split(filename) - module = rel_name.replace(os.sep, '.')[:-3] - - if rel_name.startswith("examples"): - # Examples files do not have __init__.py files, - # So we have to temporarily extend sys.path to import them - sys.path.insert(0, dirname) - module = file[:-3] # remove ".py" - setup_pprint() - try: - module = pdoctest._normalize_module(module) - tests = SymPyDocTestFinder().find(module) - except (SystemExit, KeyboardInterrupt): - raise - except ImportError: - self._reporter.import_error(filename, sys.exc_info()) - return - finally: - if rel_name.startswith("examples"): - del sys.path[0] - - tests = [test for test in tests if len(test.examples) > 0] - # By default tests are sorted by alphabetical order by function name. - # We sort by line number so one can edit the file sequentially from - # bottom to top. However, if there are decorated functions, their line - # numbers will be too large and for now one must just search for these - # by text and function name. - tests.sort(key=lambda x: -x.lineno) - - if not tests: - return - self._reporter.entering_filename(filename, len(tests)) - for test in tests: - assert len(test.examples) != 0 - runner = SymPyDocTestRunner(optionflags=pdoctest.ELLIPSIS | - pdoctest.NORMALIZE_WHITESPACE | - pdoctest.IGNORE_EXCEPTION_DETAIL) - old = sys.stdout - new = StringIO() - sys.stdout = new - # If the testing is normal, the doctests get importing magic to - # provide the global namespace. If not normal (the default) then - # then must run on their own; all imports must be explicit within - # a function's docstring. Once imported that import will be - # available to the rest of the tests in a given function's - # docstring (unless clear_globs=True below). - if not self._normal: - test.globs = {} - # if this is uncommented then all the test would get is what - # comes by default with a "from sympy import *" - #exec('from sympy import *') in test.globs - try: - f, t = runner.run(test, out=new.write, clear_globs=False) - except KeyboardInterrupt: - raise - finally: - sys.stdout = old - if f > 0: - self._reporter.doctest_fail(test.name, new.getvalue()) - else: - self._reporter.test_pass() - self._reporter.leaving_filename() - - def get_test_files(self, dir, pat='*.py', init_only=True): - """ - Returns the list of \*.py files (default) from which docstrings - will be tested which are at or below directory ``dir``. By default, - only those that have an __init__.py in their parent directory - and do not start with ``test_`` will be included. - """ - def importable(x): - """ - Checks if given pathname x is an importable module by checking for - __init__.py file. - - Returns True/False. - - Currently we only test if the __init__.py file exists in the - directory with the file "x" (in theory we should also test all the - parent dirs). - """ - init_py = os.path.join(os.path.dirname(x), "__init__.py") - return os.path.exists(init_py) - - dir = os.path.join(self._root_dir, convert_to_native_paths([dir])[0]) - - g = [] - for path, folders, files in os.walk(dir): - g.extend([os.path.join(path, f) for f in files - if not f.startswith('test_') and fnmatch(f, pat)]) - if init_only: - # skip files that are not importable (i.e. missing __init__.py) - g = [x for x in g if importable(x)] - - return [sys_normcase(gi) for gi in g] - - -
    [docs]class SymPyDocTestFinder(DocTestFinder): - """ - A class used to extract the DocTests that are relevant to a given - object, from its docstring and the docstrings of its contained - objects. Doctests can currently be extracted from the following - object types: modules, functions, classes, methods, staticmethods, - classmethods, and properties. - - Modified from doctest's version by looking harder for code in the - case that it looks like the the code comes from a different module. - In the case of decorated functions (e.g. @vectorize) they appear - to come from a different module (e.g. multidemensional) even though - their code is not there. - """ - - def _find(self, tests, obj, name, module, source_lines, globs, seen): - """ - Find tests for the given object and any contained objects, and - add them to ``tests``. - """ - if self._verbose: - print('Finding tests in %s' % name) - - # If we've already processed this object, then ignore it. - if id(obj) in seen: - return - seen[id(obj)] = 1 - - # Make sure we don't run doctests for classes outside of sympy, such - # as in numpy or scipy. - if inspect.isclass(obj): - if obj.__module__.split('.')[0] != 'sympy': - return - - # Find a test for this object, and add it to the list of tests. - test = self._get_test(obj, name, module, globs, source_lines) - if test is not None: - tests.append(test) - - # Look for tests in a module's contained objects. - if inspect.ismodule(obj) and self._recurse: - for rawname, val in list(obj.__dict__.items()): - # Recurse to functions & classes. - if inspect.isfunction(val) or inspect.isclass(val): - in_module = self._from_module(module, val) - if not in_module: - # double check in case this function is decorated - # and just appears to come from a different module. - pat = r'\s*(def|class)\s+%s\s*\(' % rawname - PAT = pre.compile(pat) - in_module = any( - PAT.match(line) for line in source_lines) - if in_module: - try: - valname = '%s.%s' % (name, rawname) - self._find(tests, val, valname, module, - source_lines, globs, seen) - except KeyboardInterrupt: - raise - except ValueError: - raise - except Exception: - pass - - # Look for tests in a module's __test__ dictionary. - if inspect.ismodule(obj) and self._recurse: - for valname, val in list(getattr(obj, '__test__', {}).items()): - if not isinstance(valname, str): - raise ValueError("SymPyDocTestFinder.find: __test__ keys " - "must be strings: %r" % - (type(valname),)) - if not (inspect.isfunction(val) or inspect.isclass(val) or - inspect.ismethod(val) or inspect.ismodule(val) or - isinstance(val, str)): - raise ValueError("SymPyDocTestFinder.find: __test__ values " - "must be strings, functions, methods, " - "classes, or modules: %r" % - (type(val),)) - valname = '%s.__test__.%s' % (name, valname) - self._find(tests, val, valname, module, source_lines, - globs, seen) - - # Look for tests in a class's contained objects. - if inspect.isclass(obj) and self._recurse: - for valname, val in list(obj.__dict__.items()): - # Special handling for staticmethod/classmethod. - if isinstance(val, staticmethod): - val = getattr(obj, valname) - if isinstance(val, classmethod): - val = getattr(obj, valname).__func__ - - # Recurse to methods, properties, and nested classes. - if (inspect.isfunction(val) or - inspect.isclass(val) or - isinstance(val, property)): - in_module = self._from_module(module, val) - if not in_module: - # "double check" again - pat = r'\s*(def|class)\s+%s\s*\(' % valname - PAT = pre.compile(pat) - in_module = any(PAT.match(line) for line in - source_lines) - if in_module: - valname = '%s.%s' % (name, valname) - self._find(tests, val, valname, module, source_lines, - globs, seen) - - def _get_test(self, obj, name, module, globs, source_lines): - """ - Return a DocTest for the given object, if it defines a docstring; - otherwise, return None. - """ - # Extract the object's docstring. If it doesn't have one, - # then return None (no test for this object). - if isinstance(obj, str): - docstring = obj - else: - try: - if obj.__doc__ is None: - docstring = '' - else: - docstring = obj.__doc__ - if not isinstance(docstring, str): - docstring = str(docstring) - except (TypeError, AttributeError): - docstring = '' - - # Find the docstring's location in the file. - lineno = self._find_lineno(obj, source_lines) - - if lineno is None: - # if None, then _find_lineno couldn't find the docstring. - # But IT IS STILL THERE. Likely it was decorated or something - # (i.e., @property docstrings have lineno == None) - # TODO: Write our own _find_lineno that is smarter in this regard. - # Until then, just give it a dummy lineno. This is just used for - # sorting the tests, so the only bad effect is that they will - # appear last instead of in the order that they appear in the file. - # lineno is also used to report the offending line of a failing - # doctest, which is another reason to fix this. See issue 1947. - lineno = 0 - - # Don't bother if the docstring is empty. - if self._exclude_empty and not docstring: - return None - - # Return a DocTest for this object. - if module is None: - filename = None - else: - filename = getattr(module, '__file__', module.__name__) - if filename[-4:] in (".pyc", ".pyo"): - filename = filename[:-1] - return self._parser.get_doctest(docstring, globs, name, - filename, lineno) - -
    -
    [docs]class SymPyDocTestRunner(DocTestRunner): - """ - A class used to run DocTest test cases, and accumulate statistics. - The ``run`` method is used to process a single DocTest case. It - returns a tuple ``(f, t)``, where ``t`` is the number of test cases - tried, and ``f`` is the number of test cases that failed. - - Modified from the doctest version to not reset the sys.displayhook (see - issue 2041). - - See the docstring of the original DocTestRunner for more information. - """ - -
    [docs] def run(self, test, compileflags=None, out=None, clear_globs=True): - """ - Run the examples in ``test``, and display the results using the - writer function ``out``. - - The examples are run in the namespace ``test.globs``. If - ``clear_globs`` is true (the default), then this namespace will - be cleared after the test runs, to help with garbage - collection. If you would like to examine the namespace after - the test completes, then use ``clear_globs=False``. - - ``compileflags`` gives the set of flags that should be used by - the Python compiler when running the examples. If not - specified, then it will default to the set of future-import - flags that apply to ``globs``. - - The output of each example is checked using - ``SymPyDocTestRunner.check_output``, and the results are - formatted by the ``SymPyDocTestRunner.report_*`` methods. - """ - self.test = test - - if compileflags is None: - compileflags = pdoctest._extract_future_flags(test.globs) - - save_stdout = sys.stdout - if out is None: - out = save_stdout.write - sys.stdout = self._fakeout - - # Patch pdb.set_trace to restore sys.stdout during interactive - # debugging (so it's not still redirected to self._fakeout). - # Note that the interactive output will go to *our* - # save_stdout, even if that's not the real sys.stdout; this - # allows us to write test cases for the set_trace behavior. - save_set_trace = pdb.set_trace - self.debugger = pdoctest._OutputRedirectingPdb(save_stdout) - self.debugger.reset() - pdb.set_trace = self.debugger.set_trace - - # Patch linecache.getlines, so we can see the example's source - # when we're inside the debugger. - self.save_linecache_getlines = pdoctest.linecache.getlines - linecache.getlines = self.__patched_linecache_getlines - - try: - return self.__run(test, compileflags, out) - finally: - sys.stdout = save_stdout - pdb.set_trace = save_set_trace - linecache.getlines = self.save_linecache_getlines - if clear_globs: - test.globs.clear() - -# We have to override the name mangled methods.
    -SymPyDocTestRunner._SymPyDocTestRunner__patched_linecache_getlines = \ - DocTestRunner._DocTestRunner__patched_linecache_getlines -SymPyDocTestRunner._SymPyDocTestRunner__run = DocTestRunner._DocTestRunner__run -SymPyDocTestRunner._SymPyDocTestRunner__record_outcome = \ - DocTestRunner._DocTestRunner__record_outcome - - -
    [docs]class Reporter(object): - """ - Parent class for all reporters. - """ - pass - -
    -
    [docs]class PyTestReporter(Reporter): - """ - Py.test like reporter. Should produce output identical to py.test. - """ - - def __init__(self, verbose=False, tb="short", colors=True, - force_colors=False): - self._verbose = verbose - self._tb_style = tb - self._colors = colors - self._force_colors = force_colors - self._xfailed = 0 - self._xpassed = [] - self._failed = [] - self._failed_doctest = [] - self._passed = 0 - self._skipped = 0 - self._exceptions = [] - self._terminal_width = None - self._default_width = 80 - - # this tracks the x-position of the cursor (useful for positioning - # things on the screen), without the need for any readline library: - self._write_pos = 0 - self._line_wrap = False - - def root_dir(self, dir): - self._root_dir = dir - - @property - def terminal_width(self): - if self._terminal_width is not None: - return self._terminal_width - - def findout_terminal_width(): - if sys.platform == "win32": - # Windows support is based on: - # - # http://code.activestate.com/recipes/ - # 440694-determine-size-of-console-window-on-windows/ - - from ctypes import windll, create_string_buffer - - h = windll.kernel32.GetStdHandle(-12) - csbi = create_string_buffer(22) - res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi) - - if res: - import struct - (_, _, _, _, _, left, _, right, _, _, _) = \ - struct.unpack("hhhhHhhhhhh", csbi.raw) - return right - left - else: - return self._default_width - - if hasattr(sys.stdout, 'isatty') and not sys.stdout.isatty(): - return self._default_width # leave PIPEs alone - - try: - process = subprocess.Popen(['stty', '-a'], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - stdout = process.stdout.read() - if sys.version_info[0] > 2: - stdout = stdout.decode("utf-8") - except (OSError, IOError): - pass - else: - # We support the following output formats from stty: - # - # 1) Linux -> columns 80 - # 2) OS X -> 80 columns - # 3) Solaris -> columns = 80 - - re_linux = r"columns\s+(?P<columns>\d+);" - re_osx = r"(?P<columns>\d+)\s*columns;" - re_solaris = r"columns\s+=\s+(?P<columns>\d+);" - - for regex in (re_linux, re_osx, re_solaris): - match = re.search(regex, stdout) - - if match is not None: - columns = match.group('columns') - - try: - return int(columns) - except ValueError: - pass - - return self._default_width - - width = findout_terminal_width() - self._terminal_width = width - - return width - -
    [docs] def write(self, text, color="", align="left", width=None, - force_colors=False): - """ - Prints a text on the screen. - - It uses sys.stdout.write(), so no readline library is necessary. - - Parameters - ========== - - color : choose from the colors below, "" means default color - align : "left"/"right", "left" is a normal print, "right" is aligned on - the right-hand side of the screen, filled with spaces if - necessary - width : the screen width - - """ - color_templates = ( - ("Black", "0;30"), - ("Red", "0;31"), - ("Green", "0;32"), - ("Brown", "0;33"), - ("Blue", "0;34"), - ("Purple", "0;35"), - ("Cyan", "0;36"), - ("LightGray", "0;37"), - ("DarkGray", "1;30"), - ("LightRed", "1;31"), - ("LightGreen", "1;32"), - ("Yellow", "1;33"), - ("LightBlue", "1;34"), - ("LightPurple", "1;35"), - ("LightCyan", "1;36"), - ("White", "1;37"), - ) - - colors = {} - - for name, value in color_templates: - colors[name] = value - c_normal = '\033[0m' - c_color = '\033[%sm' - - if width is None: - width = self.terminal_width - - if align == "right": - if self._write_pos + len(text) > width: - # we don't fit on the current line, create a new line - self.write("\n") - self.write(" "*(width - self._write_pos - len(text))) - - if not self._force_colors and hasattr(sys.stdout, 'isatty') and not \ - sys.stdout.isatty(): - # the stdout is not a terminal, this for example happens if the - # output is piped to less, e.g. "bin/test | less". In this case, - # the terminal control sequences would be printed verbatim, so - # don't use any colors. - color = "" - elif sys.platform == "win32": - # Windows consoles don't support ANSI escape sequences - color = "" - elif not self._colors: - color = "" - - if self._line_wrap: - if text[0] != "\n": - sys.stdout.write("\n") - - # Avoid UnicodeEncodeError when printing out test failures - if IS_PYTHON_3 and IS_WINDOWS: - text = text.encode('raw_unicode_escape').decode('utf8', 'ignore') - elif IS_PYTHON_3 and not sys.stdout.encoding.lower().startswith('utf'): - text = text.encode(sys.stdout.encoding, 'backslashreplace' - ).decode(sys.stdout.encoding) - - if color == "": - sys.stdout.write(text) - else: - sys.stdout.write("%s%s%s" % - (c_color % colors[color], text, c_normal)) - sys.stdout.flush() - l = text.rfind("\n") - if l == -1: - self._write_pos += len(text) - else: - self._write_pos = len(text) - l - 1 - self._line_wrap = self._write_pos >= width - self._write_pos %= width -
    - def write_center(self, text, delim="="): - width = self.terminal_width - if text != "": - text = " %s " % text - idx = (width - len(text)) // 2 - t = delim*idx + text + delim*(width - idx - len(text)) - self.write(t + "\n") - - def write_exception(self, e, val, tb): - t = traceback.extract_tb(tb) - # remove the first item, as that is always runtests.py - t = t[1:] - t = traceback.format_list(t) - self.write("".join(t)) - t = traceback.format_exception_only(e, val) - self.write("".join(t)) - - def start(self, seed=None, msg="test process starts"): - self.write_center(msg) - executable = sys.executable - v = tuple(sys.version_info) - python_version = "%s.%s.%s-%s-%s" % v - self.write("executable: %s (%s)\n" % - (executable, python_version)) - from .misc import ARCH - self.write("architecture: %s\n" % ARCH) - from sympy.core.cache import USE_CACHE - self.write("cache: %s\n" % USE_CACHE) - from sympy.polys.domains import GROUND_TYPES - self.write("ground types: %s\n" % GROUND_TYPES) - if seed is not None: - self.write("random seed: %d\n" % seed) - from .misc import HASH_RANDOMIZATION - self.write("hash randomization: ") - hash_seed = os.getenv("PYTHONHASHSEED") - if HASH_RANDOMIZATION and (hash_seed == "random" or int(hash_seed)): - self.write("on (PYTHONHASHSEED=%s)\n" % hash_seed) - else: - self.write("off\n") - self.write('\n') - self._t_start = clock() - - def finish(self): - self._t_end = clock() - self.write("\n") - global text, linelen - text = "tests finished: %d passed, " % self._passed - linelen = len(text) - - def add_text(mytext): - global text, linelen - """Break new text if too long.""" - if linelen + len(mytext) > self.terminal_width: - text += '\n' - linelen = 0 - text += mytext - linelen += len(mytext) - - if len(self._failed) > 0: - add_text("%d failed, " % len(self._failed)) - if len(self._failed_doctest) > 0: - add_text("%d failed, " % len(self._failed_doctest)) - if self._skipped > 0: - add_text("%d skipped, " % self._skipped) - if self._xfailed > 0: - add_text("%d expected to fail, " % self._xfailed) - if len(self._xpassed) > 0: - add_text("%d expected to fail but passed, " % len(self._xpassed)) - if len(self._exceptions) > 0: - add_text("%d exceptions, " % len(self._exceptions)) - add_text("in %.2f seconds" % (self._t_end - self._t_start)) - - if len(self._xpassed) > 0: - self.write_center("xpassed tests", "_") - for e in self._xpassed: - self.write("%s: %s\n" % (e[0], e[1])) - self.write("\n") - - if self._tb_style != "no" and len(self._exceptions) > 0: - #self.write_center("These tests raised an exception", "_") - for e in self._exceptions: - filename, f, (t, val, tb) = e - self.write_center("", "_") - if f is None: - s = "%s" % filename - else: - s = "%s:%s" % (filename, f.__name__) - self.write_center(s, "_") - self.write_exception(t, val, tb) - self.write("\n") - - if self._tb_style != "no" and len(self._failed) > 0: - #self.write_center("Failed", "_") - for e in self._failed: - filename, f, (t, val, tb) = e - self.write_center("", "_") - self.write_center("%s:%s" % (filename, f.__name__), "_") - self.write_exception(t, val, tb) - self.write("\n") - - if self._tb_style != "no" and len(self._failed_doctest) > 0: - #self.write_center("Failed", "_") - for e in self._failed_doctest: - filename, msg = e - self.write_center("", "_") - self.write_center("%s" % filename, "_") - self.write(msg) - self.write("\n") - - self.write_center(text) - ok = len(self._failed) == 0 and len(self._exceptions) == 0 and \ - len(self._failed_doctest) == 0 - if not ok: - self.write("DO *NOT* COMMIT!\n") - return ok - - def entering_filename(self, filename, n): - rel_name = filename[len(self._root_dir) + 1:] - self._active_file = rel_name - self._active_file_error = False - self.write(rel_name) - self.write("[%d] " % n) - - def leaving_filename(self): - self.write(" ") - if self._active_file_error: - self.write("[FAIL]", "Red", align="right") - else: - self.write("[OK]", "Green", align="right") - self.write("\n") - if self._verbose: - self.write("\n") - - def entering_test(self, f): - self._active_f = f - if self._verbose: - self.write("\n" + f.__name__ + " ") - - def test_xfail(self): - self._xfailed += 1 - self.write("f", "Green") - - def test_xpass(self, v): - if sys.version_info[:2] < (2, 6): - message = getattr(v, 'message', '') - else: - message = str(v) - self._xpassed.append((self._active_file, message)) - self.write("X", "Green") - - def test_fail(self, exc_info): - self._failed.append((self._active_file, self._active_f, exc_info)) - self.write("F", "Red") - self._active_file_error = True - - def doctest_fail(self, name, error_msg): - # the first line contains "******", remove it: - error_msg = "\n".join(error_msg.split("\n")[1:]) - self._failed_doctest.append((name, error_msg)) - self.write("F", "Red") - self._active_file_error = True - - def test_pass(self, char="."): - self._passed += 1 - if self._verbose: - self.write("ok", "Green") - else: - self.write(char, "Green") - - def test_skip(self, v): - if sys.version_info[:2] < (2, 6): - message = getattr(v, 'message', '') - else: - message = str(v) - self._skipped += 1 - char = "s" - if message == "KeyboardInterrupt": - char = "K" - elif message == "Timeout": - char = "T" - elif message == "Slow": - char = "w" - self.write(char, "Blue") - if self._verbose: - self.write(" - ", "Blue") - self.write(message, "Blue") - - def test_exception(self, exc_info): - self._exceptions.append((self._active_file, self._active_f, exc_info)) - self.write("E", "Red") - self._active_file_error = True - - def import_error(self, filename, exc_info): - self._exceptions.append((filename, None, exc_info)) - rel_name = filename[len(self._root_dir) + 1:] - self.write(rel_name) - self.write("[?] Failed to import", "Red") - self.write(" ") - self.write("[FAIL]", "Red", align="right") - self.write("\n") -
    -sympy_dir = get_sympy_dir() -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/utilities/source.html b/dev-py3k/_modules/sympy/utilities/source.html deleted file mode 100644 index 84022447734..00000000000 --- a/dev-py3k/_modules/sympy/utilities/source.html +++ /dev/null @@ -1,167 +0,0 @@ - - - - - - - - - - sympy.utilities.source — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.utilities.source

    -"""
    -This module adds several functions for interactive source code inspection.
    -"""
    -
    -import inspect
    -from sympy.core.compatibility import callable
    -import collections
    -
    -
    -
    [docs]def source(object): - """ - Prints the source code of a given object. - """ - print('In file: %s' % inspect.getsourcefile(object)) - print(inspect.getsource(object)) - -
    -
    [docs]def get_class(lookup_view): - """ - Convert a string version of a class name to the object. - - For example, get_class('sympy.core.Basic') will return - class Basic located in module sympy.core - """ - if isinstance(lookup_view, str): - lookup_view = lookup_view - mod_name, func_name = get_mod_func(lookup_view) - if func_name != '': - lookup_view = getattr( - __import__(mod_name, {}, {}, ['*']), func_name) - if not isinstance(lookup_view, collections.Callable): - raise AttributeError( - "'%s.%s' is not a callable." % (mod_name, func_name)) - return lookup_view - -
    -
    [docs]def get_mod_func(callback): - """ - splits the string path to a class into a string path to the module - and the name of the class. For example: - - >>> from sympy.utilities.source import get_mod_func - >>> get_mod_func('sympy.core.basic.Basic') - ('sympy.core.basic', 'Basic') - - """ - dot = callback.rfind('.') - if dot == -1: - return callback, '' - return callback[:dot], callback[dot + 1:]
    -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_modules/sympy/utilities/timeutils.html b/dev-py3k/_modules/sympy/utilities/timeutils.html deleted file mode 100644 index aaa7e1e387f..00000000000 --- a/dev-py3k/_modules/sympy/utilities/timeutils.html +++ /dev/null @@ -1,191 +0,0 @@ - - - - - - - - - - sympy.utilities.timeutils — SymPy 0.7.2-git documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for sympy.utilities.timeutils

    -"""Simple tools for timing functions execution, when IPython is not
    -   available. """
    -
    -import timeit
    -import math
    -
    -_scales = [1e0, 1e3, 1e6, 1e9]
    -_units = ['s', 'ms', '\u03bcs', 'ns']
    -
    -
    -
    [docs]def timed(func, setup=None): - """Adaptively measure execution time of a function. """ - timer = timeit.Timer(func, setup=setup) - - repeat, number = 3, 1 - - for i in range(1, 10): - if timer.timeit(number) >= 0.2: - break - else: - number *= 10 - - time = min(timer.repeat(repeat, number)) / number - - if time > 0.0: - order = min(-int(math.floor(math.log10(time)) // 3), 3) - else: - order = 3 - - return (number, time, time*_scales[order], _units[order]) - - -# Code for doing inline timings of recursive algorithms. -
    -def __do_timings(): - import os - res = os.getenv('SYMPY_TIMINGS', '') - res = [x.strip() for x in res.split(',')] - return set(res) - -_do_timings = __do_timings() -_timestack = None - - -def _print_timestack(stack, level=1): - print('-'*level, '%.2f %s%s' % (stack[2], stack[0], stack[3])) - for s in stack[1]: - _print_timestack(s, level + 1) - - -def timethis(name): - def decorator(func): - global _do_timings - if not name in _do_timings: - return func - - def wrapper(*args, **kwargs): - from time import time - global _timestack - oldtimestack = _timestack - _timestack = [func.__name__, [], 0, args] - t1 = time() - r = func(*args, **kwargs) - t2 = time() - _timestack[2] = t2 - t1 - if oldtimestack is not None: - oldtimestack[1].append(_timestack) - _timestack = oldtimestack - else: - _print_timestack(_timestack) - _timestack = None - return r - return wrapper - return decorator -
    - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - - diff --git a/dev-py3k/_sources/aboutus.txt b/dev-py3k/_sources/aboutus.txt deleted file mode 100644 index 9e724e1836f..00000000000 --- a/dev-py3k/_sources/aboutus.txt +++ /dev/null @@ -1,292 +0,0 @@ -About -===== - -SymPy Development Team ----------------------- - -SymPy is a team project and it was developed by a lot of people. - -Here is a list of contributors together with what they do, (and in some cases links to their -wiki pages), where they describe in -more details what they do and what they are interested in (some people didn't -want to be mentioned here, so see our repository history for a full list). - -#. Ondřej Čertík: started the project in 2006, on Jan 4, 2011 passed the project leadership to Aaron Meurer -#. Fabian Pedregosa: everything, reviewing patches, releases, general advice (issues and mailinglist), GSoC 2009 -#. Jurjen N.E. Bos: pretty printing and other patches -#. Mateusz Paprocki: GSoC 2007, concrete math module, integration module, new core integration, a lot of patches, general advice, new polynomial module, improvements to solvers, simplifications, patch review -#. Marc-Etienne M.Leveille: matrix patch -#. Brian Jorgensen: GSoC 2007, plotting module and related things, patches -#. Jason Gedge: GSoC 2007, geometry module, a lot of patches and fixes, new core integration -#. Robert Schwarz: GSoC 2007, polynomials module, patches -#. `Pearu Peterson `_: new core, sympycore project, general advice (issues and mailinglist) -#. `Fredrik Johansson `_: mpmath project and its integration in SymPy, number theory, combinatorial functions, products & summation, statistics, units, patches, documentation, general advice (issues and mailinglist) -#. Chris Wu: GSoC 2007, linear algebra module -#. Ulrich Hecht: pattern matching and other patches -#. Goutham Lakshminarayan: number theory functions -#. David Lawrence: GHOP, Mathematica parser, square root denesting -#. Jaroslaw Tworek: GHOP, sympify AST implementation, sqrt() refactoring, maxima parser and other patches -#. David Marek: GHOP, derivative evaluation patch, int(NumberSymbol) fix -#. Bernhard R. Link: documentation patch -#. Andrej Tokarčík: GHOP, python printer -#. Or Dvory: GHOP, documentation -#. Saroj Adhikari: bug fixes -#. Pauli Virtanen: bug fix -#. Robert Kern: bug fix, common subexpression elimination -#. James Aspnes: bug fixes -#. Nimish Telang: multivariate lambdas -#. Abderrahim Kitouni: pretty printing + complex expansion bug fixes -#. Pan Peng: ode solvers patch -#. Friedrich Hagedorn: many bug fixes, refactorings and new features added -#. Elrond der Elbenfuerst: pretty printing fix -#. Rizgar Mella: BBP formula for pi calculating algorithm -#. Felix Kaiser: documentation + whitespace testing patches -#. Roberto Nobrega: several pretty printing patches -#. David Roberts: latex printing patches -#. Sebastian Krämer: implemented lambdify/numpy/mpmath cooperation, bug fixes, refactoring, lambdifying of matrices, large printing refactoring and bugfixes -#. Vinzent Steinberg: docstring patches, a lot of bug fixes, nsolve (nonlinear equation systems solver), compiling functions to machine code, patches review -#. Riccardo Gori: improvements and speedups to matrices, many bug fixes -#. Case Van Horsen: implemented optional support for gmpy in mpmath -#. Štěpán Roučka: a lot of bug fixes all over SymPy (matrix, simplification, limits, series, ...) -#. Ali Raza Syed: pretty printing/isympy on windows fix -#. Stefano Maggiolo: many bug fixes, polishings and improvements -#. Robert Cimrman: matrix patches -#. Bastian Weber: latex printing patches -#. Sebastian Krause: match patches -#. Sebastian Kreft: latex printing patches, Dirac delta function, other fixes -#. Dan (coolg49964): documentation fixes -#. Alan Bromborsky: geometric algebra modules -#. Boris Timokhin: matrix fixes -#. Robert (average.programmer): initial piecewise function patch -#. Andy R. Terrel: piecewise function polishing and other patches -#. Hubert Tsang: LaTeX printing fixes -#. Konrad Meyer: policy patch -#. Henrik Johansson: matrix zero finder patch -#. Priit Laes: line integrals, cleanups in ODE, tests added -#. Freddie Witherden: trigonometric simplifications fixes, LaTeX printer improvements -#. Brian E. Granger: second quantization physics module -#. Andrew Straw: lambdify() improvements -#. Kaifeng Zhu: factorint() fix -#. Ted Horst: Basic.__call__() fix, pretty printing fix -#. Andrew Docherty: Fix to series -#. Akshay Srinivasan: printing fix, improvements to integration -#. Aaron Meurer: ODE solvers (GSoC 2009), The Risch Algorithm for integration (GSoC 2010), other fixes, project leader as of Jan 4, 2011 -#. Barry Wardell: implement series as function, several tests added -#. Tomasz Buchert: ccode printing fixes, code quality concerning exceptions, documentation -#. Vinay Kumar: polygamma tests -#. Johann Cohen-Tanugi: commutative diff tests -#. Jochen Voss: a test for the @cachit decorator and several other fixes -#. Luke Peterson: improve solve() to handle Derivatives -#. Chris Smith: improvements to solvers, many bug fixes, documentation and test improvements -#. Thomas Sidoti: MathML printer improvements -#. Florian Mickler: reimplementation of convex_hull, several geometry module fixes -#. Nicolas Pourcelot: Infinity comparison fixes, latex fixes -#. Ben Goodrich: Matrix.jacobian() function improvements and fixes -#. Toon Verstraelen: code generation module, latex printing improvements -#. Ronan Lamy: test coverage script; limit, expansion and other fixes and improvements; cleanup -#. James Abbatiello: fixes tests on windows -#. Ryan Krauss: fixes could_extract_minus_sign(), latex fixes and improvements -#. Bill Flynn: substitution fixes -#. Kevin Goodsell: Fix to Integer/Rational -#. Jorn Baayen: improvements to piecewise functions and latex printing, bug fixes -#. Eh Tan: improve trigonometric simplification -#. Renato Coutinho: derivative improvements -#. Oscar Benjamin: latex printer fix, gcd bug fix -#. Øyvind Jensen: implemented coupled cluster expansion and wick theorem, improvements to assumptions, bugfixes -#. Julio Idichekop Filho: indentation fixes, docstring improvements -#. Łukasz Pankowski: fix matrix multiplication with numpy scalars -#. Chu-Ching Huang: fix 3d plotting example -#. Fernando Perez: symarray() implemented -#. Raffaele De Feo: fix non-commutative expansion -#. Christian Muise: fixes to logic module -#. Matt Curry: GSoC 2010 project (symbolic quantum mechanics) -#. Kazuo Thow: cleanup pretty printing tests -#. Christian Schubert: Fix to sympify() -#. Jezreel Ng: fix hyperbolic functions rewrite -#. James Pearson: Py3k related fixes -#. Matthew Brett: fixes to lambdify -#. Addison Cugini: GSoC 2010 project (quantum computing) -#. Nicholas J.S. Kinar: improved documentation about "Immutability of Expressions" -#. Harold Erbin: Geometry related work -#. Thomas Dixon: fix a limit bug -#. Cristóvão Sousa: implements _sage_ method for sign function. -#. Andre de Fortier Smit: doctests in matrices improved -#. Mark Dewing: Fixes to Integral/Sum, MathML work -#. Alexey U. Gudchenko: various work in matrices -#. Gary Kerr: fix examples, docs -#. Sherjil Ozair: fixes to SparseMatrix -#. Oleksandr Gituliar: fixes to Matrix -#. Sean Vig: Quantum improvement -#. Prafullkumar P. Tale: fixed plotting documentation -#. Vladimir Perić: fix some Python 3 issues -#. Tom Bachmann: fixes to Real -#. Yuri Karadzhov: improvements to hyperbolic identities -#. Vladimir Lagunov: improvements to the geometry module -#. Matthew Rocklin: stats, sets, matrix expressions -#. Saptarshi Mandal: Test for an integral -#. Gilbert Gede: Tests for solvers -#. Anatolii Koval: Infinite 1D box example -#. Tomo Lazovich: fixes to quantum operators -#. Pavel Fedotov: fix to is_number -#. Kibeom Kim: fixes to cse function and preorder_traversal -#. Gregory Ksionda: fixes to Integral instantiation -#. Tomáš Bambas: prettier printing of examples -#. Jeremias Yehdegho: fixes to the nthoery module -#. Jack McCaffery: fixes to asin and acos -#. Raymond Wong: Quantum work -#. Luca Weihs: improvements to the geometry module -#. Shai 'Deshe' Wyborski: fix to numeric evaluation of hypergeometric sums -#. Thomas Wiecki: Fix Sum.diff -#. Óscar Nájera: better Laguerre polynomial generator -#. Mario Pernici: faster algorithm for computing Groebner bases -#. Benjamin McDonald: Fix bug in geometry -#. Sam Magura: Improvements to Plot.saveimage -#. Stefan Krastanov: Make Pyglet an external dependency -#. Bradley Froehle: Fix shell command to generate modules in setup.py -#. Min Ragan-Kelley: Fix isympy to work with IPython 0.11 -#. Nikhil Sarda: Fix to combinatorics/prufer -#. Emma Hogan: Fixes to the documentation -#. Jason Moore: Fixes to the mechanics module -#. Julien Rioux: Fix behavior of some deprecated methods -#. Roberto Colistete, Jr.: Add num_columns option to the pretty printer -#. Raoul Bourquin: Implement Euler numbers -#. Gert-Ludwig Ingold: Correct derivative of coth -#. Srinivas Vasudevan: Implementation of Catalan numbers -#. Miha Marolt: Add numpydoc extension to the Sphinx docs, fix ode_order -#. Tim Lahey: Rotation matrices -#. Luis Garcia: update examples in symarry -#. Matt Rajca: Code quality fixes -#. David Li: Documentation fixes -#. David Ju: Increase test coverage, fixes to KroneckerDelta -#. Alexandr Gudulin: Code quality fixes -#. Bilal Akhtar: isympy man page -#. Grzegorz Świrski: Fix to latex(), MacPorts portfile -#. Matt Habel: SymPy-wide pyflakes editing -#. Nikolay Lazarov: Translation of the tutorial to Bulgarian -#. Nichita Utiu: Add pretty printing to Product() -#. Tristan Hume: Fixes to test_lambdify.py -#. Imran Ahmed Manzoor: Fixes to the test runner -#. Steve Anton: pyflakes cleanup of various modules, documentation fixes for logic -#. Sam Sleight: Fixes to geometry documentation -#. tsmars15: Fixes to code quality -#. Chancellor Arkantos: Fixes to the logo -#. Stepan Simsa: Translation of the tutorial to Czech -#. Tobias Lenz: Unicode pretty printer for Sum -#. Siddhanathan Shanmugam: Documentation fixes for the Physics module -#. Tiffany Zhu: Improved the latex() docstring -#. Alexey Subach: Translation of the tutorial to Russian -#. Joan Creus: Improvements to the test runner -#. Geoffry Song: Improve code coverage -#. Puneeth Chaganti: Fix for the tutorial -#. Marcin Kostrzewa: SymPy Cheatsheet -#. Jim Zhang: Fixes to AUTHORS and .mailmap -#. Natalia Nawara: Fixes to Product() -#. vishal: Update the Czech tutorial translation to use a .po file -#. Shruti Mangipudi: added See Also documentation to Matrices -#. Davy Mao: Documentation -#. Swapnil Agarwal: added See Also documentation to ntheory, functions -#. Kendhia: Add XFAIL tests -#. jerryma1121: See Also documentation for geometry -#. Joachim Durchholz: corrected spacing issues identified by PyDev -#. Martin Povišer: fix problem with rationaltools -#. Siddhant Jain: See Also documentation for functions/special -#. Kevin Hunter: Improvements to the inequality classes -#. Michael Mayorov: Improvement to series -#. Nathan Alison: Additions to the stats module -#. Christian Bühler: remove use of set_main() in GA -#. Carsten Knoll: improvement to preview() -#. M R Bharath: modified use of int_tested, improvement to Permutation -#. Matthias Toews: File permissions -#. Sergiu Ivanov: Fixes to zoo, SymPyDeprecationWarning -#. Jorge E. Cardona: Cleanup in polys -#. Sanket Agarwal: Rewrite coverage_doctest.py script -#. Manoj Babu K.: Improve gamma function -#. Sai Nikhil: Fix to Heavyside with complex arguments -#. Aleksandar Makelov: Fixes regarding the dihedral group generator -#. Raphael Michel: German translation of the tutorial -#. Sachin Irukula: Changes to allow Dict sorting -#. Ashwini Oruganti: Changes to Pow printing -#. Andreas Kloeckner: Fix to cse() -#. Prateek Papriwal: improve summation documentation -#. Arpit Goyal: Improvements to Integral and Sum -#. Angadh Nanjangud: in physics.mechanics, added a function and tests for the parallel axis theorem -#. Comer Duncan: added dual, is_antisymmetric, and det_lu_decomposition to matrices.py -#. Jens H. Nielsen: added sets to modules listing, update IPython printing extension -#. Joseph Dougherty: modified whitespace cleaning to remove multiple newlines at eof -#. marshall2389: Spelling correction -#. Guru Devanla: Implemented quantum density operator -#. George Waksman: Implemented JavaScript code printer and MathML printer -#. Angus Griffith: Fix bug in rsolve -#. Timothy Reluga: Rewrite trigonometric functions as rationals -#. Brian Stephanik: Test for a bug in fcode -#. Ljubiša Moćić: Serbian translation of the tutorial -#. Piotr Korgul: Polish translation of the tutorial -#. Rom le Clair: French translation of the tutorial -#. Alexandr Popov: Fixes to Pauli algebra -#. Saurabh Jha: Work on Kauers algorithm -#. Tarun Gaba: Implemented some trigonometric integrals -#. Takafumi Arakaki: Add info target to the doc Makefile -#. Alexander Eberspächer: correct typo in aboutus.rst -#. Sachin Joglekar: Simplification of logic expressions to SOP and POS forms -#. Tyler Pirtle: Fix improperly formatted error message -#. Vasily Povalyaev: Fix latex(Min) -#. Colleen Lee: replace uses of fnan with S.NaN -#. Niklas Thörne: Fix links in the docs -#. Huijun Mai: Chinese translation of the tutorial -#. Marek Šuppa: Improvements to symbols, tests -#. Prasoon Shukla: Bug fixes - -Up-to-date list in the order of the first contribution is given in the `AUTHORS -`_ file. - -You can find a brief history of SymPy in the README. - -Financial and Infrastructure Support ------------------------------------- - -* `Google `_: SymPy has received generous - financial support from Google in various years through the `Google Summer of - Code `_ program by providing stipends: - - * in 2007 for 5 students (`GSoC 2007 `_) - * in 2008 for 1 student (`GSoC 2008 `_) - * in 2009 for 5 students (`GSoC 2009 `_) - * in 2010 for 5 students (`GSoC 2010 `_) - * in 2011 for 9 students (`GSoC 2011 `_) - * in 2012 for 6 students (`GSoC 2012 `_) - -* `Python Software Foundation (PSF) `_ has hosted - various GSoC students over the years: - - * 3 GSoC 2007 students (Brian, Robert and Jason) - * 1 GSoC 2008 student (Fredrik) - * 2 GSoC 2009 students (Freddie and Priit) - * 4 GSoC 2010 students (Aaron, Christian, Matthew and Øyvind) - -* `Portland State University (PSU) `_ has hosted following - GSoC students: - - * 1 student (Chris) in 2007 - * 3 students (Aaron, Dale and Fabian) in 2009 - * 1 student (Addison) in 2010 - -* `The Space Telescope Science Institute `_: STScI hosted 1 GSoC 2007 student (Mateusz) - -* Several 13-17 year old pre-university students contributed as part of - Google's `Code-In - `_ 2011. (`GCI - 2011 `_) - -* `Simula Research Laboratory `_: supports Pearu Peterson work in SymPy/SymPy Core projects - -* `GitHub `_ is providing us with development - and collaboration tools - -License -------- - -Unless stated otherwise, all files in the SymPy project, SymPy's webpage (and -wiki), all images and all documentation including this User's Guide are licensed -using the new BSD license: - -.. literalinclude:: ../../LICENSE diff --git a/dev-py3k/_sources/gotchas.txt b/dev-py3k/_sources/gotchas.txt deleted file mode 100644 index ddd647865ea..00000000000 --- a/dev-py3k/_sources/gotchas.txt +++ /dev/null @@ -1,783 +0,0 @@ -.. _gotchas: - -==================== -Gotchas and Pitfalls -==================== - -.. role:: input(strong) - -Introduction -============ - -SymPy runs under the `Python Programming Language -`_, so there are some things that may behave -differently than they do in other, independent computer algebra systems -like Maple or Mathematica. These are some of the gotchas and pitfalls -that you may encounter when using SymPy. See also the `FAQ -`_, the :ref:`Tutorial`, the -remainder of the SymPy Docs, and the `official Python Tutorial -`_. - -If you are already familiar with C or Java, you might also want to look -this `4 minute Python tutorial -`_. - -Ignore ``#doctest: +SKIP`` in the examples. That has to do with -internal testing of the examples. - -.. _equals-signs: - -Equals Signs (=) -================ - -Single Equals Sign ------------------- - -The equals sign (``=``) is the assignment operator, not an equality. If -you want to do :math:`x = y`, use ``Eq(x, y)`` for equality. -Alternatively, all expressions are assumed to equal zero, so you can -just subtract one side and use ``x - y``. - -The proper use of the equals sign is to assign expressions to variables. - -For example: - - >>> from sympy.abc import x, y - >>> a = x - y - >>> print(a) - x - y - -Double Equals Signs -------------------- - -Double equals signs (``==``) are used to test equality. However, this -tests expressions exactly, not symbolically. For example: - - >>> (x + 1)**2 == x**2 + 2*x + 1 - False - >>> (x + 1)**2 == (x + 1)**2 - True - -If you want to test for symbolic equality, one way is to subtract one -expression from the other and run it through functions like -:func:`expand`, :func:`simplify`, and :func:`trigsimp` and see if the -equation reduces to 0. - - >>> from sympy import simplify, cos, sin, expand - >>> simplify((x + 1)**2 - (x**2 + 2*x + 1)) - 0 - >>> simplify(sin(2*x) - 2*sin(x)*cos(x)) - -2*sin(x)*cos(x) + sin(2*x) - >>> expand(sin(2*x) - 2*sin(x)*cos(x), trig=True) - 0 - -.. note:: - - See also `Why does SymPy say that two equal expressions are unequal? - `_ in the FAQ. - - -Variables -========= - -Variables Assignment does not Create a Relation Between Expressions -------------------------------------------------------------------- - -When you use ``=`` to do assignment, remember that in Python, as in most -programming languages, the variable does not change if you change the -value you assigned to it. The equations you are typing use the values -present at the time of creation to "fill in" values, just like regular -Python definitions. They are not altered by changes made afterwards. -Consider the following: - - >>> from sympy import Symbol - >>> a = Symbol('a') # Symbol, `a`, stored as variable "a" - >>> b = a + 1 # an expression involving `a` stored as variable "b" - >>> print(b) - a + 1 - >>> a = 4 # "a" now points to literal integer 4, not Symbol('a') - >>> print(a) - 4 - >>> b # "b" is still pointing at the expression involving `a` - a + 1 - -Changing quantity :obj:`a` does not change :obj:`b`; you are not working -with a set of simultaneous equations. It might be helpful to remember -that the string that gets printed when you print a variable referring to -a SymPy object is the string that was given to it when it was created; -that string does not have to be the same as the variable that you assign -it to. - - >>> from sympy import var - >>> r, t, d = var('rate time short_life') - >>> d = r*t - >>> print(d) - rate*time - >>> r = 80 - >>> t = 2 - >>> print(d) # We haven't changed d, only r and t - rate*time - >>> d = r*t - >>> print(d) # Now d is using the current values of r and t - 160 - - -If you need variables that have dependence on each other, you can define -functions. Use the ``def`` operator. Indent the body of the function. -See the Python docs for more information on defining functions. - - >>> c, d = var('c d') - >>> print(c) - c - >>> print(d) - d - >>> def ctimesd(): - ... """ - ... This function returns whatever c is times whatever d is. - ... """ - ... return c*d - ... - >>> ctimesd() - c*d - >>> c = 2 - >>> print(c) - 2 - >>> ctimesd() - 2*d - - -If you define a circular relationship, you will get a -:exc:`RuntimeError`. - - >>> def a(): - ... return b() - ... - >>> def b(): - ... return a() - ... - >>> a() - Traceback (most recent call last): - File "...", line ..., in ... - compileflags, 1) in test.globs - File "<...>", line 1, in - a() - File "<...>", line 2, in a - return b() - File "<...>", line 2, in b - return a() - File "<...>", line 2, in a - return b() - ... - RuntimeError: maximum recursion depth exceeded - - -.. note:: - See also `Why doesn't changing one variable change another that depends on it? - `_ in the FAQ. - -.. _symbols: - -Symbols -------- - -Symbols are variables, and like all other variables, they need to be -assigned before you can use them. For example: - - >>> import sympy - >>> z**2 # z is not defined yet #doctest: +SKIP - Traceback (most recent call last): - File "", line 1, in - NameError: name 'z' is not defined - >>> sympy.var('z') # This is the easiest way to define z as a standard symbol - z - >>> z**2 - z**2 - - -If you use :command:`isympy`, it runs the following commands for you, -giving you some default Symbols and Functions. - - >>> - >>> from sympy import * - >>> x, y, z, t = symbols('x y z t') - >>> k, m, n = symbols('k m n', integer=True) - >>> f, g, h = symbols('f g h', cls=Function) - -You can also import common symbol names from :mod:`sympy.abc`. - - >>> from sympy.abc import w - >>> w - w - >>> import sympy - >>> dir(sympy.abc) #doctest: +SKIP - ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', - 'P', 'Q', 'R', 'S', 'Symbol', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - '__builtins__', '__doc__', '__file__', '__name__', '__package__', '_greek', - '_latin', 'a', 'alpha', 'b', 'beta', 'c', 'chi', 'd', 'delta', 'e', - 'epsilon', 'eta', 'f', 'g', 'gamma', 'h', 'i', 'iota', 'j', 'k', 'kappa', - 'l', 'm', 'mu', 'n', 'nu', 'o', 'omega', 'omicron', 'p', 'phi', 'pi', - 'psi', 'q', 'r', 'rho', 's', 'sigma', 't', 'tau', 'theta', 'u', 'upsilon', - 'v', 'w', 'x', 'xi', 'y', 'z', 'zeta'] - -If you want control over the assumptions of the variables, use -:func:`Symbol` and :func:`symbols`. See :ref:`Keyword -Arguments` below. - -Lastly, it is recommended that you not use :obj:`I`, :obj:`E`, :obj:`S`, -:obj:`N`, :obj:`C`, :obj:`O`, or :obj:`Q` for variable or symbol names, as those -are used for the imaginary unit (:math:`i`), the base of the natural -logarithm (:math:`e`), the :func:`sympify` function (see :ref:`Symbolic -Expressions` below), numeric evaluation (:func:`N` -is equivalent to :ref:`evalf()` ), the class registry (for -things like :func:`C.cos`, to prevent cyclic imports in some code), -the `big O `_ order symbol -(as in :math:`O(n\log{n})`), and the assumptions object that holds a list of -supported ask keys (such as :obj:`Q.real`), respectively. You can use the -mnemonic ``QCOSINE`` to remember what Symbols are defined by default in SymPy. -Or better yet, always use lowercase letters for Symbol names. Python will -not prevent you from overriding default SymPy names or functions, so be -careful. - - >>> cos(pi) # cos and pi are a built-in sympy names. - -1 - >>> pi = 3 # Notice that there is no warning for overriding pi. - >>> cos(pi) - cos(3) - >>> def cos(x): # No warning for overriding built-in functions either. - ... return 5*x - ... - >>> cos(pi) - 15 - >>> from sympy import cos # reimport to restore normal behavior - - -To get a full list of all default names in SymPy do: - - >>> import sympy - >>> dir(sympy) #doctest: +SKIP - # A big list of all default sympy names and functions follows. - # Ignore everything that starts and ends with __. - -If you have `iPython `_ installed and -use :command:`isympy`, you can also press the TAB key to get a list of -all built-in names and to autocomplete. Also, see `this page -`_ for a -trick for getting tab completion in the regular Python console. - -.. note:: - See also `What is the best way to create symbols? - `_ in the FAQ. - -.. _symbolic-expressions: - -Symbolic Expressions -==================== - -.. _python-vs-sympy-numbers: - -Python numbers vs. SymPy Numbers --------------------------------- - -SymPy uses its own classes for integers, rational numbers, and floating -point numbers instead of the default Python :obj:`int` and :obj:`float` -types because it allows for more control. But you have to be careful. -If you type an expression that just has numbers in it, it will default -to a Python expression. Use the :func:`sympify` function, or just -:func:`S`, to ensure that something is a SymPy expression. - - >>> 6.2 # Python float. Notice the floating point accuracy problems. #doctest: +SKIP - 6.2000000000000002 - >>> type(6.2) - <... 'float'> - >>> S(6.2) # SymPy Float has no such problems because of arbitrary precision. - 6.20000000000000 - >>> type(S(6.2)) - - -If you include numbers in a SymPy expression, they will be sympified -automatically, but there is one gotcha you should be aware of. If you -do ``/`` inside of a SymPy expression, Python will -evaluate the two numbers before SymPy has a chance to get -to them. The solution is to :func:`sympify` one of the numbers, or use -:mod:`Rational`. - - >>> x**(1/2) # evaluates to x**0 or x**0.5 #doctest: +SKIP - x**0.5 - >>> x**(S(1)/2) # sympyify one of the ints - sqrt(x) - >>> x**Rational(1, 2) # use the Rational class - sqrt(x) - -With a power of ``1/2`` you can also use ``sqrt`` shorthand: - - >>> sqrt(x) == x**Rational(1, 2) - True - -If the two integers are not directly separated by a division sign then -you don't have to worry about this problem: - - >>> x**(2*x/3) - x**(2*x/3) - -.. note:: - - A common mistake is copying an expression that is printed and - reusing it. If the expression has a :mod:`Rational` (i.e., - ``/``) in it, you will not get the same result, - obtaining the Python result for the division rather than a SymPy - Rational. - - >>> x = Symbol('x') - >>> print(solve(7*x -22, x)) - [22/7] - >>> 22/7 # If we just copy and paste we get int 3 or a float #doctest: +SKIP - 3.142857142857143 - >>> # One solution is to just assign the expression to a variable - >>> # if we need to use it again. - >>> a = solve(7*x - 22, x) - >>> a - [22/7] - - The other solution is to put quotes around the expression - and run it through S() (i.e., sympify it): - - >>> S("22/7") - 22/7 - -Also, if you do not use :command:`isympy`, you could use ``from -__future__ import division`` to prevent the ``/`` sign from performing -`integer division `_. - - >>> - >>> 1/2 # With division imported it evaluates to a python float #doctest: +SKIP - 0.5 - >>> 1//2 # You can still achieve integer division with // - 0 - - But be careful: you will now receive floats where you might have desired - a Rational: - - >>> x**(1/2) #doctest: +SKIP - x**0.5 - -:mod:`Rational` only works for number/number and is only meant for -rational numbers. If you want a fraction with symbols or expressions in -it, just use ``/``. If you do number/expression or expression/number, -then the number will automatically be converted into a SymPy Number. -You only need to be careful with number/number. - - >>> Rational(2, x) - Traceback (most recent call last): - ... - TypeError: invalid input: x - >>> 2/x - 2/x - -Evaluating Expressions with Floats and Rationals ------------------------------------------------- - -SymPy keeps track of the precision of Floats. The default precision is -15 digits. When expressions involving Floats are evaluated, the result -will be expressed to 15 digits of precision but those digits (depending -on the numbers involved with the calculation) may not all be significant. - -The first issue to keep in mind is how the Float is created: it is created -with a value and a precision. The precision indicates how precise of a value -to use when that Float (or an expression it appears in) is evaluated. - -The values can be given as strings, integers, floats, or Rationals. - - - strings and integers are interpreted as exact - - >>> Float(100) - 100.000000000000 - >>> Float('100', 5) - 100.00 - - - to have the precision match the number of digits, the null string - can be used for the precision - - >>> Float(100, '') - 100. - >>> Float('12.34') - 12.3400000000000 - >>> Float('12.34', '') - 12.34 - - >>> s, r = [Float(j, 3) for j in ('0.25', Rational(1, 7))] - >>> for f in [s, r]: - ... print(f) - 0.250 - 0.143 - -Next, notice that each of those values looks correct to 3 digits. But if we try -to evaluate them to 20 digits, a difference will become apparent: - - The 0.25 (with precision of 3) represents a number that has a non-repeating - binary decimal; 1/7 is repeating in binary and decimal -- it cannot be - represented accurately too far past those first 3 digits (the correct - decimal is a repeating 142857): - - >>> s.n(20) - 0.25000000000000000000 - >>> r.n(20) - 0.14285278320312500000 - - It is important to realize that although a Float is being displayed in - decimal at aritrary precision, it is actually stored in binary. Once the - Float is created, its binary information is set at the given precision. - The accuracy of that value cannot be subsequently changed; so 1/7, at a - precision of 3 digits, can be padded with binary zeros, but these will - not make it a more accurate value of 1/7. - -If inexact, low-precision numbers are involved in a calculation with -with higher precision values, the evalf engine will increase the precision -of the low precision values and inexact results will be obtained. This is -feature of calculations with limited precision: - - >>> Float('0.1', 10) + Float('0.1', 3) - 0.2000061035 - -Although the evalf engine tried to maintain 10 digits of precision (since -that was the highest precision represented) the 3-digit precision used -limits the accuracy to about 4 digits -- not all the digits you see -are significant. evalf doesn't try to keep track of the number of -significant digits. - -That very simple expression involving the addition of two numbers with -different precisions will hopefully be instructive in helping you -understand why more complicated expressions (like trig expressions that -may not be simplified) will not evaluate to an exact zero even though, -with the right simplification, they should be zero. Consider this -unsimplified trig identity, multiplied by a big number: - - >>> big = 12345678901234567890 - >>> big_trig_identity = big*cos(x)**2 + big*sin(x)**2 - big*1 - >>> abs(big_trig_identity.subs(x, .1).n(2)) > 1000 - True - -When the cos and sin terms were evaluated to 15 digits of precision and -multiplied by the big number, they gave a large number that was only -precise to 15 digits (approximately) and when the 20 digit big number -was subtracted the result was not zero. - -There are three things that will help you obtain more precise numerical -values for expressions: - - 1) Pass the desired substitutions with the call to evaluate. By doing - the subs first, the Float values can not be updated as necessary. By - passing the desired substitutions with the call to evalf the ability - to re-evaluate as necessary is gained and the results are impressively - better: - - >>> big_trig_identity.n(2, {x: 0.1}) - -0.e-91 - - 2) Use Rationals, not Floats. During the evaluation process, the - Rational can be computed to an arbitrary precision while the Float, - once created -- at a default of 15 digits -- cannot. Compare the - value of -1.4e+3 above with the nearly zero value obtained when - replacing x with a Rational representing 1/10 -- before the call - to evaluate: - - >>> big_trig_identity.subs(x, S('1/10')).n(2) - 0.e-91 - - 3) Try to simplify the expression. In this case, SymPy will recognize - the trig identity and simplify it to zero so you don't even have to - evaluate it numerically: - - >>> big_trig_identity.simplify() - 0 - - -.. _Immutability-of-Expressions: - -Immutability of Expressions ---------------------------- - -Expressions in SymPy are immutable, and cannot be modified by an in-place -operation. This means that a function will always return an object, and the -original expression will not be modified. The following example snippet -demonstrates how this works:: - - def main(): - var('x y a b') - expr = 3*x + 4*y - print 'original =', expr - expr_modified = expr.subs({x: a, y: b}) - print 'modified =', expr_modified - - if __name__ == "__main__": - main() - -The output shows that the :func:`subs` function has replaced variable -:obj:`x` with variable :obj:`a`, and variable :obj:`y` with variable :obj:`b`:: - - original = 3*x + 4*y - modified = 3*a + 4*b - -The :func:`subs` function does not modify the original expression :obj:`expr`. -Rather, a modified copy of the expression is returned. This returned object -is stored in the variable :obj:`expr_modified`. Note that unlike C/C++ and -other high-level languages, Python does not require you to declare a variable -before it is used. - - -Mathematical Operators ----------------------- - -SymPy uses the same default operators as Python. Most of these, like -``*/+-``, are standard. Aside from integer division discussed in -:ref:`Python numbers vs. SymPy Numbers ` above, -you should also be aware that implied multiplication is not allowed. You -need to use ``*`` whenever you wish to multiply something. Also, to -raise something to a power, use ``**``, not ``^`` as many computer -algebra systems use. Parentheses ``()`` change operator precedence as -you would normally expect. - -In :command:`isympy`, with the :command:`ipython` shell:: - - >>> 2x - Traceback (most recent call last): - ... - SyntaxError: invalid syntax - >>> 2*x - 2*x - >>> (x + 1)^2 # This is not power. Use ** instead. - Traceback (most recent call last): - ... - TypeError: unsupported operand type(s) for ^: 'Add' and 'int' - >>> (x + 1)**2 - (x + 1)**2 - >>> pprint(3 - x**(2*x)/(x + 1)) - 2*x - x - - ----- + 3 - x + 1 - - -Inverse Trig Functions ----------------------- - -SymPy uses different names for some functions than most computer algebra -systems. In particular, the inverse trig functions use the python names -of :func:`asin`, :func:`acos` and so on instead of the usual ``arcsin`` -and ``arccos``. Use the methods described in :ref:`Symbols ` -above to see the names of all SymPy functions. - -Special Symbols -=============== - -The symbols ``[]``, ``{}``, ``=``, and ``()`` have special meanings in -Python, and thus in SymPy. See the Python docs linked to above for -additional information. - -.. _lists: - -Lists ------ - -Square brackets ``[]`` denote a list. A list is a container that holds -any number of different objects. A list can contain anything, including -items of different types. Lists are mutable, which means that you can -change the elements of a list after it has been created. You access the -items of a list also using square brackets, placing them after the list -or list variable. Items are numbered using the space before the item. - -.. note:: - - List indexes begin at 0. - -Example: - - >>> a = [x, 1] # A simple list of two items - >>> a - [x, 1] - >>> a[0] # This is the first item - x - >>> a[0] = 2 # You can change values of lists after they have been created - >>> print(a) - [2, 1] - >>> print(solve(x**2 + 2*x - 1, x)) # Some functions return lists - [-1 + sqrt(2), -sqrt(2) - 1] - - -.. note:: - See the Python docs for more information on lists and the square - bracket notation for accessing elements of a list. - -Dictionaries ------------- - -Curly brackets ``{}`` denote a dictionary, or a dict for short. A -dictionary is an unordered list of non-duplicate keys and values. The -syntax is ``{key: value}``. You can access values of keys using square -bracket notation. - - >>> d = {'a': 1, 'b': 2} # A dictionary. - >>> d - {'a': 1, 'b': 2} - >>> d['a'] # How to access items in a dict - 1 - >>> roots((x - 1)**2*(x - 2), x) # Some functions return dicts - {1: 2, 2: 1} - >>> # Some SymPy functions return dictionaries. For example, - >>> # roots returns a dictionary of root:multiplicity items. - >>> roots((x - 5)**2*(x + 3), x) - {-3: 1, 5: 2} - >>> # This means that the root -3 occurs once and the root 5 occurs twice. - -.. note:: - - See the Python docs for more information on dictionaries. - -Tuples ------- - -Parentheses ``()``, aside from changing operator precedence and their -use in function calls, (like ``cos(x)``), are also used for tuples. A -``tuple`` is identical to a :ref:`list `, except that it is not -mutable. That means that you can not change their values after they -have been created. In general, you will not need tuples in SymPy, but -sometimes it can be more convenient to type parentheses instead of -square brackets. - - >>> t = (1, 2, x) # Tuples are like lists - >>> t - (1, 2, x) - >>> t[0] - 1 - >>> t[0] = 4 # Except you can not change them after they have been created - Traceback (most recent call last): - File "", line 1, in - TypeError: 'tuple' object does not support item assignment - - Single element tuples, unlike lists, must have a comma in them: - - >>> (x,) - (x,) - - Without the comma, a single expression without a comma is not a tuple: - - >>> (x) - x - - integrate takes a sequence as the second argument if you want to integrate - with limits (and a tuple or list will work): - - >>> integrate(x**2, (x, 0, 1)) - 1/3 - >>> integrate(x**2, [x, 0, 1]) - 1/3 - - -.. note:: - - See the Python docs for more information on tuples. - -.. _keyword-arguments: - -Keyword Arguments ------------------ - -Aside from the usage described :ref:`above `, equals signs -(``=``) are also used to give named arguments to functions. Any -function that has ``key=value`` in its parameters list (see below on how -to find this out), then ``key`` is set to ``value`` by default. You can -change the value of the key by supplying your own value using the equals -sign in the function call. Also, functions that have ``**`` followed by -a name in the parameters list (usually ``**kwargs`` or -``**assumptions``) allow you to add any number of ``key=value`` pairs -that you want, and they will all be evaluated according to the function. - - sqrt(x**2) doesn't auto simplify to x because x is assumed to be - complex by default, and, for example, sqrt((-1)**2) == sqrt(1) == 1 != -1: - - >>> sqrt(x**2) - sqrt(x**2) - - Giving assumptions to Symbols is an example of using the keyword argument: - - >>> x = Symbol('x', positive=True) - - The square root will now simplify since it knows that x >= 0: - - >>> sqrt(x**2) - x - - powsimp has a default argument of combine='all': - - >>> pprint(powsimp(x**n*x**m*y**n*y**m)) - m + n - (x*y) - - Setting combine to the default value is the same as not setting it. - - >>> pprint(powsimp(x**n*x**m*y**n*y**m, combine='all')) - m + n - (x*y) - - The non-default options are 'exp', which combines exponents... - - >>> pprint(powsimp(x**n*x**m*y**n*y**m, combine='exp')) - m + n m + n - x *y - - ...and 'base', which combines bases. - - >>> pprint(powsimp(x**n*x**m*y**n*y**m, combine='base')) - m n - (x*y) *(x*y) - -.. note:: - - See the Python docs for more information on function parameters. - -Getting help from within SymPy -============================== - -help() ------- - -Although all docs are available at `docs.sympy.org `_ or on the -`SymPy Wiki `_, you can also get info on functions from within the -Python interpreter that runs SymPy. The easiest way to do this is to do -``help(function)``, or ``function?`` if you are using :command:`ipython`:: - - In [1]: help(powsimp) # help() works everywhere - - In [2]: # But in ipython, you can also use ?, which is better because it - In [3]: # it gives you more information - In [4]: powsimp? - -These will give you the function parameters and docstring for -:func:`powsimp`. The output will look something like this: - -.. module:: sympy.simplify.simplify -.. autofunction:noindex: powsimp - -source() --------- - -Another useful option is the :func:`source` function. This will print -the source code of a function, including any docstring that it may have. -You can also do ``function??`` in :command:`ipython`. For example, -from SymPy 0.6.5: - - >>> source(simplify) # simplify() is actually only 2 lines of code. #doctest: +SKIP - In file: ./sympy/simplify/simplify.py - def simplify(expr): - """Naively simplifies the given expression. - ... - Simplification is not a well defined term and the exact strategies - this function tries can change in the future versions of SymPy. If - your algorithm relies on "simplification" (whatever it is), try to - determine what you need exactly - is it powsimp()? radsimp()? - together()?, logcombine()?, or something else? And use this particular - function directly, because those are well defined and thus your algorithm - will be robust. - ... - """ - expr = Poly.cancel(powsimp(expr)) - return powsimp(together(expr.expand()), combine='exp', deep=True) diff --git a/dev-py3k/_sources/guide.txt b/dev-py3k/_sources/guide.txt deleted file mode 100644 index 3dc1f67d8b4..00000000000 --- a/dev-py3k/_sources/guide.txt +++ /dev/null @@ -1,538 +0,0 @@ -.. _guide: - -================== -SymPy User's Guide -================== - -.. role:: input(strong) - -Introduction -============ - -If you are new to SymPy, start with the :ref:`Tutorial `. If you went -through it, now -it's time to learn how SymPy works internally and this is what this guide is -about. Once you grasp the idea behind SymPy, you will be able to use it -effectively and also know how to extend it and fix it. -You may also be just interested in :ref:`SymPy Modules Reference `. - -Learning SymPy -============== - -Everyone has different ways of understanding the code written by others. - -Ondřej's approach ------------------ - -Let's say I'd like to understand how ``x + y + x`` works and how it is possible -that it gets simplified to ``2*x + y``. - -I write a simple script, I usually call it ``t.py`` (I don't remember anymore -why I call it that way):: - - from sympy.abc import x, y - - e = x + y + x - - print e - -And I try if it works - -.. parsed-literal:: - - $ :input:`python t.py` - y + 2*x - -Now I start `winpdb `_ on it (if you've never used winpdb --- it's an excellent multiplatform debugger, works on Linux, Windows and Mac OS -X): - -.. parsed-literal:: - - $ :input:`winpdb t.py` - y + 2*x - -and a winpdb window will popup, I move to the next line using F6: - -.. image:: pics/winpdb1.png - -Then I step into (F7) and after a little debugging I get for example: - -.. image:: pics/winpdb2.png - -.. tip:: Make the winpdb window larger on your screen, it was just made smaller - to fit in this guide. - -I see values of all local variables in the left panel, so it's very easy to see -what's happening. You can see, that the ``y + 2*x`` is emerging in the ``obj`` -variable. Observing that ``obj`` is constructed from ``c_part`` and ``nc_part`` -and seeing what ``c_part`` contains (``y`` and ``2*x``). So looking at the line -28 (the whole line is not visible on the screenshot, so here it is):: - - c_part, nc_part, lambda_args, order_symbols = cls.flatten(map(_sympify, args)) - -you can see that the simplification happens in ``cls.flatten``. Now you can set -the breakpoint on the line 28, quit winpdb (it will remember the breakpoint), -start it again, hit F5, this will stop at this breakpoint, hit F7, this will go -into the function ``Add.flatten()``:: - - @classmethod - def flatten(cls, seq): - """ - Takes the sequence "seq" of nested Adds and returns a flatten list. - - Returns: (commutative_part, noncommutative_part, lambda_args, - order_symbols) - - Applies associativity, all terms are commutable with respect to - addition. - """ - terms = {} # term -> coeff - # e.g. x**2 -> 5 for ... + 5*x**2 + ... - - coeff = S.Zero # standalone term - # e.g. 3 + ... - lambda_args = None - order_factors = [] - while seq: - o = seq.pop(0) - -and then you can study how it works. I am going to stop here, this should be -enough to get you going -- with the above technique, I am able to understand -almost any Python code. - - -SymPy's Architecture -==================== - -We try to make the sources easily understandable, so you can look into the -sources and read the doctests, it should be well documented and if you don't -understand something, ask on the mailinglist_. - -You can find all the decisions archived in the issues_, to see rationale for -doing this and that. - -Basics ------- - -All symbolic things are implemented using subclasses of the ``Basic`` class. -First, you need to create symbols using ``Symbol("x")`` or numbers using -``Integer(5)`` or ``Float(34.3)``. Then you construct the expression using any -class from SymPy. For example ``Add(Symbol("a"), Symbol("b"))`` gives an -instance of the ``Add`` class. You can call all methods, which the particular -class supports. - -For easier use, there is a syntactic sugar for expressions like: - -``cos(x) + 1`` is equal to ``cos(x).__add__(1)`` is equal to -``Add(cos(x), Integer(1))`` - -or - -``2/cos(x)`` is equal to ``cos(x).__rdiv__(2)`` is equal to -``Mul(Rational(2), Pow(cos(x), Rational(-1)))``. - -So, you can write normal expressions using python arithmetics like this:: - - a = Symbol("a") - b = Symbol("b") - e = (a + b)**2 - print e - -but from the sympy point of view, we just need the classes ``Add``, ``Mul``, -``Pow``, ``Rational``, ``Integer``. - -Automatic evaluation to canonical form --------------------------------------- - -For computation, all expressions need to be in a -canonical form, this is done during the creation of the particular instance -and only inexpensive operations are performed, necessary to put the expression -in the -canonical form. So the canonical form doesn't mean the simplest possible -expression. The exact list of operations performed depend on the -implementation. Obviously, the definition of the canonical form is arbitrary, -the only requirement is that all equivalent expressions must have the same -canonical form. We tried the conversion to a canonical (standard) form to be -as fast as possible and also in a way so that the result is what you would -write by hand - so for example ``b*a + -4 + b + a*b + 4 + (a + b)**2`` becomes -``2*a*b + b + (a + b)**2``. - -Whenever you construct an expression, for example ``Add(x, x)``, the -``Add.__new__()`` is called and it determines what to return. In this case:: - - >>> from sympy import Add - >>> from sympy.abc import x - >>> e = Add(x, x) - >>> e - 2*x - - >>> type(e) - - -``e`` is actually an instance of ``Mul(2, x)``, because ``Add.__new__()`` -returned ``Mul``. - -Comparisons ------------ - -Expressions can be compared using a regular python syntax:: - - >>> from sympy.abc import x, y - >>> x + y == y + x - True - - >>> x + y == y - x - False - -We made the following decision in SymPy: ``a = Symbol("x")`` and another -``b = Symbol("x")`` (with the same string "x") is the same thing, i.e ``a == b`` is -``True``. We chose ``a == b``, because it is more natural - ``exp(x) == exp(x)`` is -also ``True`` for the same instance of ``x`` but different instances of ``exp``, -so we chose to have ``exp(x) == exp(x)`` even for different instances of ``x``. - -Sometimes, you need to have a unique symbol, for example as a temporary one in -some calculation, which is going to be substituted for something else at the -end anyway. This is achieved using ``Dummy("x")``. So, to sum it -up:: - - >>> from sympy import Symbol, Dummy - >>> Symbol("x") == Symbol("x") - True - - >>> Dummy("x") == Dummy("x") - False - - -Debugging ---------- - -Starting with 0.6.4, you can turn on/off debug messages with the environment -variable SYMPY_DEBUG, which is expected to have the values True or False. For -example, to turn on debugging, you would issue:: - - [user@localhost]: SYMPY_DEBUG=True ./bin/isympy - -Functionality -------------- - -There are no given requirements on classes in the library. For example, if they -don't implement the ``fdiff()`` method and you construct an expression using -such a class, then trying to use the ``Basic.series()`` method will raise an -exception of not finding the ``fdiff()`` method in your class. This "duck -typing" has an advantage that you just implement the functionality which you -need. - -You can define the ``cos`` class like this:: - - class cos(Function): - pass - -and use it like ``1 + cos(x)``, but if you don't implement the ``fdiff()`` method, -you will not be able to call ``(1 + cos(x)).series()``. - -The symbolic object is characterized (defined) by the things which it can do, -so implementing more methods like ``fdiff()``, ``subs()`` etc., you are creating -a "shape" of the symbolic object. Useful things to implement in new classes are: -``hash()`` (to use the class in comparisons), ``fdiff()`` (to use it in series -expansion), ``subs()`` (to use it in expressions, where some parts are being -substituted) and ``series()`` (if the series cannot be computed using the -general ``Basic.series()`` method). When you create a new class, don't worry -about this too much - just try to use it in your code and you will realize -immediately which methods need to be implemented in each situation. - -All objects in sympy are immutable - in the sense that any operation just -returns a new instance (it can return the same instance only if it didn't -change). This is a common mistake to change the current instance, like -``self.arg = self.arg + 1`` (wrong!). Use ``arg = self.arg + 1; return arg`` instead. -The object is immutable in the -sense of the symbolic expression it represents. It can modify itself to keep -track of, for example, its hash. Or it can recalculate anything regarding the -expression it contains. But the expression cannot be changed. So you can pass -any instance to other objects, because you don't have to worry that it will -change, or that this would break anything. - -Conclusion ----------- - -Above are the main ideas behind SymPy that we try to obey. The rest -depends on the current implementation and may possibly change in the future. -The point of all of this is that the interdependencies inside SymPy should be -kept to a minimum. If one wants to add new functionality to SymPy, all that is -necessary is to create a subclass of ``Basic`` and implement what you want. - -Functions ---------- - -How to create a new function with one variable:: - - class sign(Function): - - nargs = 1 - - @classmethod - def eval(cls, arg): - if isinstance(arg, Basic.NaN): - return S.NaN - if isinstance(arg, Basic.Zero): - return S.Zero - if arg.is_positive: - return S.One - if arg.is_negative: - return S.NegativeOne - if isinstance(arg, Basic.Mul): - coeff, terms = arg.as_coeff_terms() - if not isinstance(coeff, Basic.One): - return cls(coeff) * cls(Basic.Mul(*terms)) - - is_bounded = True - - def _eval_conjugate(self): - return self - - def _eval_is_zero(self): - return isinstance(self[0], Basic.Zero) - -and that's it. The ``_eval_*`` functions are called when something is needed. -The ``eval`` is called when the class is about to be instantiated and it -should return either some simplified instance of some other class or if the -class should be unmodified, return ``None`` (see ``core/function.py`` in -``Function.__new__`` for implementation details). See also tests in -`sympy/functions/elementary/tests/test_interface.py -`_ that test this interface. You can use them to create your own new functions. - -The applied function ``sign(x)`` is constructed using -:: - - sign(x) - -both inside and outside of SymPy. Unapplied functions ``sign`` is just the class -itself:: - - sign - -both inside and outside of SymPy. This is the current structure of classes in -SymPy:: - - class BasicType(type): - pass - class MetaBasicMeths(BasicType): - ... - class BasicMeths(AssumeMeths): - __metaclass__ = MetaBasicMeths - ... - class Basic(BasicMeths): - ... - class FunctionClass(MetaBasicMeths): - ... - class Function(Basic, RelMeths, ArithMeths): - __metaclass__ = FunctionClass - ... - -The exact names of the classes and the names of the methods and how they work -can be changed in the future. - -This is how to create a function with two variables:: - - class chebyshevt_root(Function): - nargs = 2 - - @classmethod - def eval(cls, n, k): - if not 0 <= k < n: - raise ValueError("must have 0 <= k < n") - return C.cos(S.Pi*(2*k + 1)/(2*n)) - - -.. note:: the first argument of a @classmethod should be ``cls`` (i.e. not - ``self``). - -Here it's how to define a derivative of the function:: - - >>> from sympy import Function, sympify, cos - >>> class my_function(Function): - ... nargs = 1 - ... - ... def fdiff(self, argindex = 1): - ... return cos(self.args[0]) - ... - ... @classmethod - ... def eval(cls, arg): - ... arg = sympify(arg) - ... if arg == 0: - ... return sympify(0) - -So guess what this ``my_function`` is going to be? Well, it's derivative is -``cos`` and the function value at 0 is 0, but let's pretend we don't know:: - - >>> from sympy import pprint - >>> pprint(my_function(x).series(x, 0, 10)) - 3 5 7 9 - x x x x / 10\ - x - -- + --- - ---- + ------ + O\x / - 6 120 5040 362880 - -Looks familiar indeed:: - - >>> from sympy import sin - >>> pprint(sin(x).series(x, 0, 10)) - 3 5 7 9 - x x x x / 10\ - x - -- + --- - ---- + ------ + O\x / - 6 120 5040 362880 - -Let's try a more complicated example. Let's define the derivative in terms of -the function itself:: - - >>> class what_am_i(Function): - ... nargs = 1 - ... - ... def fdiff(self, argindex = 1): - ... return 1 - what_am_i(self.args[0])**2 - ... - ... @classmethod - ... def eval(cls, arg): - ... arg = sympify(arg) - ... if arg == 0: - ... return sympify(0) - -So what is ``what_am_i``? Let's try it:: - - >>> pprint(what_am_i(x).series(x, 0, 10)) - 3 5 7 9 - x 2*x 17*x 62*x / 10\ - x - -- + ---- - ----- + ----- + O\x / - 3 15 315 2835 - -Well, it's ``tanh``:: - - >>> from sympy import tanh - >>> pprint(tanh(x).series(x, 0, 10)) - 3 5 7 9 - x 2*x 17*x 62*x / 10\ - x - -- + ---- - ----- + ----- + O\x / - 3 15 315 2835 - -The new functions we just defined are regular SymPy objects, you -can use them all over SymPy, e.g.:: - - >>> from sympy import limit - >>> limit(what_am_i(x)/x, x, 0) - 1 - - -Common tasks ------------- - -Please use the same way as is shown below all across SymPy. - -**accessing parameters**:: - - >>> from sympy import sign, sin - >>> from sympy.abc import x, y, z - - >>> e = sign(x**2) - >>> e.args - (x**2,) - - >>> e.args[0] - x**2 - - Number arguments (in Adds and Muls) will always be the first argument; - other arguments might be in arbitrary order: - >>> (1 + x + y*z).args[0] - 1 - >>> (1 + x + y*z).args[1] in (x, y*z) - True - - >>> (y*z).args - (y, z) - - >>> sin(y*z).args - (y*z,) - -Never use internal methods or variables, prefixed with "``_``" (example: don't -use ``_args``, use ``.args`` instead). - -**testing the structure of a SymPy expression** - -Applied functions:: - - >>> from sympy import sign, exp, Function - >>> e = sign(x**2) - - >>> isinstance(e, sign) - True - - >>> isinstance(e, exp) - False - - >>> isinstance(e, Function) - True - -So ``e`` is a ``sign(z)`` function, but not ``exp(z)`` function. - -Unapplied functions:: - - >>> from sympy import sign, exp, FunctionClass - >>> e = sign - - >>> f = exp - - >>> g = Add - - >>> isinstance(e, FunctionClass) - True - - >>> isinstance(f, FunctionClass) - True - - >>> isinstance(g, FunctionClass) - False - - >>> g is Add - True - -So ``e`` and ``f`` are functions, ``g`` is not a function. - -Contributing -============ - -We welcome every SymPy user to participate in it's development. Don't worry if -you've never contributed to any open source project, we'll help you learn -anything necessary, just ask on our mailinglist_. - -Don't be afraid to ask anything and don't worry that you are wasting our time -if you are new to SymPy and ask questions that maybe most of the people know -the answer to -- you are not, because that's exactly what the mailinglist_ is -for and people answer your emails because they want to. Also we try hard to -answer every email, so you'll always get some feedback and pointers what to do -next. - -Improving the code ------------------- - -Go to issues_ that are sorted by priority and simply find something that you -would like to get fixed and fix it. If you find something odd, please report it -into issues_ first before fixing it. Feel free to consult with us on the -mailinglist_. Then send your patch either to the issues_ or the mailinglist_. -See the `SymPy Development -` wiki page, -but don't worry about it too much if you find it too formal - simply get in touch -with us on the mailinglist_ and we'll help you get your patch accepted. - -.. _issues: http://code.google.com/p/sympy/issues/list -.. _mailinglist: http://groups.google.com/group/sympy -.. _SymPyDevelopment: http://code.google.com/p/sympy/wiki/SymPyDevelopment - -Please read our excellent `SymPy Patches Tutorial -`_ at our -wiki for a guide on how to write patches to SymPy, how to work with Git, -and how to make your life easier as you get started with SymPy. - - -Improving the docs ------------------- - -Please see :ref:`the documentation ` how to fix and improve -SymPy's documentation. All contribution is very welcome. diff --git a/dev-py3k/_sources/index.txt b/dev-py3k/_sources/index.txt deleted file mode 100644 index f1f3168df58..00000000000 --- a/dev-py3k/_sources/index.txt +++ /dev/null @@ -1,34 +0,0 @@ -.. SymPy documentation master file, created by sphinx-quickstart.py on Sat Mar 22 19:34:32 2008. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to SymPy's documentation! -================================= - -`SymPy `_ is a Python library for symbolic mathematics. -If you are new to SymPy, start with the Tutorial. - -This is the central page for all of SymPy's documentation. - - -Contents: - -.. toctree:: - :maxdepth: 2 - - install.rst - tutorial/tutorial.en.rst - gotchas.rst - guide.rst - modules/index.rst - python-comparisons.rst - wiki.rst - outreach.rst - aboutus.rst - -If something cannot be easily accessed from this page, it's a bug (`please -report it`_). - -This documentation is maintained with docutils, so you might see some comments in the form #doctest:... . You can safely ignore them. - -.. _please report it: http://code.google.com/p/sympy/issues/list diff --git a/dev-py3k/_sources/install.txt b/dev-py3k/_sources/install.txt deleted file mode 100644 index e6207a0edf3..00000000000 --- a/dev-py3k/_sources/install.txt +++ /dev/null @@ -1,113 +0,0 @@ -Installation ------------- - -The SymPy CAS can be installed on virtually any computer with Python 2.5 or -above. SymPy does not require any special Python modules: let us know if you -have any problems with SymPy on a standard Python install. The current -recommended method of installation is directly from the source files. -Alternatively, executables are available for Windows, and some Linux -distributions have SymPy packages available. - -Source -====== - -SymPy currently recommends that users install directly from the source files. -You will first have to download the source files via the archive. Download the -latest release (tar.gz) from the `downloads site`_ and open it with your -operating system's standard decompression utility. - -When downloading the archive, make sure to get the correct version (Python 2 or -Python 3). If you're not sure which one to use, you probably want the Python 2 -version. Note that you can install both if you want. - -After the download is complete, you should have a folder called "sympy". From -your favorite command line terminal, change directory into that folder and -execute the following:: - - $ python setup.py install - -Alternatively, if you don't want to install the package onto your computer, you -may run SymPy with the "isympy" console (which automatically imports SymPy -packages and defines common symbols) by executing within the "sympy" folder:: - - $ ./bin/isympy - -You may now run SymPy statements directly within the Python shell:: - - >>> - >>> from sympy import * - >>> x, y, z, t = symbols('x y z t') - >>> k, m, n = symbols('k m n', integer=True) - >>> f, g, h = symbols('f g h', cls=Function) - >>> print(diff(x**2/2, x)) - x - -Git -=== - -If you are a developer or like to get the latest updates as they come, be sure -to install from git. To download the repository, execute the following from the -command line:: - - $ git clone git://github.com/sympy/sympy.git - -Then, execute either the setup.py or the bin/isympy scripts as demonstrated -above. - -To update to the latest version, go into your repository and execute:: - - $ git pull origin master - -If you want to install SymPy, but still want to use the git version, you can run -from your repository:: - - $ setupegg.py develop - -This will cause the installed version to always point to the version in the git -directory. - -If you're using the git repository with Python 3, you have to use the -``./bin/use2to3`` script to build the Python 3 version of SymPy. This will put -everything in the py3k-sympy directory. - -Other Methods -============= - -An installation executable is available for Windows users at the -`downloads site`_ (.exe). In addition, various Linux distributions have SymPy -available as a package. Others are strongly encouraged to download from source -(details above). - -Run SymPy -========= - -After installation, it is best to verify that your freshly-installed SymPy -works. To do this, start up Python and import the SymPy libraries:: - - $ python - >>> from sympy import * - -From here, execute some simple SymPy statements like the ones below:: - - >>> x = Symbol('x') - >>> limit(sin(x)/x, x, 0) - 1 - >>> integrate(1/x, x) - log(x) - -For a starter guide on using SymPy effectively, refer to the :ref:`tutorial`. - -Questions -========= - -If you have a question about installation or SymPy in general, feel free to -visit the IRC channel at irc.freenode.net, channel `#sympy`_. In addition, -our `mailing list`_ is an excellent source of community support. - -If you think there's a bug or you would like to request a feature, please open -an `issue ticket`_. - -.. _downloads site: https://code.google.com/p/sympy/downloads/list -.. _#sympy: irc://irc.freenode.net/sympy -.. _issue ticket: http://code.google.com/p/sympy/issues/list -.. _mailing list: http://groups.google.com/group/sympy diff --git a/dev-py3k/_sources/modules/assumptions/ask.txt b/dev-py3k/_sources/modules/assumptions/ask.txt deleted file mode 100644 index a0642e6aacb..00000000000 --- a/dev-py3k/_sources/modules/assumptions/ask.txt +++ /dev/null @@ -1,6 +0,0 @@ -=== -Ask -=== - -.. automodule:: sympy.assumptions.ask - :members: diff --git a/dev-py3k/_sources/modules/assumptions/assume.txt b/dev-py3k/_sources/modules/assumptions/assume.txt deleted file mode 100644 index eeb09bf1f24..00000000000 --- a/dev-py3k/_sources/modules/assumptions/assume.txt +++ /dev/null @@ -1,6 +0,0 @@ -====== -Assume -====== - -.. automodule:: sympy.assumptions.assume - :members: diff --git a/dev-py3k/_sources/modules/assumptions/handlers/calculus.txt b/dev-py3k/_sources/modules/assumptions/handlers/calculus.txt deleted file mode 100644 index 2a668aa9f9d..00000000000 --- a/dev-py3k/_sources/modules/assumptions/handlers/calculus.txt +++ /dev/null @@ -1,6 +0,0 @@ -======== -Calculus -======== - -.. automodule:: sympy.assumptions.handlers.calculus - :members: diff --git a/dev-py3k/_sources/modules/assumptions/handlers/index.txt b/dev-py3k/_sources/modules/assumptions/handlers/index.txt deleted file mode 100644 index a446fbdcdd2..00000000000 --- a/dev-py3k/_sources/modules/assumptions/handlers/index.txt +++ /dev/null @@ -1,16 +0,0 @@ -======== -Handlers -======== - -.. automodule:: sympy.assumptions.handlers - -Contents -======== - -.. toctree:: - :maxdepth: 3 - - calculus.rst - ntheory.rst - order.rst - sets.rst diff --git a/dev-py3k/_sources/modules/assumptions/handlers/ntheory.txt b/dev-py3k/_sources/modules/assumptions/handlers/ntheory.txt deleted file mode 100644 index 63c2b176e66..00000000000 --- a/dev-py3k/_sources/modules/assumptions/handlers/ntheory.txt +++ /dev/null @@ -1,6 +0,0 @@ -======= -nTheory -======= - -.. automodule:: sympy.assumptions.handlers.ntheory - :members: diff --git a/dev-py3k/_sources/modules/assumptions/handlers/order.txt b/dev-py3k/_sources/modules/assumptions/handlers/order.txt deleted file mode 100644 index 040cacc8131..00000000000 --- a/dev-py3k/_sources/modules/assumptions/handlers/order.txt +++ /dev/null @@ -1,6 +0,0 @@ -===== -Order -===== - -.. automodule:: sympy.assumptions.handlers.order - :members: diff --git a/dev-py3k/_sources/modules/assumptions/handlers/sets.txt b/dev-py3k/_sources/modules/assumptions/handlers/sets.txt deleted file mode 100644 index ac464967902..00000000000 --- a/dev-py3k/_sources/modules/assumptions/handlers/sets.txt +++ /dev/null @@ -1,6 +0,0 @@ -==== -Sets -==== - -.. automodule:: sympy.assumptions.handlers.sets - :members: diff --git a/dev-py3k/_sources/modules/assumptions/index.txt b/dev-py3k/_sources/modules/assumptions/index.txt deleted file mode 100644 index 2b52d7e261f..00000000000 --- a/dev-py3k/_sources/modules/assumptions/index.txt +++ /dev/null @@ -1,402 +0,0 @@ -================== -Assumptions module -================== - -.. automodule:: sympy.assumptions - -Contents -======== - -.. toctree:: - :maxdepth: 3 - - ask.rst - assume.rst - refine.rst - handlers/index.rst - -Queries are used to ask information about expressions. Main method for this -is ask(): - -.. autofunction:: sympy.assumptions.ask.ask - :noindex: - -Querying -======== - -ask's optional second argument should be a boolean expression involving -assumptions about objects in expr. Valid values include: - - * Q.integer(x) - * Q.positive(x) - * Q.integer(x) & Q.positive(x) - * etc. - -Q is a class in sympy.assumptions holding known predicates. - -See documentation for the logic module for a complete list of valid boolean -expressions. - -You can also define global assumptions so you don't have to pass that argument -each time to function ask(). This is done by using the global_assumptions -object from module sympy.assumptions. You can then clear global assumptions -with global_assumptions.clear():: - - >>> from sympy import * - >>> x = Symbol('x') - >>> global_assumptions.add(Q.positive(x)) - >>> ask(Q.positive(x)) - True - >>> global_assumptions.clear() - - -Supported predicates -==================== - -bounded -------- - -Test that a function is bounded with respect to its variables. For example, -sin(x) is a bounded functions, but exp(x) is not. - -Examples:: - - >>> from sympy import * - >>> x = Symbol('x') - >>> ask(Q.bounded(exp(x)), ~Q.bounded(x)) - False - >>> ask(Q.bounded(exp(x)) , Q.bounded(x)) - True - >>> ask(Q.bounded(sin(x)), ~Q.bounded(x)) - True - - -commutative ------------ - -Test that objects are commutative. By default, symbols in SymPy are considered -commutative except otherwise stated. - -Examples:: - - >>> from sympy import * - >>> x, y = symbols('x,y') - >>> ask(Q.commutative(x)) - True - >>> ask(Q.commutative(x), ~Q.commutative(x)) - False - >>> ask(Q.commutative(x*y), ~Q.commutative(x)) - False - - -complex -------- - -Test that expression belongs to the field of complex numbers. - -Examples:: - - >>> from sympy import * - >>> ask(Q.complex(2)) - True - >>> ask(Q.complex(I)) - True - >>> x, y = symbols('x,y') - >>> ask(Q.complex(x+I*y), Q.real(x) & Q.real(y)) - True - - -even ----- - -Test that expression represents an even number, that is, an number that -can be written in the form 2*n, n integer. - -Examples:: - - >>> from sympy import * - >>> ask(Q.even(2)) - True - >>> n = Symbol('n') - >>> ask(Q.even(2*n), Q.integer(n)) - True - - -extended_real -------------- - -Test that an expression belongs to the field of extended real numbers, that is, real -numbers union {Infinity, -Infinity}. - -Examples:: - - >>> from sympy import * - >>> ask(Q.extended_real(oo)) - True - >>> ask(Q.extended_real(2)) - True - >>> ask(Q.extended_real(x), Q.real(x)) - True - - -imaginary ---------- - -Test that an expression belongs to the set of imaginary numbers, that is, - it can be written as x*I, where x is real and I is the imaginary unit. - -Examples:: - - >>> from sympy import * - >>> ask(Q.imaginary(2*I)) - True - >>> x = Symbol('x') - >>> ask(Q.imaginary(x*I), Q.real(x)) - True - - -infinitesimal -------------- - -Test that an expression is equivalent to an infinitesimal number. - -Examples:: - - >>> from sympy import * - >>> ask(Q.infinitesimal(1/oo)) - True - >>> x, y = symbols('x,y') - >>> ask(Q.infinitesimal(2*x), Q.infinitesimal(x)) - True - >>> ask(Q.infinitesimal(x*y), Q.infinitesimal(x) & Q.bounded(y)) - True - - -integer -------- - -Test that an expression belongs to the set of integer numbers. - -Examples:: - - >>> from sympy import * - >>> ask(Q.integer(2)) - True - >>> ask(Q.integer(sqrt(2))) - False - >>> x = Symbol('x') - >>> ask(Q.integer(x/2), Q.even(x)) - True - - -irrational ----------- - -Test that an expression represents an irrational number. - -Examples:: - - >>> from sympy import * - >>> ask(Q.irrational(pi)) - True - >>> ask(Q.irrational(sqrt(2))) - True - >>> ask(Q.irrational(x*sqrt(2)), Q.rational(x)) - True - - -rational --------- - -Test that an expression represents a rational number. - -Examples:: - - >>> from sympy import * - >>> ask(Q.rational(Rational(3, 4))) - True - >>> x, y = symbols('x,y') - >>> ask(Q.rational(x/2), Q.integer(x)) - True - >>> ask(Q.rational(x/y), Q.integer(x) & Q.integer(y)) - True - - -negative --------- - -Test that an expression is less (strict) than zero. - -Examples:: - - >>> from sympy import * - >>> ask(Q.negative(0.3)) - False - >>> x = Symbol('x') - >>> ask(Q.negative(-x), Q.positive(x)) - True - -Remarks -^^^^^^^ -negative numbers are defined as real numbers that are not zero nor positive, so -complex numbers (with nontrivial imaginary coefficients) will return False -for this predicate. The same applies to Q.positive. - - -positive --------- - -Test that a given expression is greater (strict) than zero. - -Examples:: - - >>> from sympy import * - >>> ask(Q.positive(0.3)) - True - >>> x = Symbol('x') - >>> ask(Q.positive(-x), Q.negative(x)) - True - -Remarks -^^^^^^^ -see Remarks for negative - - -prime ------ - -Test that an expression represents a prime number. - -Examples:: - - >>> from sympy import * - >>> ask(Q.prime(13)) - True - -Remarks: Use sympy.ntheory.isprime to test numeric values efficiently. - - -real ----- - -Test that an expression belongs to the field of real numbers. - -Examples:: - - >>> from sympy import * - >>> ask(Q.real(sqrt(2))) - True - >>> x, y = symbols('x,y') - >>> ask(Q.real(x*y), Q.real(x) & Q.real(y)) - True - - -odd ---- - -Test that an expression represents an odd number. - -Examples:: - - >>> from sympy import * - >>> ask(Q.odd(3)) - True - >>> n = Symbol('n') - >>> ask(Q.odd(2*n + 1), Q.integer(n)) - True - - -nonzero -------- - -Test that an expression is not zero. - -Examples:: - - >>> from sympy import * - >>> x = Symbol('x') - >>> ask(Q.nonzero(x), Q.positive(x) | Q.negative(x)) - True - - -Design -====== - -Each time ask is called, the appropriate Handler for the current key is called. This is -always a subclass of sympy.assumptions.AskHandler. It's classmethods have the name's of the classes -it supports. For example, a (simplified) AskHandler for the ask 'positive' would -look like this:: - - class AskPositiveHandler(CommonHandler): - - def Mul(self): - # return True if all argument's in self.expr.args are positive - ... - - def Add(self): - for arg in self.expr.args: - if not ask(arg, positive, self.assumptions): - break - else: - # if all argument's are positive - return True - ... - -The .Mul() method is called when self.expr is an instance of Mul, the Add method -would be called when self.expr is an instance of Add and so on. - - -Extensibility -============= - -You can define new queries or support new types by subclassing sympy.assumptions.AskHandler - and registering that handler for a particular key by calling register_handler: - -.. autofunction:: sympy.assumptions.ask.register_handler - :noindex: - -You can undo this operation by calling remove_handler. - -.. autofunction:: sympy.assumptions.ask.remove_handler - :noindex: - -You can support new types [1]_ by adding a handler to an existing key. In the -following example, we will create a new type MyType and extend the key 'prime' -to accept this type (and return True) - -.. parsed-literal:: - - >>> from sympy.core import Basic - >>> from sympy.assumptions import register_handler - >>> from sympy.assumptions.handlers import AskHandler - >>> class MyType(Basic): - ... pass - >>> class MyAskHandler(AskHandler): - ... @staticmethod - ... def MyType(expr, assumptions): - ... return True - >>> a = MyType() - >>> register_handler('prime', MyAskHandler) - >>> ask(Q.prime(a)) - True - - -Performance improvements -======================== - -On queries that involve symbolic coefficients, logical inference is used. Work on -improving satisfiable function (sympy.logic.inference.satisfiable) should result -in notable speed improvements. - -Logic inference used in one ask could be used to speed up further queries, and -current system does not take advantage of this. For example, a truth maintenance -system (http://en.wikipedia.org/wiki/Truth_maintenance_system) could be implemented. - -Misc -==== - -You can find more examples in the in the form of test under directory -sympy/assumptions/tests/ - -.. [1] New type must inherit from Basic, otherwise an exception will be raised. - This is a bug and should be fixed. diff --git a/dev-py3k/_sources/modules/assumptions/refine.txt b/dev-py3k/_sources/modules/assumptions/refine.txt deleted file mode 100644 index eb4a7173a61..00000000000 --- a/dev-py3k/_sources/modules/assumptions/refine.txt +++ /dev/null @@ -1,6 +0,0 @@ -====== -Refine -====== - -.. automodule:: sympy.assumptions.refine - :members: diff --git a/dev-py3k/_sources/modules/categories.txt b/dev-py3k/_sources/modules/categories.txt deleted file mode 100644 index cba475621cc..00000000000 --- a/dev-py3k/_sources/modules/categories.txt +++ /dev/null @@ -1,71 +0,0 @@ -Category Theory Module -====================== - -Introduction ------------- - -The category theory module for SymPy will allow manipulating diagrams -within a single category, including drawing them in TikZ and deciding -whether they are commutative or not. - -The general reference work this module tries to follow is - - [JoyOfCats] J. Adamek, H. Herrlich. G. E. Strecker: Abstract and - Concrete Categories. The Joy of Cats. - -The latest version of this book should be available for free download -from - - katmat.math.uni-bremen.de/acc/acc.pdf - -The module is still in its pre-embryonic stage. - -Base Class Reference --------------------- - -.. module:: sympy.categories - -This section lists the classes which implement some of the basic -notions in category theory: objects, morphisms, categories, and -diagrams. - -.. autoclass:: Object - :members: - -.. autoclass:: Morphism - :members: - -.. autoclass:: NamedMorphism - :members: - -.. autoclass:: CompositeMorphism - :members: - -.. autoclass:: IdentityMorphism - :members: - -.. autoclass:: Category - :members: - -.. autoclass:: Diagram - :members: - -Diagram Drawing ---------------- - -.. module:: sympy.categories.diagram_drawing - -This section lists the classes which allow automatic drawing of -diagrams. - -.. autoclass:: DiagramGrid - :members: - -.. autoclass:: ArrowStringDescription - -.. autoclass:: XypicDiagramDrawer - :members: - -.. autofunction:: xypic_draw_diagram - -.. autofunction:: preview_diagram diff --git a/dev-py3k/_sources/modules/combinatorics/graycode.txt b/dev-py3k/_sources/modules/combinatorics/graycode.txt deleted file mode 100644 index 80ca0810d2f..00000000000 --- a/dev-py3k/_sources/modules/combinatorics/graycode.txt +++ /dev/null @@ -1,19 +0,0 @@ -.. _combinatorics-graycode: - -Gray Code -========= - -.. module:: sympy.combinatorics.graycode - -.. autoclass:: GrayCode - :members: - -.. automethod:: sympy.combinatorics.graycode.random_bitstring - -.. automethod:: sympy.combinatorics.graycode.gray_to_bin - -.. automethod:: sympy.combinatorics.graycode.bin_to_gray - -.. automethod:: sympy.combinatorics.graycode.get_subset_from_bitstring - -.. automethod:: sympy.combinatorics.graycode.graycode_subsets diff --git a/dev-py3k/_sources/modules/combinatorics/group_constructs.txt b/dev-py3k/_sources/modules/combinatorics/group_constructs.txt deleted file mode 100644 index 3861622cdd3..00000000000 --- a/dev-py3k/_sources/modules/combinatorics/group_constructs.txt +++ /dev/null @@ -1,8 +0,0 @@ -.. _combinatorics-group_constructs: - -Group constructors -================== - -.. module:: sympy.combinatorics.group_constructs - -.. autofunction:: DirectProduct diff --git a/dev-py3k/_sources/modules/combinatorics/index.txt b/dev-py3k/_sources/modules/combinatorics/index.txt deleted file mode 100644 index 2834e376f1d..00000000000 --- a/dev-py3k/_sources/modules/combinatorics/index.txt +++ /dev/null @@ -1,24 +0,0 @@ -.. _combinatiorics-docs: - -==================== -Combinatorics Module -==================== - -Contents -======== - -.. toctree:: - :maxdepth: 2 - - partitions.rst - permutations.rst - perm_groups.rst - polyhedron.rst - prufer.rst - subsets.rst - graycode.rst - named_groups.rst - util.rst - group_constructs.rst - testutil.rst - tensor_can.rst diff --git a/dev-py3k/_sources/modules/combinatorics/named_groups.txt b/dev-py3k/_sources/modules/combinatorics/named_groups.txt deleted file mode 100644 index 7ad565d40d6..00000000000 --- a/dev-py3k/_sources/modules/combinatorics/named_groups.txt +++ /dev/null @@ -1,16 +0,0 @@ -.. _combinatorics-named_groups: - -Named Groups -============ - -.. module:: sympy.combinatorics.named_groups - -.. autofunction:: SymmetricGroup - -.. autofunction:: CyclicGroup - -.. autofunction:: DihedralGroup - -.. autofunction:: AlternatingGroup - -.. autofunction:: AbelianGroup diff --git a/dev-py3k/_sources/modules/combinatorics/partitions.txt b/dev-py3k/_sources/modules/combinatorics/partitions.txt deleted file mode 100644 index 8935babe493..00000000000 --- a/dev-py3k/_sources/modules/combinatorics/partitions.txt +++ /dev/null @@ -1,22 +0,0 @@ -.. _combinatorics-partitions: - -Partitions -========== - -.. module:: sympy.combinatorics.partitions - -.. autoclass:: Partition - :members: - -.. autoclass:: IntegerPartition - :members: - -.. autofunction:: random_integer_partition - -.. autofunction:: RGS_generalized - -.. autofunction:: RGS_enum - -.. autofunction:: RGS_unrank - -.. autofunction:: RGS_rank diff --git a/dev-py3k/_sources/modules/combinatorics/perm_groups.txt b/dev-py3k/_sources/modules/combinatorics/perm_groups.txt deleted file mode 100644 index 6f15f263227..00000000000 --- a/dev-py3k/_sources/modules/combinatorics/perm_groups.txt +++ /dev/null @@ -1,9 +0,0 @@ -.. _combinatorics-perm_groups: - -Permutation Groups -================== - -.. module:: sympy.combinatorics.perm_groups - -.. autoclass:: PermutationGroup - :members: diff --git a/dev-py3k/_sources/modules/combinatorics/permutations.txt b/dev-py3k/_sources/modules/combinatorics/permutations.txt deleted file mode 100644 index c518e2b2ee4..00000000000 --- a/dev-py3k/_sources/modules/combinatorics/permutations.txt +++ /dev/null @@ -1,27 +0,0 @@ -.. _combinatorics-permutations: - -Permutations -============ - -.. module:: sympy.combinatorics.permutations - -.. autoclass:: Permutation - :members: - -.. autoclass:: Cycle - :members: - -.. _combinatorics-generators: - -Generators ----------- - -.. module:: sympy.combinatorics.generators - -.. automethod:: sympy.combinatorics.generators.symmetric - -.. automethod:: sympy.combinatorics.generators.cyclic - -.. automethod:: sympy.combinatorics.generators.alternating - -.. automethod:: sympy.combinatorics.generators.dihedral diff --git a/dev-py3k/_sources/modules/combinatorics/polyhedron.txt b/dev-py3k/_sources/modules/combinatorics/polyhedron.txt deleted file mode 100644 index 0f4d03f8d3b..00000000000 --- a/dev-py3k/_sources/modules/combinatorics/polyhedron.txt +++ /dev/null @@ -1,9 +0,0 @@ -.. _combinatorics-polyhedron: - -Polyhedron -========== - -.. module:: sympy.combinatorics.polyhedron - -.. autoclass:: Polyhedron - :members: diff --git a/dev-py3k/_sources/modules/combinatorics/prufer.txt b/dev-py3k/_sources/modules/combinatorics/prufer.txt deleted file mode 100644 index 36b6c22fb14..00000000000 --- a/dev-py3k/_sources/modules/combinatorics/prufer.txt +++ /dev/null @@ -1,9 +0,0 @@ -.. _combinatorics-prufer: - -Prufer Sequences -================ - -.. module:: sympy.combinatorics.prufer - -.. autoclass:: Prufer - :members: diff --git a/dev-py3k/_sources/modules/combinatorics/subsets.txt b/dev-py3k/_sources/modules/combinatorics/subsets.txt deleted file mode 100644 index ec91e71c674..00000000000 --- a/dev-py3k/_sources/modules/combinatorics/subsets.txt +++ /dev/null @@ -1,11 +0,0 @@ -.. _combinatorics-subsets: - -Subsets -======= - -.. module:: sympy.combinatorics.subsets - -.. autoclass:: Subset - :members: - -.. automethod:: sympy.combinatorics.subsets.ksubsets diff --git a/dev-py3k/_sources/modules/combinatorics/tensor_can.txt b/dev-py3k/_sources/modules/combinatorics/tensor_can.txt deleted file mode 100644 index 8e2e9a67657..00000000000 --- a/dev-py3k/_sources/modules/combinatorics/tensor_can.txt +++ /dev/null @@ -1,14 +0,0 @@ -.. _combinatorics-tensor_can: - -Tensor Canonicalization -======================= - -.. module:: sympy.combinatorics.tensor_can - -.. autofunction:: canonicalize - -.. autofunction:: double_coset_can_rep - -.. autofunction:: get_symmetric_group_sgs - -.. autofunction:: bsgs_direct_product diff --git a/dev-py3k/_sources/modules/combinatorics/testutil.txt b/dev-py3k/_sources/modules/combinatorics/testutil.txt deleted file mode 100644 index 507510b9a2d..00000000000 --- a/dev-py3k/_sources/modules/combinatorics/testutil.txt +++ /dev/null @@ -1,16 +0,0 @@ -.. _combinatorics-testutil: - -Test Utilities -============== - -.. module:: sympy.combinatorics.testutil - -.. autofunction:: _cmp_perm_lists - -.. autofunction:: _naive_list_centralizer - -.. autofunction:: _verify_bsgs - -.. autofunction:: _verify_centralizer - -.. autofunction:: _verify_normal_closure diff --git a/dev-py3k/_sources/modules/combinatorics/util.txt b/dev-py3k/_sources/modules/combinatorics/util.txt deleted file mode 100644 index 2c49bcbac10..00000000000 --- a/dev-py3k/_sources/modules/combinatorics/util.txt +++ /dev/null @@ -1,22 +0,0 @@ -.. _combinatorics-util: - -Utilities -============ - -.. module:: sympy.combinatorics.util - -.. autofunction:: _base_ordering - -.. autofunction:: _check_cycles_alt_sym - -.. autofunction:: _distribute_gens_by_base - -.. autofunction:: _handle_precomputed_bsgs - -.. autofunction:: _orbits_transversals_from_bsgs - -.. autofunction:: _remove_gens - -.. autofunction:: _strip - -.. autofunction:: _strong_gens_from_distr diff --git a/dev-py3k/_sources/modules/concrete.txt b/dev-py3k/_sources/modules/concrete.txt deleted file mode 100644 index 159fefb8853..00000000000 --- a/dev-py3k/_sources/modules/concrete.txt +++ /dev/null @@ -1,100 +0,0 @@ -Concrete Mathematics -==================== - -Hypergeometric terms --------------------- - -The center stage, in recurrence solving and summations, play hypergeometric -terms. Formally these are sequences annihilated by first order linear -recurrence operators. In simple words if we are given term `a(n)` then it is -hypergeometric if its consecutive term ratio is a rational function in `n`. - -To check if a sequence is of this type you can use the ``is_hypergeometric`` -method which is available in Basic class. Here is simple example involving a -polynomial: - - >>> from sympy import * - >>> n, k = symbols('n,k') - >>> (n**2 + 1).is_hypergeometric(n) - True - -Of course polynomials are hypergeometric but are there any more complicated -sequences of this type? Here are some trivial examples: - - >>> factorial(n).is_hypergeometric(n) - True - >>> binomial(n, k).is_hypergeometric(n) - True - >>> rf(n, k).is_hypergeometric(n) - True - >>> ff(n, k).is_hypergeometric(n) - True - >>> gamma(n).is_hypergeometric(n) - True - >>> (2**n).is_hypergeometric(n) - True - -We see that all species used in summations and other parts of concrete -mathematics are hypergeometric. Note also that binomial coefficients and both -rising and falling factorials are hypergeometric in both their arguments: - - >>> binomial(n, k).is_hypergeometric(k) - True - >>> rf(n, k).is_hypergeometric(k) - True - >>> ff(n, k).is_hypergeometric(k) - True - -To say more, all previously shown examples are valid for integer linear -arguments: - - >>> factorial(2*n).is_hypergeometric(n) - True - >>> binomial(3*n+1, k).is_hypergeometric(n) - True - >>> rf(n+1, k-1).is_hypergeometric(n) - True - >>> ff(n-1, k+1).is_hypergeometric(n) - True - >>> gamma(5*n).is_hypergeometric(n) - True - >>> (2**(n-7)).is_hypergeometric(n) - True - -However nonlinear arguments make those sequences fail to be hypergeometric: - - >>> factorial(n**2).is_hypergeometric(n) - False - >>> (2**(n**3 + 1)).is_hypergeometric(n) - False - -If not only the knowledge of being hypergeometric or not is needed, you can use -``hypersimp()`` function. It will try to simplify combinatorial expression and -if the term given is hypergeometric it will return a quotient of polynomials of -minimal degree. Otherwise is will return `None` to say that sequence is not -hypergeometric: - - >>> hypersimp(factorial(2*n), n) - 4*n**2 + 6*n + 2 - >>> hypersimp(factorial(n**2), n) - -Concrete Class Reference ------------------------- -.. autoclass:: sympy.concrete.summations.Sum - :members: - -.. autoclass:: sympy.concrete.products.Product - :members: - -Concrete Functions Reference ----------------------------- - -.. autofunction:: sympy.concrete.summations.summation - -.. autofunction:: sympy.concrete.products.product - -.. autofunction:: sympy.concrete.gosper.gosper_normal - -.. autofunction:: sympy.concrete.gosper.gosper_term - -.. autofunction:: sympy.concrete.gosper.gosper_sum diff --git a/dev-py3k/_sources/modules/core.txt b/dev-py3k/_sources/modules/core.txt deleted file mode 100644 index 43efb10e835..00000000000 --- a/dev-py3k/_sources/modules/core.txt +++ /dev/null @@ -1,463 +0,0 @@ -SymPy Core -========== - -sympify -------- -.. module:: sympy.core.sympify - -sympify -^^^^^^^ -.. autofunction:: sympify - -cache -------- -.. module:: sympy.core.cache - -cacheit -^^^^^^^ -.. autofunction:: cacheit - -basic ------ -.. module:: sympy.core.basic - -Basic -^^^^^ -.. autoclass:: Basic - :members: - -Atom -^^^^ -.. autoclass:: Atom - :members: - -C -^ -.. autoclass:: C - :members: - -singleton ---------- -.. module:: sympy.core.singleton - -S -^ -.. autoclass:: S - :members: - -expr ----- -.. module:: sympy.core.expr - -Expr ----- -.. autoclass:: Expr - :members: - -AtomicExpr ----------- -.. autoclass:: AtomicExpr - :members: - -symbol ------- -.. module:: sympy.core.symbol - -Symbol -^^^^^^ -.. autoclass:: Symbol - :members: - -Wild -^^^^ -.. autoclass:: Wild - :members: - -Dummy -^^^^^ -.. autoclass:: Dummy - :members: - -symbols -^^^^^^^ -.. autofunction:: symbols - -var -^^^ -.. autofunction:: var - -numbers -------- -.. module:: sympy.core.numbers - -Number -^^^^^^ -.. autoclass:: Number - :members: - -Float -^^^^^ -.. autoclass:: Float - :members: - -Rational -^^^^^^^^ -.. autoclass:: Rational - :members: - -Integer -^^^^^^^ -.. autoclass:: Integer - :members: - -NumberSymbol -^^^^^^^^^^^^ -.. autoclass:: NumberSymbol - :members: - -RealNumber -^^^^^^^^^^ -.. autoclass:: RealNumber - :members: - -Real -^^^^ -.. autoclass:: Real - :members: - -igcd -^^^^ -.. autofunction:: igcd - -ilcm -^^^^ -.. autofunction:: ilcm - -seterr -^^^^^^ -.. autofunction:: seterr - -E -^ -.. autoclass:: E - :members: - -I -^ -.. autoclass:: I - :members: - -nan -^^^ -.. autofunction:: nan - -oo -^^ -.. autofunction:: oo - -pi -^^ -.. autofunction:: pi - -zoo -^^^ -.. autofunction:: zoo - -power ------ -.. module:: sympy.core.power - -Pow -^^^ -.. autoclass:: Pow - :members: - -integer_nthroot -^^^^^^^^^^^^^^^ -.. autofunction:: integer_nthroot - -mul ---- -.. module:: sympy.core.mul - -Mul -^^^ -.. autoclass:: Mul - :members: - -prod -^^^^ -.. autofunction:: prod - -add ---- -.. module:: sympy.core.add - -Add -^^^ -.. autoclass:: Add - :members: - -mod ---- -.. module:: sympy.core.mod - -Mod -^^^ -.. autoclass:: Mod - :members: - -relational ----------- -.. module:: sympy.core.relational - -Rel -^^^ -.. autoclass:: Rel - :members: - -Eq -^^ -.. autoclass:: Eq - :members: - -Ne -^^ -.. autoclass:: Ne - :members: - -Lt -^^ -.. autoclass:: Lt - :members: - -Le -^^ -.. autoclass:: Le - :members: - -Gt -^^ -.. autoclass:: Gt - :members: - -Ge -^^ -.. autoclass:: Ge - :members: - -Equality -^^^^^^^^ -.. autoclass:: Equality - :members: - -GreaterThan -^^^^^^^^^^^ -.. autoclass:: GreaterThan - :members: - -LessThan -^^^^^^^^ -.. autoclass:: LessThan - :members: - -Unequality -^^^^^^^^^^ -.. autoclass:: Unequality - :members: - -StrictGreaterThan -^^^^^^^^^^^^^^^^^ -.. autoclass:: StrictGreaterThan - :members: - -StrictLessThan -^^^^^^^^^^^^^^ -.. autoclass:: StrictLessThan - :members: - -multidimensional ----------------- -.. module:: sympy.core.multidimensional - -vectorize -^^^^^^^^^ -.. autoclass:: vectorize - :members: - -function --------- -.. module:: sympy.core.function - -Lambda -^^^^^^ -.. autoclass:: Lambda - :members: - -WildFunction -^^^^^^^^^^^^ -.. autoclass:: WildFunction - :members: - -Derivative -^^^^^^^^^^ -.. autoclass:: Derivative - :members: - -diff -^^^^ -.. autofunction:: diff - -FunctionClass -^^^^^^^^^^^^^ -.. autoclass:: FunctionClass - :members: - -Function -^^^^^^^^ -.. autoclass:: Function - :members: - -.. note:: Not all functions are the same - - SymPy defines many functions (like ``cos`` and ``factorial``). It also - allows the user to create generic functions which act as argument - holders. Such functions are created just like symbols: - - >>> from sympy import Function, cos - >>> from sympy.abc import x - >>> f = Function('f') - >>> f(2) + f(x) - f(2) + f(x) - - If you want to see which functions appear in an expression you can use - the atoms method: - - >>> e = (f(x) + cos(x) + 2) - >>> e.atoms(Function) - set([f(x), cos(x)]) - - If you just want the function you defined, not SymPy functions, the - thing to search for is AppliedUndef: - - >>> from sympy.core.function import AppliedUndef - >>> e.atoms(AppliedUndef) - set([f(x)]) - -Subs -^^^^ -.. autoclass:: Subs - :members: - -expand -^^^^^^ -.. autofunction:: expand - -PoleError -^^^^^^^^^ -.. autoclass:: PoleError - :members: - -count_ops -^^^^^^^^^ -.. autofunction:: count_ops - -expand_mul -^^^^^^^^^^ -.. autofunction:: expand_mul - -expand_log -^^^^^^^^^^ -.. autofunction:: expand_log - -expand_func -^^^^^^^^^^^ -.. autofunction:: expand_func - -expand_trig -^^^^^^^^^^^ -.. autofunction:: expand_trig - -expand_complex -^^^^^^^^^^^^^^ -.. autofunction:: expand_complex - -expand_multinomial -^^^^^^^^^^^^^^^^^^ -.. autofunction:: expand_multinomial - -expand_power_exp -^^^^^^^^^^^^^^^^ -.. autofunction:: expand_power_exp - -expand_power_base -^^^^^^^^^^^^^^^^^ -.. autofunction:: expand_power_base - -nfloat -^^^^^^ -.. autofunction:: nfloat - -evalf ------ -.. module:: sympy.core.evalf - -PrecisionExhausted -^^^^^^^^^^^^^^^^^^ -.. autoclass:: PrecisionExhausted - :members: - -N -^ -.. autoclass:: N - :members: - -containers ----------- -.. module:: sympy.core.containers - -Tuple -^^^^^ -.. autoclass:: Tuple - :members: - -Dict -^^^^ -.. autoclass:: Dict - :members: - -compatibility -------------- -.. module:: sympy.core.compatibility - -iterable -^^^^^^^^ -.. autofunction:: iterable - -is_sequence -^^^^^^^^^^^ -.. autofunction:: is_sequence - -set_intersection -^^^^^^^^^^^^^^^^ -.. autofunction:: set_intersection - -set_union -^^^^^^^^^ -.. autofunction:: set_union - -as_int -^^^^^^ -.. autofunction:: as_int - -exprtools ---------- -.. module:: sympy.core.exprtools - -gcd_terms -^^^^^^^^^ -.. autofunction:: gcd_terms - -factor_terms -^^^^^^^^^^^^ -.. autofunction:: factor_terms diff --git a/dev-py3k/_sources/modules/diffgeom.txt b/dev-py3k/_sources/modules/diffgeom.txt deleted file mode 100644 index 4379d426cf5..00000000000 --- a/dev-py3k/_sources/modules/diffgeom.txt +++ /dev/null @@ -1,33 +0,0 @@ -Differential Geometry Module -============================ - -.. module:: sympy.diffgeom - -Introduction ------------- - -Base Class Reference --------------------- -.. autoclass:: Manifold - :members: - -.. autoclass:: Patch - :members: - -.. autoclass:: CoordSystem - :members: - -.. autoclass:: BaseScalarField - :members: - -.. autoclass:: BaseVectorField - :members: - -.. autoclass:: Differential - :members: - -.. autoclass:: BaseCovarDerivativeOp - :members: - -.. autoclass:: CovarDerivativeOp - :members: diff --git a/dev-py3k/_sources/modules/evalf.txt b/dev-py3k/_sources/modules/evalf.txt deleted file mode 100644 index 67f6f340285..00000000000 --- a/dev-py3k/_sources/modules/evalf.txt +++ /dev/null @@ -1,424 +0,0 @@ -.. _evalf-label: - -Numerical evaluation -==================== - -Basics ------- - -Exact SymPy expressions can be converted to floating-point approximations -(decimal numbers) using either the ``.evalf()`` method or the ``N()`` function. -``N(expr, )`` is equivalent to ``sympify(expr).evalf()``. - - >>> from sympy import * - >>> N(sqrt(2)*pi) - 4.44288293815837 - >>> (sqrt(2)*pi).evalf() - 4.44288293815837 - - -By default, numerical evaluation is performed to an accuracy of 15 decimal -digits. You can optionally pass a desired accuracy (which should be a positive -integer) as an argument to ``evalf`` or ``N``: - - >>> N(sqrt(2)*pi, 5) - 4.4429 - >>> N(sqrt(2)*pi, 50) - 4.4428829381583662470158809900606936986146216893757 - - -Complex numbers are supported: - - >>> N(1/(pi + I), 20) - 0.28902548222223624241 - 0.091999668350375232456*I - - -If the expression contains symbols or for some other reason cannot be evaluated -numerically, calling ``.evalf()`` or ``N()`` returns the original expression, or -in some cases a partially evaluated expression. For example, when the -expression is a polynomial in expanded form, the coefficients are evaluated: - - >>> x = Symbol('x') - >>> (pi*x**2 + x/3).evalf() - 3.14159265358979*x**2 + 0.333333333333333*x - - -You can also use the standard Python functions ``float()``, ``complex()`` to -convert SymPy expressions to regular Python numbers: - - >>> float(pi) #doctest: +SKIP - 3.1415926535... - >>> complex(pi+E*I) #doctest: +SKIP - (3.1415926535...+2.7182818284...j) - - -If these functions are used, failure to evaluate the expression to an explicit -number (for example if the expression contains symbols) will raise an exception. - -There is essentially no upper precision limit. The following command, for -example, computes the first 100,000 digits of π/e: - - >>> N(pi/E, 100000) #doctest: +SKIP - ... - - -This shows digits 999,951 through 1,000,000 of pi: - - >>> str(N(pi, 10**6))[-50:] #doctest: +SKIP - '95678796130331164628399634646042209010610577945815' - - -High-precision calculations can be slow. It is recommended (but entirely -optional) to install gmpy (http://code.google.com/p/gmpy/), which will -significantly speed up computations such as the one above. - -Floating-point numbers ----------------------- - -Floating-point numbers in SymPy are instances of the class ``Float``. A ``Float`` -can be created with a custom precision as second argument: - - >>> Float(0.1) - 0.100000000000000 - >>> Float(0.1, 10) - 0.1000000000 - >>> Float(0.125, 30) - 0.125000000000000000000000000000 - >>> Float(0.1, 30) - 0.100000000000000005551115123126 - -As the last example shows, some Python floats are only accurate to about 15 -digits as inputs, while others (those that have a denominator that is a -power of 2, like .125 = 1/4) are exact. To create a ``Float`` from a -high-precision decimal number, it is better to pass a string, ``Rational``, -or ``evalf`` a ``Rational``: - - >>> Float('0.1', 30) - 0.100000000000000000000000000000 - >>> Float(Rational(1, 10), 30) - 0.100000000000000000000000000000 - >>> Rational(1, 10).evalf(30) - 0.100000000000000000000000000000 - - -The precision of a number determines 1) the precision to use when performing -arithmetic with the number, and 2) the number of digits to display when printing -the number. When two numbers with different precision are used together in an -arithmetic operation, the higher of the precisions is used for the result. The -product of 0.1 +/- 0.001 and 3.1415 +/- 0.0001 has an uncertainty of about 0.003 -and yet 5 digits of precision are shown. - - >>> Float(0.1, 3)*Float(3.1415, 5) - 0.31417 - -So the displayed precision should not be used as a model of error propagation or -significance arithmetic; rather, this scheme is employed to ensure stability of -numerical algorithms. - -``N`` and ``evalf`` can be used to change the precision of existing -floating-point numbers: - - >>> N(3.5) - 3.50000000000000 - >>> N(3.5, 5) - 3.5000 - >>> N(3.5, 30) - 3.50000000000000000000000000000 - - -Accuracy and error handling ---------------------------- - -When the input to ``N`` or ``evalf`` is a complicated expression, numerical -error propagation becomes a concern. As an example, consider the 100'th -Fibonacci number and the excellent (but not exact) approximation `\varphi^{100} / \sqrt{5}` -where `\varphi` is the golden ratio. With ordinary floating-point arithmetic, -subtracting these numbers from each other erroneously results in a complete -cancellation: - - >>> a, b = GoldenRatio**1000/sqrt(5), fibonacci(1000) - >>> float(a) #doctest: +SKIP - 4.34665576869...e+208 - >>> float(b) #doctest: +SKIP - 4.34665576869...e+208 - >>> float(a) - float(b) - 0.0 - -``N`` and ``evalf`` keep track of errors and automatically increase the -precision used internally in order to obtain a correct result: - - >>> N(fibonacci(100) - GoldenRatio**100/sqrt(5)) - -5.64613129282185e-22 - - -Unfortunately, numerical evaluation cannot tell an expression that is exactly -zero apart from one that is merely very small. The working precision is -therefore capped, by default to around 100 digits. If we try with the 1000'th -Fibonacci number, the following happens: - - >>> N(fibonacci(1000) - (GoldenRatio)**1000/sqrt(5)) - 0.e+85 - - -The lack of digits in the returned number indicates that ``N`` failed to achieve -full accuracy. The result indicates that the magnitude of the expression is something less than 10^84, but that is not a particularly good answer. To force a higher working precision, the ``maxn`` keyword argument can be used: - - >>> N(fibonacci(1000) - (GoldenRatio)**1000/sqrt(5), maxn=500) - -4.60123853010113e-210 - - -Normally, ``maxn`` can be set very high (thousands of digits), but be aware that -this may cause significant slowdown in extreme cases. Alternatively, the -``strict=True`` option can be set to force an exception instead of silently -returning a value with less than the requested accuracy: - - >>> N(fibonacci(1000) - (GoldenRatio)**1000/sqrt(5), strict=True) - Traceback (most recent call last): - ... - PrecisionExhausted: Failed to distinguish the expression: - - -sqrt(5)*GoldenRatio**1000/5 + 43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875 - - from zero. Try simplifying the input, using chop=True, or providing a higher maxn for evalf - - -If we add a term so that the Fibonacci approximation becomes exact (the full -form of Binet's formula), we get an expression that is exactly zero, but ``N`` -does not know this: - - >>> f = fibonacci(100) - (GoldenRatio**100 - (GoldenRatio-1)**100)/sqrt(5) - >>> N(f) - 0.e-104 - >>> N(f, maxn=1000) - 0.e-1336 - - -In situations where such cancellations are known to occur, the ``chop`` options -is useful. This basically replaces very small numbers in the real or -imaginary portions of a number with exact zeros: - - >>> N(f, chop=True) - 0 - >>> N(3 + I*f, chop=True) - 3.00000000000000 - - -In situations where you wish to remove meaningless digits, re-evaluation or -the use of the ``round`` method are useful: - - >>> Float('.1', '')*Float('.12345', '') - 0.012297 - >>> ans = _ - >>> N(ans, 1) - 0.01 - >>> ans.round(2) - 0.01 - - -If you are dealing with a numeric expression that contains no floats, it -can be evaluated to arbitrary precision. To round the result relative to -a given decimal, the round method is useful: - - >>> v = 10*pi + cos(1) - >>> N(v) - 31.9562288417661 - >>> v.round(3) - 31.956 - - -Sums and integrals ------------------- - -Sums (in particular, infinite series) and integrals can be used like regular -closed-form expressions, and support arbitrary-precision evaluation: - - >>> var('n x') - (n, x) - >>> Sum(1/n**n, (n, 1, oo)).evalf() - 1.29128599706266 - >>> Integral(x**(-x), (x, 0, 1)).evalf() - 1.29128599706266 - >>> Sum(1/n**n, (n, 1, oo)).evalf(50) - 1.2912859970626635404072825905956005414986193682745 - >>> Integral(x**(-x), (x, 0, 1)).evalf(50) - 1.2912859970626635404072825905956005414986193682745 - >>> (Integral(exp(-x**2), (x, -oo, oo)) ** 2).evalf(30) - 3.14159265358979323846264338328 - - -By default, the tanh-sinh quadrature algorithm is used to evaluate integrals. -This algorithm is very efficient and robust for smooth integrands (and even -integrals with endpoint singularities), but may struggle with integrals that -are highly oscillatory or have mid-interval discontinuities. In many cases, -``evalf``/``N`` will correctly estimate the error. With the following integral, -the result is accurate but only good to four digits: - - >>> f = abs(sin(x)) - >>> Integral(abs(sin(x)), (x, 0, 4)).evalf() - 2.346 - - -It is better to split this integral into two pieces: - - >>> (Integral(f, (x, 0, pi)) + Integral(f, (x, pi, 4))).evalf() - 2.34635637913639 - - -A similar example is the following oscillatory integral: - - - >>> Integral(sin(x)/x**2, (x, 1, oo)).evalf(maxn=20) - 0.5 - - -It can be dealt with much more efficiently by telling ``evalf`` or ``N`` to -use an oscillatory quadrature algorithm: - - >>> Integral(sin(x)/x**2, (x, 1, oo)).evalf(quad='osc') - 0.504067061906928 - >>> Integral(sin(x)/x**2, (x, 1, oo)).evalf(20, quad='osc') - 0.50406706190692837199 - - -Oscillatory quadrature requires an integrand containing a factor cos(ax+b) or -sin(ax+b). Note that many other oscillatory integrals can be transformed to -this form with a change of variables: - - >>> init_printing(use_unicode=False, wrap_line=False, no_global=True) - >>> intgrl = Integral(sin(1/x), (x, 0, 1)).transform(x, 1/x) - >>> intgrl - oo - / - | - | sin(x) - | ------ dx - | 2 - | x - | - / - 1 - >>> N(intgrl, quad='osc') - 0.504067061906928 - - -Infinite series use direct summation if the series converges quickly enough. -Otherwise, extrapolation methods (generally the Euler-Maclaurin formula but -also Richardson extrapolation) are used to speed up convergence. This allows -high-precision evaluation of slowly convergent series: - - >>> var('k') - k - >>> Sum(1/k**2, (k, 1, oo)).evalf() - 1.64493406684823 - >>> zeta(2).evalf() - 1.64493406684823 - >>> Sum(1/k-log(1+1/k), (k, 1, oo)).evalf() - 0.577215664901533 - >>> Sum(1/k-log(1+1/k), (k, 1, oo)).evalf(50) - 0.57721566490153286060651209008240243104215933593992 - >>> EulerGamma.evalf(50) - 0.57721566490153286060651209008240243104215933593992 - - -The Euler-Maclaurin formula is also used for finite series, allowing them to -be approximated quickly without evaluating all terms: - - >>> Sum(1/k, (k, 10000000, 20000000)).evalf() - 0.693147255559946 - - -Note that ``evalf`` makes some assumptions that are not always optimal. For -fine-tuned control over numerical summation, it might be worthwhile to manually -use the method ``Sum.euler_maclaurin``. - -Special optimizations are used for rational hypergeometric series (where the -term is a product of polynomials, powers, factorials, binomial coefficients and -the like). ``N``/``evalf`` sum series of this type very rapidly to high -precision. For example, this Ramanujan formula for pi can be summed to 10,000 -digits in a fraction of a second with a simple command: - - >>> f = factorial - >>> n = Symbol('n', integer=True) - >>> R = 9801/sqrt(8)/Sum(f(4*n)*(1103+26390*n)/f(n)**4/396**(4*n), - ... (n, 0, oo)) #doctest: +SKIP - >>> N(R, 10000) #doctest: +SKIP - 3.141592653589793238462643383279502884197169399375105820974944592307816406286208 - 99862803482534211706798214808651328230664709384460955058223172535940812848111745 - 02841027019385211055596446229489549303819644288109756659334461284756482337867831 - ... - - -Numerical simplification ------------------------- - -The function ``nsimplify`` attempts to find a formula that is numerically equal -to the given input. This feature can be used to guess an exact formula for an -approximate floating-point input, or to guess a simpler formula for a -complicated symbolic input. The algorithm used by ``nsimplify`` is capable of -identifying simple fractions, simple algebraic expressions, linear combinations -of given constants, and certain elementary functional transformations of any of -the preceding. - -Optionally, ``nsimplify`` can be passed a list of constants to include (e.g. pi) -and a minimum numerical tolerance. Here are some elementary examples: - - >>> nsimplify(0.1) - 1/10 - >>> nsimplify(6.28, [pi], tolerance=0.01) - 2*pi - >>> nsimplify(pi, tolerance=0.01) - 22/7 - >>> nsimplify(pi, tolerance=0.001) - 355 - --- - 113 - >>> nsimplify(0.33333, tolerance=1e-4) - 1/3 - >>> nsimplify(2.0**(1/3.), tolerance=0.001) - 635 - --- - 504 - >>> nsimplify(2.0**(1/3.), tolerance=0.001, full=True) - 3 ___ - \/ 2 - - -Here are several more advanced examples: - - >>> nsimplify(Float('0.130198866629986772369127970337',30), [pi, E]) - 1 - ---------- - 5*pi - ---- + 2*E - 7 - >>> nsimplify(cos(atan('1/3'))) - ____ - 3*\/ 10 - -------- - 10 - >>> nsimplify(4/(1+sqrt(5)), [GoldenRatio]) - -2 + 2*GoldenRatio - >>> nsimplify(2 + exp(2*atan('1/4')*I)) - 49 8*I - -- + --- - 17 17 - >>> nsimplify((1/(exp(3*pi*I/5)+1))) - ___________ - / ___ - 1 / \/ 5 1 - - - I* / ----- + - - 2 \/ 10 4 - >>> nsimplify(I**I, [pi]) - -pi - --- - 2 - e - >>> n = Symbol('n') - >>> nsimplify(Sum(1/n**2, (n, 1, oo)), [pi]) - 2 - pi - --- - 6 - >>> nsimplify(gamma('1/4')*gamma('3/4'), [pi]) - ___ - \/ 2 *pi diff --git a/dev-py3k/_sources/modules/functions/combinatorial.txt b/dev-py3k/_sources/modules/functions/combinatorial.txt deleted file mode 100644 index efa0c405708..00000000000 --- a/dev-py3k/_sources/modules/functions/combinatorial.txt +++ /dev/null @@ -1,88 +0,0 @@ -Combinatorial -============= - -This module implements various combinatorial functions. - -bell ----- - -.. autoclass:: sympy.functions.combinatorial.numbers.bell - :members: - -bernoulli ---------- - -.. autoclass:: sympy.functions.combinatorial.numbers.bernoulli - :members: - -binomial --------- - -.. autoclass:: sympy.functions.combinatorial.factorials.binomial - :members: - -catalan -------- - -.. autoclass:: sympy.functions.combinatorial.numbers.catalan - :members: - - -euler ------ - -.. autoclass:: sympy.functions.combinatorial.numbers.euler - :members: - - -factorial ---------- - -.. autoclass:: sympy.functions.combinatorial.factorials.factorial - :members: - -factorial2 / double factorial ------------------------------ - -.. autoclass:: sympy.functions.combinatorial.factorials.factorial2 - :members: - - -FallingFactorial ----------------- - -.. autoclass:: sympy.functions.combinatorial.factorials.FallingFactorial - :members: - -fibonacci ---------- - -.. autoclass:: sympy.functions.combinatorial.numbers.fibonacci - :members: - -harmonic --------- - -.. autoclass:: sympy.functions.combinatorial.numbers.harmonic - :members: - - -lucas ------ - -.. autoclass:: sympy.functions.combinatorial.numbers.lucas - :members: - - -MultiFactorial --------------- - -.. autoclass:: sympy.functions.combinatorial.factorials.MultiFactorial - :members: - - -RisingFactorial ---------------- - -.. autoclass:: sympy.functions.combinatorial.factorials.RisingFactorial - :members: diff --git a/dev-py3k/_sources/modules/functions/elementary.txt b/dev-py3k/_sources/modules/functions/elementary.txt deleted file mode 100644 index a6c2ee71a2c..00000000000 --- a/dev-py3k/_sources/modules/functions/elementary.txt +++ /dev/null @@ -1,326 +0,0 @@ -Elementary -========== - -This module implements elementary functions, as well as functions like Abs, -Max, etc. - - -Abs ---- - -Returns the absolute value of the argument. - -Examples:: - - >>> from sympy.functions import Abs - >>> Abs(-1) - 1 - -.. autoclass:: sympy.functions.elementary.complexes.Abs - :members: - -acos ----- - -.. autoclass:: sympy.functions.elementary.trigonometric.acos - :members: - -acosh ------ - -.. autoclass:: sympy.functions.elementary.hyperbolic.acosh - :members: - -acot ----- - -.. autoclass:: sympy.functions.elementary.trigonometric.acot - :members: - -acoth ------ - -.. autoclass:: sympy.functions.elementary.hyperbolic.acoth - :members: - -arg ---- - -Returns the argument (in radians) of a complex number. For a real -number, the argument is always 0. - -Examples:: - - >>> from sympy.functions import arg - >>> from sympy import I, sqrt - >>> arg(2.0) - 0 - >>> arg(I) - pi/2 - >>> arg(sqrt(2) + I*sqrt(2)) - pi/4 - -.. autoclass:: sympy.functions.elementary.complexes.arg - :members: - -asin ----- - -.. autoclass:: sympy.functions.elementary.trigonometric.asin - :members: - -asinh ------ - -.. autoclass:: sympy.functions.elementary.hyperbolic.asinh - :members: - -atan ----- - -.. autoclass:: sympy.functions.elementary.trigonometric.atan - :members: - -atan2 ------ - -This function is like `atan`, but considers the sign of both arguments in -order to correctly determine the quadrant of its result. - -.. autoclass:: sympy.functions.elementary.trigonometric.atan2 - :members: - -atanh ------ - -.. autoclass:: sympy.functions.elementary.hyperbolic.atanh - :members: - -ceiling -------- - -.. autoclass:: sympy.functions.elementary.integers.ceiling - :members: - -conjugate ---------- - -Returns the `complex conjugate `_ -of an argument. In mathematics, the complex conjugate of a complex number is given -by changing the sign of the imaginary part. Thus, the conjugate of the complex number - - :math:`a + ib` - -(where a and b are real numbers) is - - :math:`a - ib` - -Examples:: - - >>> from sympy.functions import conjugate - >>> from sympy import I - >>> conjugate(2) - 2 - >>> conjugate(I) - -I - -.. autoclass:: sympy.functions.elementary.complexes.conjugate - :members: - -cos ---- - -.. autoclass:: sympy.functions.elementary.trigonometric.cos - :members: - -cosh ----- -.. autoclass:: sympy.functions.elementary.hyperbolic.cosh - :members: - -cot ---- - -.. autoclass:: sympy.functions.elementary.trigonometric.cot - :members: - -coth ----- - -.. autoclass:: sympy.functions.elementary.hyperbolic.coth - :members: - -exp ---- - -.. autoclass:: sympy.functions.elementary.exponential.exp - :members: - -.. seealso:: classes :py:class:`sympy.functions.elementary.exponential.log` - -ExprCondPair ------------- - -.. autoclass:: sympy.functions.elementary.piecewise.ExprCondPair - :members: - -floor ------ - -.. autoclass:: sympy.functions.elementary.integers.floor - :members: - -HyperbolicFunction ------------------- - -.. autoclass:: sympy.functions.elementary.hyperbolic.HyperbolicFunction - :members: - -IdentityFunction ----------------- - -.. autoclass:: sympy.functions.elementary.miscellaneous.IdentityFunction - :members: - -im --- - -Returns the imaginary part of an expression. - -Examples:: - - - >>> from sympy.functions import im - >>> from sympy import I - >>> im(2+3*I) - 3 - -.. autoclass: sympy.functions.elementary.im - :members: - -.. seealso:: - :py:class:`sympy.functions.elementary.complexes.re` - -LambertW --------- - -.. autoclass:: sympy.functions.elementary.exponential.LambertW - :members: - -log ---- - -.. autoclass:: sympy.functions.elementary.exponential.log - :members: - -.. seealso:: classes :py:class:`sympy.functions.elementary.exponential.exp` - -Min ---- - -Returns the minimum of two (comparable) expressions. - -Examples:: - - >>> from sympy.functions import Min - >>> Min(1,2) - 1 - >>> from sympy.abc import x - >>> Min(1, x) - Min(1, x) - -It is named Min and not min to avoid conflicts with the built-in function min. - -.. autoclass:: sympy.functions.elementary.miscellaneous.Min - :members: - - -Max ---- - -Returns the maximum of two (comparable) expressions - -It is named Max and not max to avoid conflicts with the built-in function max. - -.. autoclass:: sympy.functions.elementary.miscellaneous.Max - :members: - -Piecewise ---------- - -.. autoclass:: sympy.functions.elementary.piecewise.Piecewise - :members: - -.. autofunction:: sympy.functions.elementary.piecewise.piecewise_fold - -re --- - -Return the real part of an expression. - -Examples:: - - >>> from sympy.functions import re - >>> from sympy import I - >>> re(2+3*I) - 2 - -.. autoclass:: sympy.functions.elementary.complexes.re - :members: - -.. seealso:: - :py:class:`sympy.functions.elementary.complexes.im` - -root ----- - -.. autofunction:: sympy.functions.elementary.miscellaneous.root - -RoundFunction -------------- - -.. autoclass:: sympy.functions.elementary.integers.RoundFunction - -sin ---- - -.. autoclass:: sympy.functions.elementary.trigonometric.sin - :members: - -sinh ----- - -.. autoclass:: sympy.functions.elementary.hyperbolic.sinh - :members: - -sqrt ----- - -Returns the square root of an expression. It is equivalent to raise to Rational(1,2) - - >>> from sympy.functions import sqrt - >>> from sympy import Rational - >>> sqrt(2) == 2**Rational(1,2) - True - -.. autoclass:: sympy.functions.elementary.miscellaneous.sqrt - :members: - - -sign ----- - -.. autoclass:: sympy.functions.elementary.complexes.sign - :members: - -tan ---- - -.. autoclass:: sympy.functions.elementary.trigonometric.tan - :members: - -tanh ----- - -.. autoclass:: sympy.functions.elementary.hyperbolic.tanh - :members: diff --git a/dev-py3k/_sources/modules/functions/index.txt b/dev-py3k/_sources/modules/functions/index.txt deleted file mode 100644 index 2f3c0e4791f..00000000000 --- a/dev-py3k/_sources/modules/functions/index.txt +++ /dev/null @@ -1,21 +0,0 @@ -Functions Module -**************** - -.. module:: sympy.functions - -All functions support the methods documented below, inherited from -:py:class:`sympy.core.function.Function`. - -.. autoclass:: sympy.core.function.Function - :noindex: - :members: - -Contents -======== - -.. toctree:: - :maxdepth: 2 - - elementary.rst - combinatorial.rst - special.rst diff --git a/dev-py3k/_sources/modules/functions/special.txt b/dev-py3k/_sources/modules/functions/special.txt deleted file mode 100644 index a8ce407a16b..00000000000 --- a/dev-py3k/_sources/modules/functions/special.txt +++ /dev/null @@ -1,176 +0,0 @@ -Special -======= - -DiracDelta ----------- -.. autoclass:: sympy.functions.special.delta_functions.DiracDelta - :members: - -Heaviside ---------- -.. autoclass:: sympy.functions.special.delta_functions.Heaviside - :members: - -beta ----- - -.. autofunction:: sympy.functions.special.gamma_functions.beta - -erf ---- - -.. autoclass:: sympy.functions.special.error_functions.erf - :members: - -Gamma and Related Functions ---------------------------- -.. autoclass:: sympy.functions.special.gamma_functions.gamma - :members: -.. autoclass:: sympy.functions.special.gamma_functions.loggamma - :members: -.. autoclass:: sympy.functions.special.gamma_functions.polygamma - :members: -.. autofunction:: sympy.functions.special.gamma_functions.digamma -.. autofunction:: sympy.functions.special.gamma_functions.trigamma -.. autoclass:: sympy.functions.special.gamma_functions.uppergamma - :members: -.. autoclass:: sympy.functions.special.gamma_functions.lowergamma - :members: - -Special Cases of the Incomplete Gamma Functions ------------------------------------------------ -.. module:: sympy.functions.special.error_functions - -.. autoclass:: erf -.. autoclass:: Ei -.. autoclass:: expint -.. autofunction:: E1 -.. autoclass:: Si -.. autoclass:: Ci -.. autoclass:: Shi -.. autoclass:: Chi - -.. autoclass:: sympy.functions.special.error_functions.FresnelIntegral - :members: - -.. autoclass:: fresnels -.. autoclass:: fresnelc - -Bessel Type Functions ---------------------- - -.. autoclass:: sympy.functions.special.bessel.BesselBase - :members: - -.. autoclass:: sympy.functions.special.bessel.besselj -.. autoclass:: sympy.functions.special.bessel.bessely -.. autoclass:: sympy.functions.special.bessel.besseli -.. autoclass:: sympy.functions.special.bessel.besselk -.. autoclass:: sympy.functions.special.bessel.hankel1 -.. autoclass:: sympy.functions.special.bessel.hankel2 -.. autoclass:: sympy.functions.special.bessel.jn -.. autoclass:: sympy.functions.special.bessel.yn - -.. autofunction:: sympy.functions.special.bessel.jn_zeros - -B-Splines ---------- - -.. autofunction:: sympy.functions.special.bsplines.bspline_basis -.. autofunction:: sympy.functions.special.bsplines.bspline_basis_set - -Riemann Zeta and Related Functions ----------------------------------- -.. module:: sympy.functions.special.zeta_functions - -.. autoclass:: zeta -.. autoclass:: dirichlet_eta -.. autoclass:: polylog -.. autoclass:: lerchphi - -Hypergeometric Functions ------------------------- -.. autoclass:: sympy.functions.special.hyper.hyper - :members: - -.. autoclass:: sympy.functions.special.hyper.meijerg - :members: - -Orthogonal Polynomials ----------------------- - -.. automodule:: sympy.functions.special.polynomials - -Jacobi Polynomials -++++++++++++++++++ - -.. autoclass:: sympy.functions.special.polynomials.jacobi - :members: - -Gegenbauer Polynomials -++++++++++++++++++++++ - -.. autoclass:: sympy.functions.special.polynomials.gegenbauer - :members: - -Chebyshev Polynomials -+++++++++++++++++++++ - -.. autoclass:: sympy.functions.special.polynomials.chebyshevt - :members: - -.. autoclass:: sympy.functions.special.polynomials.chebyshevu - :members: - -.. autoclass:: sympy.functions.special.polynomials.chebyshevt_root - :members: - -.. autoclass:: sympy.functions.special.polynomials.chebyshevu_root - :members: - -Legendre Polynomials -++++++++++++++++++++ - -.. autoclass:: sympy.functions.special.polynomials.legendre - :members: - -.. autoclass:: sympy.functions.special.polynomials.assoc_legendre - :members: - -Hermite Polynomials -+++++++++++++++++++ - -.. autoclass:: sympy.functions.special.polynomials.hermite - :members: - -Laguerre Polynomials -++++++++++++++++++++ - -.. autoclass:: sympy.functions.special.polynomials.laguerre - :members: -.. autoclass:: sympy.functions.special.polynomials.assoc_laguerre - :members: - -Spherical Harmonics -------------------- - -.. autofunction:: sympy.functions.special.spherical_harmonics.Plmcos - -.. autofunction:: sympy.functions.special.spherical_harmonics.Ylm - -.. autofunction:: sympy.functions.special.spherical_harmonics.Ylm_c - -.. autofunction:: sympy.functions.special.spherical_harmonics.Zlm - -Tensor Functions ----------------- - -.. autofunction:: sympy.functions.special.tensor_functions.Eijk - -.. autofunction:: sympy.functions.special.tensor_functions.eval_levicivita - -.. autoclass:: sympy.functions.special.tensor_functions.LeviCivita - :members: - -.. autoclass:: sympy.functions.special.tensor_functions.KroneckerDelta - :members: diff --git a/dev-py3k/_sources/modules/galgebra/GA.txt b/dev-py3k/_sources/modules/galgebra/GA.txt deleted file mode 100644 index 51e542b22e2..00000000000 --- a/dev-py3k/_sources/modules/galgebra/GA.txt +++ /dev/null @@ -1,6 +0,0 @@ -======== -GAlgebra -======== - -.. automodule:: sympy.galgebra.GA - :members: diff --git a/dev-py3k/_sources/modules/galgebra/GA/GAsympy.txt b/dev-py3k/_sources/modules/galgebra/GA/GAsympy.txt deleted file mode 100644 index bbc694c2651..00000000000 --- a/dev-py3k/_sources/modules/galgebra/GA/GAsympy.txt +++ /dev/null @@ -1,1729 +0,0 @@ -************************************** - Geometric Algebra Module for SymPy -************************************** - -: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 of a geometric algebra module in - python that utilizes the :mod:`sympy` symbolic algebra library. The python - module :mod:`GA` 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 numpy and the sympy modules. - - -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,Lasenby]. The elements of the geometric -algebra are called multivectors and consist of the linear combination of -scalars, vectros, 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: - -.. 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 these also apply to any multivectors. - -Several software packages for numerical geometric algebra calculations are -available from Doran-Lazenby 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, -:mod:`GA`, 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:`a_{1},a_{2},\dots,a_{n}` and a symbolic metric -tensor :math:`g_{ij} = a_{i}\cdot a_{j}`. - -In order not to reinvent the wheel all scalar symbolic algebra is handled by the -python module :mod:`sympy`. - -The basic geometic algebra operations will be implemented in python by defining -a multivector class, MV, and overloading the python operators in Table -:ref:`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`` or ``A>>B``, Right contraction of multivectors - -Table :ref:`1 `. Multivector operations for symbolicGA - -The option to use ``<`` or ``<<`` for left contraction and ``>`` or ``>>`` is given since the ``<`` and -``>`` operators do not have r-forms (there are no :func:`__rlt__` and :func:`__rgt__` functions to overload) while -``<<`` and ``>>`` do have r-forms so that ``x << A`` and ``x >> A`` are allowed where ``x`` is a scalar (symbol or integer) -and ``A`` is a multivector. With ``<`` and ``>`` we can only have mixed modes (scalars and multivectors) if the -multivector is the first operand. - -.. 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 - neccessarily 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 ``-``. - - -.. _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: - -``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 - -.. _eq1: - - - -.. math:: - :label: 1 - :nowrap: - - \begin{equation*} - g = \left[ - \begin{array}{ccc} - a0**2 & (a0.a1) & (a0.a2) \\ - (a0.a1) & a1**2 & (a1.a2) \\ - (a0.a2) & (a1.a2) & a2**2 \\ - \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:`1 ` are only used when the values -of :math:`g_{ij}` are output (printed). In the :mod:`GA` module (library) -the :math:`g_{ij}` symbols are stored in a static member list of the multivector -class :class:`MV` as the double list ``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 - -``basis = 'a0 a1 a2 n nbar'`` - -and for a metric - -``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 - - - -.. math:: - :nowrap: - - \begin{equation*} - g = \left[ - \begin{array}{ccccc} - a0**2 & (a0.a1) & (a0.a2) & 0 & 0\\ - (a0.a1) & a1**2 & (a1.a2) & 0 & 0\\ - (a0.a2) & (a1.a2) & a2**2 & 0 & 0 \\ - 0 & 0 & 0 & 0 & 2 \\ - 0 & 0 & 0 & 2 & 0 - \end{array} - \right] - \end{equation*} - - -Here we have specified that :math:`n` and :math:`nbar` are orthonal to all the -:math:`a`'s, :math:`n**2 = nbar**2 = 0`, and :math:`(n.nbar) = 2`. Using -:math:`\#` 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 made available -to the programmer for future calculations. - -In addition to the basis vectors the :math:`g_{ij}` are also made available to -the programer with the following convention. If ``a0`` and ``a1`` are basis -vectors, then their dot products are denoted by ``a0sq``, ``a2sq``, and -``a0dota1`` for use as python program varibles. If you print ``a0sq`` the -output would be ``a0**2`` and the output for ``a0dota1`` would be ``(a0.a1)`` as -shown in equation :ref:`1 `. If the default value are overridden the new -values are output by print. For examle if :math:`g_{00} = 0` then "``print -a0sq``" would output "0." - -More generally, if ``metric`` is not a string, but a list of lists or a two -dimension numpy array, it is assumed that each element of ``metric`` is symbolic -variable so that the :math:`g_{ij}` could be defined as symbolic functions as -well as variables. For example instead of letting :math:`g_{01} = (a0.a1)` we -could have :math:`g_{01} = cos(theta)` where we use a symbolic :math:`\cos` -function. - -.. note:: - - Additionally ``MV.setup`` has an option for an othogonal basis where the signature - of the metric space is defined by a string. For example if the signature of the - vector space is :math:`(1,1,1)` (Euclidian 3-space) set - - ``metric = '[1,1,1]'`` - - Likewise if the signature is that of spacetime, :math:`(1,-1,-1,-1)` then define - - ``metric = '[1,-1,-1,-1]'``. - - -Representation and Reduction of Multivector Bases -================================================= - -In our symbolic geometric algebra we assume that all multivectors of interest to -us 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 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}` - -.. note:: - - The empty list, ``[]``, represents the scalar 1. - -.. _table2: - -:: - - MV.basislabel = ['1', ['a0', 'a1', 'a2'], ['a0a1', 'a0a2', 'a1a2'], - ['a0a1a2']] - MV.basis = [[], [[0], [1], [2]], [[0, 1], [0, 2], [1, 2]], - [[0, 1, 2]]] - -Table :ref:`2 `. Multivector basis labels and internal basis representation. - -Since there are :math:`2^{n}` bases and the number of bases with equal list -lengths is the same as for the grade decomposition of a dimension :math:`n` -geometric algebra we will call the collections of bases of equal length -``psuedogrades``. - -The critical operation in setting up the geometric algebra module is reducing -the geomertric product of any two bases to a linear combination of bases so that -we can calculate a multiplication table for the bases. First we represent the -product as the concatenation of two base lists. For example ``a1a2*a0a1`` is -represented by the list ``[1,2]+[0,1] = [1,2,0,1]``. The representation of the -product is reduced via two operations, contraction and revision. The state of -the reduction is saved in two lists of equal length. The first list contains -symbolic scale factors (symbol or numeric types) for the corresponding interger -list representing the product of bases. If we wish to reduce -:math:`\left[ i_{1},\dots,i_{r}\right]` the starting point is the coefficient list -:math:`C = \left[ 1 \right]` and the bases list -:math:`B = \left[ \left[ i_{1},\dots,i_{r}\right] \right]`. We now operate on each -element of the lists as follows: - -* ``contraction``: Consider a basis list :math:`B` with element - :math:`B[j] = \left[ i_{1},\dots,i_{l},i_{l+1},\dots,i_{s}\right]` where - :math:`i_{l} = i_{l+1}`. Then the product of the :math:`l` and :math:`l+1` terms - result in a scalar and :math:`B[j]` is replaced by the new list representation - :math:`\left[ i_{1},\dots,i_{l-1},i_{l+2},\dots,i_{r}\right]` which is of psuedo - grade :math:`r-2` and :math:`C[j]` is replaced by the symbol - :math:`g_{i_{l}i_{l}}C[j]`. - -* ``revision``: Consider a basis list :math:`B` with element - :math:`B[j] = \left[ i_{1},\dots,i_{l},i_{l+1},\dots,i_{s}\right]` where - :math:`i_{l} > i_{l+1}`. Then the :math:`l` and :math:`l+1` elements must be - reversed to be put in normal order, but we have :math:`a_{i_{l}}a_{i_{l+1}} = 2g_{i_{l}i_{l+1}}-a_{i_{l+1}}a_{i_{l}}` - (From the geometric algebra definition of the dot product of two vectors). Thus - we append the list representing the reduced element, - :math:`\left[ i_{1},\dots,i_{l-1},i_{l+2},\dots,i_{s}\right]`, to the pseudo bases - list, :math:`B`, and append :math:`2g_{i{l}i_{l+1}}C[j]` to the coefficients - list, then we replace :math:`B[j]` with - :math:`\left[ i_{1},\dots,i_{l+1},i_{l},\dots,i_{s}\right]` and :math:`C[j]` with - :math:`-C[j]`. Both lists are increased by one element if - :math:`g_{i_{l}i_{l+1}} \ne 0`. - -These processes are repeated untill every basis list in :math:`B` is in normal -(ascending) order with no repeated elements. Then the coefficents of equivalent -bases are summed and the bases sorted according to psuedograde and ascending -order. We now have a way of calculating the geometric product of any two bases -as a symbolic linear combination of all the bases with the coefficients -determined by :math:`g`. The base multiplication table for our simple example of -three vectors is given by (the coefficient of each psuedo base is enclosed with -{} for clarity): - - -.. literalinclude:: multable.dat - - -Base Representation of Multivectors -=================================== - -In terms of the bases defined an arbitrary multivector can be represented as a -list of arrays (we use the numpy python module to implement arrays). If we -have :math:`n` basis vectors we initialize the list ``self.mv = [0,0,...,0]`` -with :math:`n+1` integers all zero. Each zero is a placeholder for an array of -python objects (in this case the objects will be sympy symbol objects). If -``self.mv[r] = numpy.array([list of symbol objects])`` each entry in the -``numpy.array`` will be a coefficient of the corresponding psuedo base. -``self.mv[r] = 0`` indicates that the coefficients of every base of psuedo grade -:math:`r` are 0. The length of the array ``self.mv[r]`` is :math:`n \choose r` -the binomial coefficient. For example the psuedo basis vector ``a1`` would be -represented as a multivector by the list: - -``a1.mv = [0,numpy.array([numeric(0),numeric(1),numeric(0)]),0,0]`` - -and ``a0a1a2`` by: - -``a0a1a2.mv = [0,0,0,numpy.array([numeric(1)])]`` - -The array is stuffed with sympy numeric objects instead of python integers so -that we can perform symbolically manipulate sympy expressions that consist of -scalar algebraic symbols and exact rational numbers which sympy can also -represent. - -The ``numpy.array`` is used because operations of addition, substraction, and -multiplication by an object are defined for the array if they are defined for -the objects making up the array, which they are by sympy. We call this -representation a base type because the ``r`` index is not a grade index since -the bases we are using are not blades. In a blade representation the structure -would be identical, but the bases would be replaced by blades and -``self.mv[r]`` would represent the ``r`` grade components of the multivector. -The first use of the base representation is to store the results of the -multiplication tabel for the bases in the class variable ``MV.mtabel``. This -variable is a group of nested lists so that the geometric product of the -``igrade`` and ``ibase`` with the ``jgrade`` and ``jbase`` is -``MV.mtabel[igrade][ibase][jgrade][jbase]``. We can then use this table to -calculate the geometric product of any two multivectors. - - -Blade Representation of Multivectors -==================================== - -Since we can now calculate the symbolic geometric product of any two -multivectors we can also calculate the blades corresponding to the product of -the symbolic basis vectors using the formula - -.. math:: - :nowrap: - - \begin{equation*} - A_{r}\wedge b = \frac{1}{2}\left( A_{r}b-\left( -1 \right)^{r}bA_{r} \right), - \end{equation*} - - -where :math:`A_{r}` is a multivector of grade :math:`r` and :math:`b` is a -vector. For our example basis the result is shown in Table :ref:`3 `. - -.. _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:`3 `. Bases blades in terms of bases. - -The important thing to notice about Table :ref:`3 ` is that it is a -triagonal (lower triangular) system of equations so that using a simple back -substitution algorithym we can solve for the psuedo bases in terms of the blades -giving Table :ref:`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:`4 `. Bases in terms of basis blades. - -Using Table :ref:`4 ` and simple substitution we can convert from a base -multivector representation to a blade representation. Likewise, using Table -:ref:`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 zero for a base representation -and 1 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\langle A_{r}B_{s} \right\rangle_{\left| r-s \right|} + \left\langle A_{r}B_{s} \right\rangle_{\left| r-s \right|+2} + \cdots + \left\langle A_{r}B_{s} \right\rangle_{r+s} - \end{equation*} - - -where :math:`\left\langle \right\rangle_{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\langle A_{r}B_{s} \right\rangle_{\left| r-s \right|} - \end{equation*} - - - - -.. math:: - :nowrap: - - \begin{equation*} - A_{r}\wedge B_{s} = \left\langle A_{r}B_{s} \right\rangle_{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\langle A_{r}B_{s} \right\rangle_{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\langle A_{r}B_{s} \right\rangle_{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*} - - -The ``MV`` class function for the outer product of the multivectors ``mv1`` and -``mv2`` is :: - - @staticmethod - def outer_product(mv1,mv2): - product = MV() - product.bladeflg = 1 - mv1.convert_to_blades() - mv2.convert_to_blades() - for igrade1 in MV.n1rg: - if not isint(mv1.mv[igrade1]): - pg1 = mv1.project(igrade1) - for igrade2 in MV.n1rg: - igrade = igrade1+igrade2 - if igrade <= MV.n: - if not isint(mv2.mv[igrade2]): - pg2 = mv2.project(igrade2) - pg1pg2 = pg1*pg2 - product.add_in_place(pg1pg2.project(igrade)) - return(product) - - -The steps for calculating the outer product are: - -#. Convert ``mv1`` and ``mv2`` to blade representation if they are not already - in that form. - -#. Project and loop through each grade ``mv1.mv[i1]`` and ``mv2.mv[i2]``. - -#. Calculate the geometric product ``pg1*pg2``. - -#. Project the ``i1+i2`` grade from ``pg1*pg2``. - -#. Accumulate the results for each pair of grades in the input multivectors. - -.. 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. - -For the inner product of the multivectors ``mv1`` and ``mv2`` the ``MV`` class -function is :: - - @staticmethod - def inner_product(mv1,mv2,mode='s'): - """ - MV.inner_product(mv1,mv2) calculates the inner - - mode = 's' - symmetic (Doran & Lasenby) - mode = 'l' - left contraction (Dorst) - mode = 'r' - right contraction (Dorst) - """ - if isinstance(mv1,MV) and isinstance(mv2,MV): - product = MV() - product.bladeflg = 1 - mv1.convert_to_blades() - mv2.convert_to_blades() - for igrade1 in range(MV.n1): - if isinstance(mv1.mv[igrade1],numpy.ndarray): - pg1 = mv1.project(igrade1) - for igrade2 in range(MV.n1): - igrade = igrade1-igrade2 - if mode == 's': - igrade = igrade.__abs__() - else: - if mode == 'l': - igrade = -igrade - if igrade >= 0: - if isinstance(mv2.mv[igrade2],numpy.ndarray): - pg2 = mv2.project(igrade2) - pg1pg2 = pg1*pg2 - product.add_in_place(pg1pg2.project(igrade)) - return(product) - else: - if mode == 's': - if isinstance(mv1,MV): - product = mv1.scalar_mul(mv2) - if isinstance(mv2,MV): - product = mv2.scalar_mul(mv1) - else: - product = None - return(product) - - -The inner product is calculated the same way as the outer product except that in -step 4, ``i1+i2`` is replaced by ``abs(i1-i2)`` or ``i1-i2`` for the right contraction or -``i2-i1`` for the left contraction. If ``i1-i2`` is less than zero there is no contribution -to the right contraction. If ``i2-i1`` is less than zero there is no contribution to the -left contraction. - -.. 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` 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. Additionaly 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{\gamma}_{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{\gamma}^{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{\gamma}_{i_{1}}\wedge\dots\wedge \boldsymbol{\gamma}_{i_{R}}` -then - -.. math:: - :nowrap: - - \begin{equation*} - \nabla F_{R} = \displaystyle\frac{\partial F_{R}^{i_{1}\dots i_{R}}}{\partial x^{j}}\boldsymbol{\gamma}^{j}\left(\boldsymbol{\gamma}_{i_{1}}\wedge - \dots\wedge \boldsymbol{\gamma}_{i_{R}} \right) - \end{equation*} - - -Note that -:math:`\boldsymbol{\gamma}^{j}\left(\boldsymbol{\gamma}_{i_{1}}\wedge\dots\wedge \boldsymbol{\gamma}_{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{{\gamma}_{x}}}+ r \sin\left({\phi}\right) \sin\left({\theta}\right){\boldsymbol{{\gamma}_{y}}}+ r \cos\left({\theta}\right){\boldsymbol{{\gamma}_{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{{\gamma}_{x}}}+\sin\left({\phi}\right) \sin\left({\theta}\right){\boldsymbol{{\gamma}_{y}}}+\cos\left({\theta}\right){\boldsymbol{{\gamma}_{z}}} - \end{equation*} - - - - -.. math:: - :nowrap: - - \begin{equation*} - \boldsymbol{e}_{{\theta}} = \cos\left({\phi}\right) \cos\left({\theta}\right){\boldsymbol{{\gamma}_{x}}}+r \cos\left({\theta}\right) \sin\left({\phi}\right){\boldsymbol{{\gamma}_{y}}} - r \sin\left({\theta}\right){\boldsymbol{{\gamma}_{z}}} - \end{equation*} - - - - -.. math:: - :nowrap: - - \begin{equation*} - \boldsymbol{e}_{{\phi}} = - r \sin\left({\phi}\right) \sin\left({\theta}\right){\boldsymbol{{\gamma}_{x}}}+r \cos\left({\phi}\right) \sin\left({\theta}\right){\boldsymbol{{\gamma}_{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{{\gamma}_{x}}}+\sin\left({\phi}\right) \sin\left({\theta}\right){\boldsymbol{{\gamma}_{y}}}+\cos\left({\theta}\right){\boldsymbol{{\gamma}_{z}}} - \end{equation*} - - - - -.. math:: - :nowrap: - - \begin{equation*} - \boldsymbol{\hat{e}}_{{\theta}} = \cos\left({\phi}\right) \cos\left({\theta}\right){\boldsymbol{{\gamma}_{x}}}+\cos\left({\theta}\right) \sin\left({\phi}\right){\boldsymbol{{\gamma}_{y}}}- \sin\left({\theta}\right){\boldsymbol{{\gamma}_{z}}} - \end{equation*} - - - - -.. math:: - :nowrap: - - \begin{equation*} - \boldsymbol{\hat{e}}_{{\phi}} = - \sin\left({\phi}\right){\boldsymbol{{\gamma}_{x}}}+\cos\left({\phi}\right){\boldsymbol{{\gamma}_{y}}} - \end{equation*} - - -The geometric derivative in curvilinear coordinates is given by - -.. math:: - :nowrap: - - \begin{align*} - \nabla F_{R} & = \boldsymbol{\gamma}^{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{\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*} - - - -Module Components -================= - - -Initializing Multivector Class ------------------------------- - -The multivector class is initialized with: - - -.. function:: MV.setup(basis,metric='',rframe=False,coords=None,debug=False,offset=0) - - 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 list 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``). ``offset`` is an integer that is added to the multivector - coefficient index. For example if one wishes to start labeling vector coefficient - indexes at one instead of zero then set ``offset=1``. Additionally, :func:`MV.setup` - calculates the pseudo scalar, :math:`I` and its inverse, :math:`I^{-1}` and makes - them available to the programmer as ``MV.I`` and ``MV.Iinv``. - -After :func:`MV.setup` is run one can reinialize the :class:`MV` class with -curvilinear coordinates using: - - -.. function:: MV.rebase(x,coords,base_name,debug=False,debug_level=0) - - A typical usage of ``MV.rebase`` for generating spherical curvilinear coordinate - is:: - - metric = '1 0 0,0 1 0,0 0 1' - gamma_x,gamma_y,gamma_z = MV.setup('gamma_x gamma_y gamma_z',metric,True) - coords = r,theta,phi = sympy.symbols('r theta phi') - x = r*(sympy.cos(theta)*gamma_z+sympy.sin(theta)*\ - (sympy.cos(phi)*gamma_x+sympy.sin(phi)*gamma_y)) - x.set_name('x') - MV.rebase(x,coords,'e',True) - - The input parameters for ``MV.rebase`` are - -* ``x``: Vector function of coordinates (derivatives define curvilinear basis) - -* ``coords``: List of sympy symbols for curvilinear coordinates - -* ``debug``: If ``True`` printout (LaTeX) all quantities required for derivative calculation - -* ``debug_level``: Set to 0,1,2, or 3 to stop curvilinear calculation before all quatities are - calculated. This is done when debugging new curvilinear coordinate systems since simplification - of expressions is not sufficiently automated to insure success of process of any coordinate system - defined by vector function ``x`` - - - - To date ``MV.rebase`` works for cylindrical and spherical coordinate systems in - any number of dimensions (until the execution time becomes too long). To make - it work for these systems required creating some hacks for expression - simplification since both trigsimp and simplify were not general enough to - perform the required simplification. - - -.. function:: MV.set_str_format(str_mode=0) - - If ``str_mode=0`` the string representation of the multivector contains no - newline characters (prints on one line). If ``str_mode=1`` the string - representation of the multivector places a newline after each grade of the - multivector (prints one grade per line). If ``str_mode=2`` the string - representation of the multivector places a newline after each base of the - multivector (prints one base per line). In both cases bases with zero - coefficients are not printed. - - .. note:: - - This function directly affects the way multivectors are printed with the - print command since it interacts with the :func:`__str__` function for the - multivector class which is used by the ``print`` command. - - - .. csv-table:: - :header: ``str_mode``, Effect on print - :widths: 10, 50 - - 0, One multivector per line - 1, One grade per line - 2, One base per line - -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 ``e1,e2,e3 = MV.setup('e1 e2 e3')``. - -So that multivectors -could be instantiated with statements such as (``a1``, ``a2``, and ``a3`` are -``sympy`` symbols):: - - x = a1*e1+a2*e2+a3*e3 - y = x*e1*e2 - z = x|y - w = x^y - -or with the multivector class constructor: - - -.. class:: MV(value='',mvtype='',mvname='',fct=False) - - ``mvname`` is a string that defines the name of the multivector for output - purposes. ``value`` and ``type`` 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: - - .. csv-table:: - :delim: ; - :header: ``mvtype``, ``value``, Result - :widths: 10, 30, 30 - - default; default; Zero multivector - 'basisvector'; int i; :math:`\mbox{i}^{th}` basis vector - 'basisbivector'; int i; :math:`\mbox{i}^{th}` basis bivector - 'scalar'; symbol x; Symbolic scalar of value x - ; string s; Symbolic scalar of value Symbol(s) - ; int i; SymPy integer of value i - 'grade'; [int r, 1-D symbol array A]; X.grade(r) = A - ; [int r, string s]; Symbolic grade r multivector - 'vector'; 1-D symbol array A; X.grade(1) = A - ; string s; Symbolic vector - 'grade2'; 1-D symbol array A; X.grade(2) = A - 'pseudo'; symbol x; X.grade(n) = x - 'spinor'; string s; Symbolic even multivector - - - default; string s; Symbolic general multivector - - - If the ``value`` argument has the option of being a string s then a general symbolic - multivector will be constructed constrained by the value of ``mvtype``. The string - s will be the base name of the multivector symbolic coefficients. If ``coords`` is - not defined in :func:`MV.setup` the indices of the multivector bases are appended to - the base name with a double underscore (superscript notation). If ``coords`` is defined - the coordinate names will replace the indices in the coefficient names. For example if - the base string is *A* and the coordinates *(x,y,z)* then the - coefficients of a spinor in 3d space would be *A*, *A__xy*, *A__xz*, and *A__yz*. If - the :mod:`latex_ex` is used to print the multivector the coefficients would print as - :math:`A`, :math:`A^{xy}`, :math:`A^{xz}`, and :math:`A^{yz}`. - - If the ``fct`` argrument 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:: __call__(self,igrade=0,ibase=0) - - ``__call__`` returns the the ``igrade``, ``ibase`` coefficient of the - multivector. The defaults return the scalar component of the multivector. - - -.. function:: 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:: 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:: project(self,r) - - If r is a integer return the grade-:math:`r` components of the multivector. If - r is a multivector return the grades of the multivector that correspond to the - non-zero grades of r. For example if one is projecting a general multivector and - r is a spinor, ``A.project(r)`` will return only the even grades of the multivector - A since a spinor only has even grades that are non-zero. - - -.. function:: even(self) - - Return the even grade components of the multivector. - - -.. function:: odd(self) - - Return the odd grade components of the multivector. - - -.. function:: rev(self) - - Return the reverse of the multivector. See section :ref:`reverse`. - - -.. function:: is_pure(self) - - Return true if multivector is pure grade (all grades but one are zero). - - -.. function:: diff(self,x) - - Return the partial derivative of the multivector function with respect to - variable :math:`x`. - - -.. function:: grad(self) - - Return the geometric derivative of the multivector function. - - -.. function:: grad_ext(self) - - Return the outer (curl) derivative of the multivector function. Equivalent to - :func:`curl`. - - -.. function:: grad_int(self) - - Return the inner (div) derivative of the multivector function. Equivalent to - :func:`div`. - -.. warning:: - - If *A* is a vector field in three dimensions :math:`\nabla\cdot {\bf A}` = A.grad_int() = A.div(), but - :math:`\nabla\times {\bf A}` = -MV.I*A.grad_ext() = -MV.I*A.curl(). Note that grad_int() lowers the grade - of all blades by one grade and grad_ext() raises the grade of all blades by one. - -.. function:: set_coef(self,grade,base,value) - - Set the multivector coefficient of index ``(grade,base)`` to ``value``. - - -SymPy Functions Applied Inplace to Multivector Coefficients ------------------------------------------------------------ - -All the following fuctions belong to the :class:`MV` class and apply the -corresponding :mod:`sympy` function to each component of a multivector. All -these functions perform the operations inplace (``None`` is returned) on each -coefficient. For example if you wished to simplify all the components of the -multivector ``A`` you would invoke ``A.simplify()``. The argument list for each -function is the same as for the corresponding :mod:`sympy` function. The only -function that differs in its action from the :mod:`sympy` version is -:func:`trigsimp` which is applied using the recursive option. - - -.. function:: collect(self,lst) - - -.. function:: sqrfree(self,lst) - - -.. function:: subs(self,*args) - - -.. function:: simplify(self) - - -.. function:: trigsimp(self) - - -.. function:: cancel(self) - - -.. function:: expand(self) - - -Helper Functions ----------------- - -These are functions in :mod:`GA`, but not in the multivector (:class:`MV`) -class. - - -.. function:: set_names(var_lst,var_str) - - :func:`set_names` allows one to name a list, ``var_lst``, of multivectors enmass. - The names are in ``var_str``, a blank separated string of names. An error is - generated if the number of name is not equal to the length of ``var_lst``. - - -.. function:: reciprocal_frame(vlst,names='') - - :func:`reciprocal_frame` implements the proceedure described in section - :ref:`recframe`. ``vlst`` is a list of independent vectors that you wish the - reciprocal frame calculated for. ``names`` is a blank separated string of names - for the reciprocal vectors if names are required by you application. The - function returns a list containing the reciprocal vectors. - - -.. function:: trigsimp(f) - - In general :func:`sympy.trigsimp` will not catch all the trigonometric - simplifications in an :mod:`sympy` expression, but by using the recursive - option, more of them are caught. So :func:`trigsimp` uses this option by - default. - -.. function:: S(x) - - :func:`S` instanciates a scaler multivector of value ``x``, where ``x`` can be a - :mod:`sympy` variable or integer. This is just a shorthand method for - constructing scalar multivectors and can be used when there is any ambiguity - in a multivector expression as to whether a symbol or constant should be - treated as a scalar multivector or not. - -Examples -======== - - -Algebra -------- - -The following examples of geometric algebra (not calculus) are all in the file -:program:`testsymbolicGA.py` which is included in the sympy distribution -examples under the galbebra directory. The section of code in the program for -each example is show with the respective output following the code section. - - -Example Header -^^^^^^^^^^^^^^ - -This is the header of :program:`testsymbolicGA.py` that allows access to the -required modules and also allow variables and certain multivectors to be -broadcast from the :mod:`GA` module to the main program. - - -.. literalinclude:: headerGAtest.py - - -Basic Geometric Algebra Operations -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Example of basic geometric algebra operation of geometric, outer, and inner -products. - - -.. literalinclude:: BasicGAtest.py - - -Examples of Conformal Geometry -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Examples of comformal geometry [Lasenby,Chapter 10]. The examples show that -basic geometric entities (lines, circles, planes, and spheres) in three -dimensions can be represented by blades in a five dimensional (conformal) space. - - -.. literalinclude:: conformalgeometryGAtest.py - - -Calculation of Reciprocal Frame -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Example shows the calculation of the reciprocal frame for three arbitrary -vectors and verifies that the calculated reciprocal vectors have the correct -properties. - - -.. literalinclude:: reciprocalframeGAtest.py - - -Hyperbolic Geometry -^^^^^^^^^^^^^^^^^^^ - -Examples of calculation of distance in hyperbolic geometry [Lasenby,pp373-375]. -This is a good example of the utility of not restricting the basis vector to be -orthogonal. Note that most of the calculation is simplifying a scalar -expression. - - -.. literalinclude:: hyperbolicGAtest.py - - -Calculus --------- - -The calculus examples all use the extened LaTeXoutput module, ``latex_ex``, for -clarity. - - -Maxwell's Equations -^^^^^^^^^^^^^^^^^^^ - -In geometric calculus the equivalent of the electromagnetic tensor is -:math:`F = E\gamma_{0}+IB\gamma_{0}` where a spacetime vector is given by -:math:`x = x^{0}\gamma_{0}+x^{1}\gamma_{1}+x^{2}\gamma_{2}+x^{3}\gamma_{3}` -where :math:`x^{0} = ct`, the pseudoscalar -:math:`I = \gamma_{0}\gamma_{1}\gamma_{2}\gamma_{3}`, :math:`E` and :math:`B` -are four vectors where the time component is zero and the spatial components -equal to the electric and magnetic field components. Then Maxwell's equations -can be all written as :math:`\nabla F = J` with :math:`J` the four current. -This example shows that this equations generates all of Maxwell's equations -correctly (in our units:math:`c=1`) [Lasenby,pp229-231]. - -``Begin Program Maxwell.py`` - - -.. literalinclude:: Maxwell.py - -``End Program Maxwell.py`` - -``Begin Program Output`` - -:math:`I` Pseudo-Scalar - -.. math:: - :nowrap: - - \begin{equation*} - I = {\gamma}_{t}{\gamma}_{x}{\gamma}_{y}{\gamma}_{z} - \end{equation*} - - -:math:`B` Magnetic Field Bi-Vector - -.. math:: - :nowrap: - - \begin{equation*} - B = - {B^{x}}{\gamma}_{t}{\gamma}_{x}- {B^{y}}{\gamma}_{t}{\gamma}_{y}- {B^{z}}{\gamma}_{t}{\gamma}_{z} - \end{equation*} - - -:math:`F` Electric Field Bi-Vector - -.. math:: - :nowrap: - - \begin{equation*} - E = - {E^{x}}{\gamma}_{t}{\gamma}_{x}- {E^{y}}{\gamma}_{t}{\gamma}_{y}- {E^{z}}{\gamma}_{t}{\gamma}_{z} - \end{equation*} - - -:math:`E+IB` Electo-Magnetic Field Bi-Vector - -.. math:: - :nowrap: - - \begin{equation*} - F = - {E^{x}}{\gamma}_{t}{\gamma}_{x}- {E^{y}}{\gamma}_{t}{\gamma}_{y}- {B^{z}}{\gamma}_{x}{\gamma}_{y}- {E^{z}}{\gamma}_{t}{\gamma}_{z}+ {B^{y}}{\gamma}_{x}{\gamma}_{z}- {B^{x}}{\gamma}_{y}{\gamma}_{z} - \end{equation*} - - -:math:`J` Four Current - -.. math:: - :nowrap: - - \begin{equation*} - J = {J^{t}}{\gamma}_{t}+ {J^{x}}{\gamma}_{x}+ {J^{y}}{\gamma}_{y}+ {J^{z}}{\gamma}_{z} - \end{equation*} - - -Geometric Derivative of Electo-Magnetic Field Bi-Vector - -.. math:: - :nowrap: - - \begin{align*} - \nabla F & = \left( \partial_{z} {E^{z}} + \partial_{y} {E^{y}} + \partial_{x} {E^{x}}\right){\gamma}_{t} \\ - & + \left(-\partial_{t} {E^{x}} + \partial_{y} {B^{z}} - \partial_{z} {B^{y}}\right){\gamma}_{x} \\ - & + \left( \partial_{z} {B^{x}} - \partial_{t} {E^{y}} - \partial_{x} {B^{z}}\right){\gamma}_{y} \\ - & + \left(-\partial_{y} {B^{x}} - \partial_{t} {E^{z}} + \partial_{x} {B^{y}}\right){\gamma}_{z} \\ - & + \left(-\partial_{x} {E^{y}} - \partial_{t} {B^{z}} + \partial_{y} {E^{x}}\right){\gamma}_{t}{\gamma}_{x}{\gamma}_{y} \\ - & + \left(-\partial_{x} {E^{z}} + \partial_{t} {B^{y}} + \partial_{z} {E^{x}}\right){\gamma}_{t}{\gamma}_{x}{\gamma}_{z} \\ - & + \left(-\partial_{t} {B^{x}} - \partial_{y} {E^{z}} + \partial_{z} {E^{y}}\right){\gamma}_{t}{\gamma}_{y}{\gamma}_{z} \\ - & + \left( \partial_{y} {B^{y}} + \partial_{z} {B^{z}} + \partial_{x} {B^{x}}\right){\gamma}_{x}{\gamma}_{y}{\gamma}_{z} - \end{align*} - - -All Maxwell Equations are - -.. math:: - :nowrap: - - \begin{equation*} - \nabla F = J - \end{equation*} - - -Div :math:`E` and Curl :math:`H` Equations - -.. math:: - :nowrap: - - \begin{align*} - <\nabla F>_1 -J & = \left(-{J^{t}} + \partial_{z} {E^{z}} + \partial_{y} {E^{y}} + \partial_{x} {E^{x}}\right){\gamma}_{t} \\ - & + \left(-{J^{x}} - \partial_{t} {E^{x}} + \partial_{y} {B^{z}} - \partial_{z} {B^{y}}\right){\gamma}_{x} \\ - & + \left( \partial_{z} {B^{x}} - \partial_{t} {E^{y}} -{J^{y}} - \partial_{x} {B^{z}}\right){\gamma}_{y} \\ - & + \left(-\partial_{y} {B^{x}} - \partial_{t} {E^{z}} -{J^{z}} + \partial_{x} {B^{y}}\right){\gamma}_{z} - \end{align*} - - - - -.. math:: - :nowrap: - - \begin{equation*} - = 0 - \end{equation*} - - -Curl :math:`E` and Div :math:`B` equations - -.. math:: - :nowrap: - - \begin{align*} - <\nabla F>_3 & = \left(-\partial_{x} {E^{y}} - \partial_{t} {B^{z}} + \partial_{y} {E^{x}}\right){\gamma}_{t}\wedge {\gamma}_{x}\wedge {\gamma}_{y} \\ - & + \left(-\partial_{x} {E^{z}} + \partial_{t} {B^{y}} + \partial_{z} {E^{x}}\right){\gamma}_{t}\wedge {\gamma}_{x}\wedge {\gamma}_{z} \\ - & + \left(-\partial_{t} {B^{x}} - \partial_{y} {E^{z}} + \partial_{z} {E^{y}}\right){\gamma}_{t}\wedge {\gamma}_{y}\wedge {\gamma}_{z} \\ - & + \left( \partial_{y} {B^{y}} + \partial_{z} {B^{z}} + \partial_{x} {B^{x}}\right){\gamma}_{x}\wedge {\gamma}_{y}\wedge {\gamma}_{z} - \end{align*} - - - - -.. math:: - :nowrap: - - \begin{equation*} - = 0 - \end{equation*} - - -``End Program Output`` - - -Dirac's Equation -^^^^^^^^^^^^^^^^ - -The geometric algebra/calculus allows one to formulate the Dirac equation in -real terms (no :math:`\sqrt{-1}`). Spinors :math:`\left(\Psi\right)` are even -multivectors in space time (Minkowski space with signature (1,-1,-1,-1)) and the -Dirac equation becomes -:math:`\nabla \Psi I \sigma_{z}-eA\Psi = m\Psi\gamma_{t}`. All the terms in the -real equation are explined in Doran and Lasenby [Lasenby,pp281-283]. - -``Begin Program Dirac.py`` - - -.. literalinclude:: Dirac.py - -``End Program Dirac.py`` - -``Begin Program Output`` - -:math:`A` is 4-vector potential - -.. math:: - :nowrap: - - \begin{equation*} - A = {A^{t}}{\gamma}_{t}+ {A^{x}}{\gamma}_{x}+ {A^{y}}{\gamma}_{y}+ {A^{z}}{\gamma}_{z} - \end{equation*} - - -:math:`\boldsymbol{\psi}` is 8-component real spinor (even multi-vector) - -.. math:: - :nowrap: - - \begin{equation*} - {}\boldsymbol{{\psi}} = {{\psi}}+ {{\psi}^{tx}}{\gamma}_{t}{\gamma}_{x}+ {{\psi}^{ty}}{\gamma}_{t}{\gamma}_{y}+ {{\psi}^{xy}}{\gamma}_{x}{\gamma}_{y}+ {{\psi}^{tz}}{\gamma}_{t}{\gamma}_{z}+ {{\psi}^{xz}}{\gamma}_{x}{\gamma}_{z}+ {{\psi}^{yz}}{\gamma}_{y}{\gamma}_{z}+ {{\psi}^{txyz}}{\gamma}_{t}{\gamma}_{x}{\gamma}_{y}{\gamma}_{z} - \end{equation*} - - -Dirac equation in terms of real geometric algebra/calculus -:math:`\left(\nabla \boldsymbol{\psi} I \sigma_{z}-eA\boldsymbol{\psi} = m\boldsymbol{\psi}\gamma_{t}\right)` -Spin measured with respect to :math:`z` axis - -.. math:: - :nowrap: - - \begin{align*} - \nabla \boldsymbol{\psi} I \sigma_{z}-eA\boldsymbol{\psi}-m\boldsymbol{\psi}\gamma_{t} - & = \left(-e {A^{y}} {{\psi}^{ty}} - m {{\psi}} - \partial_{z} {{\psi}^{txyz}} - \partial_{y} {{\psi}^{tx}} - e {A^{z}} {{\psi}^{tz}} - e {A^{x}} {{\psi}^{tx}} + \partial_{x} {{\psi}^{ty}} - e {A^{t}} {{\psi}} + \partial_{t} {{\psi}^{xy}}\right){\gamma}_{t} \\ - & + \left(-e {A^{y}} {{\psi}^{xy}} + \partial_{z} {{\psi}^{yz}} - e {A^{z}} {{\psi}^{xz}} - e {A^{t}} {{\psi}^{tx}} + m {{\psi}^{tx}} - \partial_{x} {{\psi}^{xy}} + \partial_{y} {{\psi}} - e {A^{x}} {{\psi}} - \partial_{t} {{\psi}^{ty}}\right){\gamma}_{x} \\ - & + \left(-e {A^{y}} {{\psi}} + e {A^{x}} {{\psi}^{xy}} - e {A^{t}} {{\psi}^{ty}} - \partial_{x} {{\psi}} + \partial_{t} {{\psi}^{tx}} - \partial_{z} {{\psi}^{xz}} - e {A^{z}} {{\psi}^{yz}} + m {{\psi}^{ty}} - \partial_{y} {{\psi}^{xy}}\right){\gamma}_{y} \\ - & + \left(-\partial_{z} {{\psi}^{xy}} + e {A^{x}} {{\psi}^{xz}} - e {A^{t}} {{\psi}^{tz}} - \partial_{x} {{\psi}^{yz}} + \partial_{y} {{\psi}^{xz}} + e {A^{y}} {{\psi}^{yz}} + \partial_{t} {{\psi}^{txyz}} + m {{\psi}^{tz}} - e {A^{z}} {{\psi}}\right){\gamma}_{z} \\ - & + \left( \partial_{y} {{\psi}^{ty}} - e {A^{y}} {{\psi}^{tx}} + e {A^{x}} {{\psi}^{ty}} + \partial_{z} {{\psi}^{tz}} - e {A^{z}} {{\psi}^{txyz}} + \partial_{x} {{\psi}^{tx}} - e {A^{t}} {{\psi}^{xy}} - \partial_{t} {{\psi}} - m {{\psi}^{xy}}\right){\gamma}_{t}{\gamma}_{x}{\gamma}_{y} \\ - & + \left(-\partial_{y} {{\psi}^{tz}} + \partial_{x} {{\psi}^{txyz}} - e {A^{t}} {{\psi}^{xz}} + e {A^{x}} {{\psi}^{tz}} + e {A^{y}} {{\psi}^{txyz}} + \partial_{z} {{\psi}^{ty}} - m {{\psi}^{xz}} - e {A^{z}} {{\psi}^{tx}} - \partial_{t} {{\psi}^{yz}}\right){\gamma}_{t}{\gamma}_{x}{\gamma}_{z} \\ - & + \left(-e {A^{t}} {{\psi}^{yz}} - \partial_{z} {{\psi}^{tx}} + \partial_{x} {{\psi}^{tz}} - m {{\psi}^{yz}} + \partial_{y} {{\psi}^{txyz}} + \partial_{t} {{\psi}^{xz}} - e {A^{z}} {{\psi}^{ty}} - e {A^{x}} {{\psi}^{txyz}} + e {A^{y}} {{\psi}^{tz}}\right){\gamma}_{t}{\gamma}_{y}{\gamma}_{z} \\ - & + \left(\partial_{z} {{\psi}} + e {A^{y}} {{\psi}^{xz}} + m {{\psi}^{txyz}} -\partial_{t} {{\psi}^{tz}} -\partial_{x} {{\psi}^{xz}} -e {A^{x}} {{\psi}^{yz}} -e {A^{t}} {{\psi}^{txyz}} -\partial_{y} {{\psi}^{yz}} -e {A^{z}} {{\psi}^{xy}}\right){\gamma}_{x}{\gamma}_{y}{\gamma}_{z} - \end{align*} - - - - -.. math:: - :nowrap: - - \begin{equation*} - = 0 - \end{equation*} - - -``End Program Output`` - - -Spherical Coordinates -^^^^^^^^^^^^^^^^^^^^^ - -Curvilinear coodinates are implemented as shown in section :ref:`deriv`. The -gradient of a scalar function and the divergence and curl of a vector function -(:math:`-I\left(\nabla\wedge A\right)` is the curl in three dimensions in the notation of -geometric algebra) to demonstrate the formulas derived in section :ref:`deriv`. - -``Begin Program coords.py`` - - -.. literalinclude:: coords.py - -``End Program coords.py`` - -``Begin Program Output`` - -Gradient of Scalar Function :math:`\psi` - -.. math:: - :nowrap: - - \begin{equation*} - \nabla\psi = \partial_{r} {{\psi}}{}\boldsymbol{e}_{r}+\frac{1 \partial_{{\theta}} {{\psi}}}{r}{}\boldsymbol{e}_{{\theta}}+\frac{1 \partial_{{\phi}} {{\psi}}}{r \operatorname{sin}\left({\theta}\right)}{}\boldsymbol{e}_{{\phi}} - \end{equation*} - - -Div and Curl of Vector Function :math:`A` - -.. math:: - :nowrap: - - \begin{equation*} - A = {A^{r}}{}\boldsymbol{e}_{r}+ {A^{{\theta}}}{}\boldsymbol{e}_{{\theta}}+ {A^{{\phi}}}{}\boldsymbol{e}_{{\phi}} - \end{equation*} - - - - -.. math:: - :nowrap: - - \begin{equation*} - \nabla \cdot A = \left(\frac{{A^{{\theta}}} \operatorname{cos}\left({\theta}\right)}{r \operatorname{sin}\left({\theta}\right)} + 2 \frac{{A^{r}}}{r} + \partial_{r} {A^{r}} + \frac{\partial_{{\theta}} {A^{{\theta}}}}{r} + \frac{\partial_{{\phi}} {A^{{\phi}}}}{r \operatorname{sin}\left({\theta}\right)}\right) - \end{equation*} - - - - -.. math:: - :nowrap: - - \begin{align*} - -I\left(\nabla \wedge A\right) & = \left(\frac{\partial_{{\theta}} {A^{{\phi}}}}{r} + \frac{{A^{{\phi}}} \operatorname{cos}\left({\theta}\right)}{r \operatorname{sin}\left({\theta}\right)} -\frac{\partial_{{\phi}} {A^{{\theta}}}}{r \operatorname{sin}\left({\theta}\right)}\right){}\boldsymbol{e}_{r} \\ - & - \left(\frac{{A^{{\phi}}}}{r} + \partial_{r} {A^{{\phi}}} -\frac{\partial_{{\phi}} {A^{r}}}{r \operatorname{sin}\left({\theta}\right)}\right){}\boldsymbol{e}_{{\theta}} \\ - & + \left(\frac{{A^{{\theta}}}}{r} + \partial_{r} {A^{{\theta}}} -\frac{\partial_{{\theta}} {A^{r}}}{r}\right){}\boldsymbol{e}_{{\phi}} - \end{align*} - - -``End Program Output`` - - -.. seealso:: - - `Hestenes `_ - ``Clifford Algebra to Geometric Calculus`` by D.Hestenes and G. Sobczyk, Kluwer - Academic Publishers, 1984. - - `Lasenby `_ - ``Geometric Algebra for Physicists`` by C. Doran and A. Lasenby, Cambridge - University Press, 2003. diff --git a/dev-py3k/_sources/modules/galgebra/index.txt b/dev-py3k/_sources/modules/galgebra/index.txt deleted file mode 100644 index 0debcb88ced..00000000000 --- a/dev-py3k/_sources/modules/galgebra/index.txt +++ /dev/null @@ -1,15 +0,0 @@ -================================== -Geometric Algebra Module Docstring -================================== - -.. automodule: sympy.galgebra - -Documentation for Geometric Algebra module - -Contents: - -.. toctree:: - :maxdepth: 2 - - GA.rst - latex_ex.rst diff --git a/dev-py3k/_sources/modules/galgebra/latex_ex.txt b/dev-py3k/_sources/modules/galgebra/latex_ex.txt deleted file mode 100644 index 0a096443870..00000000000 --- a/dev-py3k/_sources/modules/galgebra/latex_ex.txt +++ /dev/null @@ -1,6 +0,0 @@ -======== -Latex_ex -======== - -.. automodule:: sympy.galgebra.latex_ex - :members: diff --git a/dev-py3k/_sources/modules/galgebra/latex_ex/latex_ex.txt b/dev-py3k/_sources/modules/galgebra/latex_ex/latex_ex.txt deleted file mode 100644 index 7c0a429ffd6..00000000000 --- a/dev-py3k/_sources/modules/galgebra/latex_ex/latex_ex.txt +++ /dev/null @@ -1,417 +0,0 @@ -.. _extended-latex: - -********************************** - Extended LaTeXModule for SymPy -********************************** - -:Author: Alan Bromborsky - -.. |release| replace:: 0.10 - -.. % .. math:: -.. % :nowrap: - -.. % 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 - -.. % \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 extension of the latex module for :mod:`sympy`. The - python module :mod:`latex_ex` extends the capabilities of the current - :mod:`latex` (while preserving the current capabilities) to geometric algebra - multivectors, :mod:`numpy` array's, and extends the ascii formatting of greek - symbols, accents, and subscripts and superscripts. Additionally the module is - configured to use the print command to generate a LaTeX output file and display - it using xdvi in Linux and yap in Windows. To get LaTeX displayed text latex and - xdvi must be installed on a Linux system and MikTex on a Windows system. - - -Extended Symbol Coding -====================== - -One of the main extensions in :mod:`latex_ex` is the ability to encode complex -symbols (multiple greek letters with accents and superscripts and subscripts) is -ascii strings containing only letters, numbers, and underscores. These -restrictions allow :mod:`sympy` variable names to represent complex symbols. For -example if we use the function :func:`symbols` as -follows:: - - xalpha,Gammavec__1_rho,delta__j_k = symbols('xalpha Gammavec__1_rho delta__j_k') - -``symbols`` creates the three :mod:`sympy` symbols *xalpha*, -*Gammavec__1_rho*, and *delta__j_k*. If these symbols are printed with the -:mod:`latex_ex` modules the results are - - -.. csv-table:: - :header: Ascii String, LaTeX Output - :widths: 15, 15 - - ``xalpha``, :math:`x\alpha` - ``Gammavec__1_rho``, :math:`\vec{\Gamma}^{1}_{\rho}` - ``delta__j_k``, :math:`\delta^{j}_{k}` - - -A single underscore denotes a subscript and a double undscore a superscript. - -In addition to all normal LaTeX accents boldmath is supported so that -``omegaomegabm``:math:`\rightarrow \Omega\boldsymbol{\Omega}` so an accent (or boldmath) -only applies to the character (characters in the case of the string representing -a greek letter) immediately preceeding the accent command. - - -How LatexPrinter Works -====================== - -The actual :class:`LatexPrinter` class is hidden with the helper functions -:func:`Format`, :func:`LaTeX`, and :func:`xdvi`. :class:`LatexPrinter` is -setup when :func:`Format` is called. In addition to setting format switches in -:class:`LatexPrinter`, :func:`Format` does two other critical tasks. Firstly, -the :mod:`sympy` function :func:`Basic.__str__` is redirected to the -:class:`LatexPrinter` helper function :func:`LaTeX`. If nothing more that this -were done the python print command would output LaTeX code. Secondly, -*sys.stdout* is redirected to a file until :func:`xdvi` is called. This file -is then compiled with the :program:`latex` program (if present) and the dvi -output file is displayed with the :program:`xdvi` program (if present). Thus -for :class:`LatexPrinter` to display the output in a window both -:program:`latex` and :program:`xdvi` must be installed on your system and in the -execution path. - -One problem for :class:`LatexPrinter` is determining when equation mode should be -use in the output formatting. To allow for printing output not in equation mode the -program attemps to determine from the output string context when to use equation mode. -The current test is to use equation mode if the string contains an =, \_, \^, or \\. -This is not a foolproof method. The safest thing to do if you wish to print an object, *X*, -in math mode is to use *print 'X =',X* so the = sign triggers math mode. - - -LatexPrinter Functions -====================== - - -LatexPrinter Class Functions for Extending LatexPrinter Class -------------------------------------------------------------- - -The :class:`LatexPrinter` class functions are not called directly, but rather -are called when :func:`print`, :func:`LaTeX`, or :func:`str` are called. The -two new functions for extending the :mod:`latex` module are -:func:`_print_ndarray` and :func:`_print_MV`. Some other functions in -:class:`LatexPrinter` have been modified to increase their utility. - - -.. function:: _print_ndarray(self,expr) - - :func:`_print_ndarray` returns a latex formatted string for the *expr* equal to - a :class:`numpy` array with elements that can be :class:`sympy` expressions. - The :class:`numpy` array's can have up to three dimensions. - - -.. function:: _print_MV(self,expr) - - :func:`_print_MV` returns a latex formatted string for the *expr* equal to a - :class:`GA` multivector. - - -.. function:: str_basic(in_str) - - :func:`str_basic` returns a string without the latex formatting provided by - :class:`LatexPrinter`. This is needed since :class:`LatexPrinter` takes over - the :func:`str` fuction and there are instances when the unformatted string is - needed such as during the automatic generation of multivector coefficients and - the reduction of multivector coefficients for printing. - - -Helper Functions for Extending LatexPrinter Class -------------------------------------------------- - - -.. function:: Format(fmt='1 1 1 1') - - ``Format`` iniailizes ``LatexPrinter`` and set the format for ``sympy`` symbols, - functions, and derivatives and for ``GA`` multivectors. The switch are encoded - in the text string argument of ``Format`` as follows. It is assumed that the - text string ``fmt`` always contains four integers separated by blanks. - - .. csv-table:: - :delim: ; - :header: Position, Switch, Values - :widths: 4, 6, 40 - - :math:`1^{st}`; symbol; 0: Use symbol encoding in ``latex.py`` - ; ; 1: Use extended symbol encoding in ``latex_ex.py`` - :math:`2^{nd}`; function; 0: Use symbol encoding in ``latex.py``. Print functions args, use ``\operator{ }`` format. - ; ; 1: Do not print function args. Do not use ``\operator{}`` format. Suppress printing of function arguments. - :math:`3^{rd}`; partial derivative; 0: Use partial derivative format in ``latex.py``. - ; ; 1: Use format :math:`\partial_{x}` instead of :math:`\partial/\partial x`. - :math:`4^{th}`; multivector; 1: Print entire multivector on one line. - ; ; 2: Print each grade of multivector on one line. - ; ; 3: Print each base of multivector on one line. - - -.. function:: LaTeX(expr, inline=True) - - :func:`LaTeX` returns the latex formatted string for the :class:`sympy`, - :class:`GA`, or :class:`numpy` expression *expr*. This is needed since - :class:`numpy` cannot be subclassed and hence cannot be used with the - :class:`LatexPrinter` modified :func:`print` command. Thus is *A* is a - :class:`numpy` array containing :class:`sympy` expressions one cannot simply - code :: - - print A - - but rather must use :: - - print LaTeX(A) - - -.. function:: xdvi(filename='tmplatex.tex',debug=False) - - :func:`xdvi` postprocesses the output of the print statements and generates the - latex file with name *filename*. If the :program:`latex` and :program:`xdvi` - programs are present on the system they are invoked to display the latex file in - a window. If *debug=True* the associated output of :program:`latex` is sent to - *stdout*, otherwise it is sent to */dev/null* for linux and *NUL* for Windows. - If :class:`LatexPrinter` has not been initialized :func:`xdvi` does nothing. After the - .dvi file is generated it is displayed with :program:`xdvi` for linux (if latex and xdvi - are installed ) and :program:`yap` for Windows (if MikTex is installed). - -The functions :func:`sym_format`, :func:`fct_format`, :func:`pdiff_format`, and -:func:`MV_format` allow one to change various formatting aspects of the -:class:`LatexPrinter`. They do not initialize the class and if they are called -with the class not initialized they have no effect. These functions and the -function :func:`xdvi` are designed so that if the :class:`LatexPrinter` class is -not initialized the program output is as if the :class:`LatexPrinter` class is -not used. Thus all one needs to do to get simple ascii output (possibly for -program debugging) is to comment out the one function call that initializes the -:class:`LatexPrinter` class. All other :mod:`latex_ex` function calls can -remain in the program and have no effect on program output. - - -.. function:: sym_format(sym_fmt) - - :func:`sym_format` allows one to change the latex format options for - :class:`sympy` symbol output independent of other format switches (see - :math:`1^{st}` switch in Table I). - - -.. function:: fct_format(fct_fmt) - - :func:`fct_format` allows one to change the latex format options for - :class:`sympy` function output independent of other format switches (see - :math:`2^{nd}` switch in Table I). - - -.. function:: pdiff_format(pdiff_fmt) - - :func:`pdiff_format` allows one to change the latex format options for - :class:`sympy` partial derivative output independent of other format switches - (see :math:`3^{rd}` switch in Table I). - - -.. function:: MV_format(mv_fmt) - - :func:`MV_format` allows one to change the latex format options for - :class:`sympy` partial derivative output independent of other format switches - (see :math:`3^{rd}` switch in Table I). - - -Examples -======== - - -:program:`latexdemo.py` a simple example ----------------------------------------- - -:program:`latexdemo.py` example of using :mod:`latex_ex` with :mod:`sympy` - - -.. include:: latexdemo.py - :literal: - -Start of Program Output - - - -.. math:: - :nowrap: - - \begin{equation*} - x = \frac{{\alpha}_{1} {}\boldsymbol{x}}{{\delta}^{{\nu}{\gamma}}_{r}} - \end{equation*} - - -End of Program Output - -The program :program:`latexdemo.py` demonstrates the extended symbol naming -conventions in :mod:`latex_ex`. the statment ``Format()`` starts the -:class:`LatexPrinter` driver with default formatting. Note that on the right -hand side of the output that *xbm* gives :math:`\boldsymbol{x}`, *alpha_1* gives -:math:`\alpha_{1}` and *delta__nugamma_r* gives :math:`\delta^{\nu\gamma}_{r}`. -Also the fraction is printed correctly. The statment ``print 'x =',x`` sends -the string ``'x = '+str(x)`` to the output processor (:func:`xdvi`). Because -the string contains an :math:`=` sign the processor treats the string as an -LaTeX equation (unnumbered). If ``'x ='`` was not in the print statment a -LaTeX error would be generated. In the case of a :class:`GA` multivector one -does not need the ``'x ='`` if the multivector has been given a name. - - -:program:`Maxwell.py` a multivector example -------------------------------------------- - -:program:`Maxwell.py` example of using :mod:`latex_ex` with :mod:`GA` - - -.. include:: Maxwell.py - :literal: - -Start of Program Output - -:math:`I` Pseudo-Scalar - -.. math:: - :nowrap: - - \begin{equation*} - I = {\gamma}_{t}{\gamma}_{x}{\gamma}_{y}{\gamma}_{z} - \end{equation*} - - -:math:`B` Magnetic Field Bi-Vector - -.. math:: - :nowrap: - - \begin{equation*} - B = - {B^{x}}{\gamma}_{t}{\gamma}_{x}- {B^{y}}{\gamma}_{t}{\gamma}_{y}- {B^{z}}{\gamma}_{t}{\gamma}_{z} - \end{equation*} - - -:math:`F` Electric Field Bi-Vector - -.. math:: - :nowrap: - - \begin{equation*} - E = - {E^{x}}{\gamma}_{t}{\gamma}_{x}- {E^{y}}{\gamma}_{t}{\gamma}_{y}- {E^{z}}{\gamma}_{t}{\gamma}_{z} - \end{equation*} - - -:math:`E+IB` Electo-Magnetic Field Bi-Vector - -.. math:: - :nowrap: - - \begin{equation*} - F = - {E^{x}}{\gamma}_{t}{\gamma}_{x}- {E^{y}}{\gamma}_{t}{\gamma}_{y}- {B^{z}}{\gamma}_{x}{\gamma}_{y}- {E^{z}}{\gamma}_{t}{\gamma}_{z}+ {B^{y}}{\gamma}_{x}{\gamma}_{z}- {B^{x}}{\gamma}_{y}{\gamma}_{z} - \end{equation*} - - -:math:`J` Four Current - -.. math:: - :nowrap: - - \begin{equation*} - J = {J^{t}}{\gamma}_{t}+ {J^{x}}{\gamma}_{x}+ {J^{y}}{\gamma}_{y}+ {J^{z}}{\gamma}_{z} - \end{equation*} - - -Geometric Derivative of Electo-Magnetic Field Bi-Vector - -.. math:: - :nowrap: - - \begin{align*} - \nabla F & = \left(\partial_{z} {E^{z}} + \partial_{y} {E^{y}} + \partial_{x} {E^{x}}\right){\gamma}_{t} \\ & + \left(-\partial_{t} {E^{x}} + \partial_{y} {B^{z}} -\partial_{z} {B^{y}}\right){\gamma}_{x} \\ & + \left(\partial_{z} {B^{x}} -\partial_{t} {E^{y}} -\partial_{x} {B^{z}}\right){\gamma}_{y} \\ & + \left(-\partial_{y} {B^{x}} -\partial_{t} {E^{z}} + \partial_{x} {B^{y}}\right){\gamma}_{z} \\ & + \left(-\partial_{x} {E^{y}} -\partial_{t} {B^{z}} + \partial_{y} {E^{x}}\right){\gamma}_{t}{\gamma}_{x}{\gamma}_{y} \\ & + \left(-\partial_{x} {E^{z}} + \partial_{t} {B^{y}} + \partial_{z} {E^{x}}\right){\gamma}_{t}{\gamma}_{x}{\gamma}_{z} \\ & + \left(-\partial_{t} {B^{x}} -\partial_{y} {E^{z}} + \partial_{z} {E^{y}}\right){\gamma}_{t}{\gamma}_{y}{\gamma}_{z} \\ & + \left(\partial_{y} {B^{y}} + \partial_{z} {B^{z}} + \partial_{x} {B^{x}}\right){\gamma}_{x}{\gamma}_{y}{\gamma}_{z}\end{align*} - - -All Maxwell Equations are - -.. math:: - :nowrap: - - \begin{equation*} - \nabla F = J - \end{equation*} - - -Div :math:`E` and Curl :math:`H` Equations - -.. math:: - :nowrap: - - \begin{align*} - <\nabla F>_1 -J & = \left(-{J^{t}} + \partial_{z} {E^{z}} + \partial_{y} {E^{y}} + \partial_{x} {E^{x}}\right){\gamma}_{t} \\ & + \left(-{J^{x}} -\partial_{t} {E^{x}} + \partial_{y} {B^{z}} -\partial_{z} {B^{y}}\right){\gamma}_{x} \\ & + \left(\partial_{z} {B^{x}} -\partial_{t} {E^{y}} -{J^{y}} -\partial_{x} {B^{z}}\right){\gamma}_{y} \\ & + \left(-\partial_{y} {B^{x}} -\partial_{t} {E^{z}} -{J^{z}} + \partial_{x} {B^{y}}\right){\gamma}_{z}\end{align*} - - - - -.. math:: - :nowrap: - - \begin{equation*} - = 0 - \end{equation*} - - -Curl :math:`E` and Div :math:`B` equations - -.. math:: - :nowrap: - - \begin{align*} - <\nabla F>_3 & = \left( -\partial_{x} {E^{y}} -\partial_{t} {B^{z}} + \partial_{y} {E^{x}}\right){\gamma}_{t}\wedge {\gamma}_{x}\wedge {\gamma}_{y} \\ & + \left( -\partial_{x} {E^{z}} + \partial_{t} {B^{y}} + \partial_{z} {E^{x}}\right){\gamma}_{t}\wedge {\gamma}_{x}\wedge {\gamma}_{z} \\ & + \left( -\partial_{t} {B^{x}} -\partial_{y} {E^{z}} + \partial_{z} {E^{y}}\right){\gamma}_{t}\wedge {\gamma}_{y}\wedge {\gamma}_{z} \\ & + \left( \partial_{y} {B^{y}} + \partial_{z} {B^{z}} + \partial_{x} {B^{x}}\right){\gamma}_{x}\wedge {\gamma}_{y}\wedge {\gamma}_{z}\end{align*} - - - - -.. math:: - :nowrap: - - \begin{equation*} - = 0 - \end{equation*} - - -End of Program Output - -The program :program:`Maxwell.py` demonstrates the use of the -:class:`LatexPrinter` class with the :mod:`GA` module multivector class, -:class:`MV`. The :func:`Format` call initializes :class:`LatexPrinter`. The -only other explicit :mod:`latex_x` module formatting statement used is -``MV_format(3)``. This statment changes the multivector latex format so that -instead of printing the entire multivector on one line, which would run off the -page, each multivector base and its coefficient are printed on individual lines -using the latex align environment. Another option used is that the printing of -function arguments is suppressed since :math:`E`, :math:`B`, :math:`J`, and -:math:`F` are multivector fields and printing out the argument, -:math:`(t,x,y,z)`, for every field component would greatly lengthen the output -and make it more difficult to format in a pleasing way. diff --git a/dev-py3k/_sources/modules/geometry.txt b/dev-py3k/_sources/modules/geometry.txt deleted file mode 100644 index 4c89fd049dd..00000000000 --- a/dev-py3k/_sources/modules/geometry.txt +++ /dev/null @@ -1,268 +0,0 @@ -Geometry Module -=============== - -.. module:: sympy.geometry - -Introduction ------------- - -The geometry module for SymPy allows one to create two-dimensional geometrical -entities, such as lines and circles, and query information about these -entities. This could include asking the area of an ellipse, checking for -collinearity of a set of points, or finding the intersection between two lines. -The primary use case of the module involves entities with numerical values, but -it is possible to also use symbolic representations. - -Available Entities ------------------- - -The following entities are currently available in the geometry module: - -* Point -* Line, Ray, Segment -* Ellipse, Circle -* Polygon, RegularPolygon, Triangle - -Most of the work one will do will be through the properties and methods of -these entities, but several global methods exist for one's usage: - -* intersection(entity1, entity2) -* are_similar(entity1, entity2) -* convex_hull(points) - -For a full API listing and an explanation of the methods and their return -values please see the list of classes at the end of this document. - -Example Usage -------------- - -The following Python session gives one an idea of how to work with some of the -geometry module. - - >>> from sympy import * - >>> from sympy.geometry import * - >>> x = Point(0, 0) - >>> y = Point(1, 1) - >>> z = Point(2, 2) - >>> zp = Point(1, 0) - >>> Point.is_collinear(x, y, z) - True - >>> Point.is_collinear(x, y, zp) - False - >>> t = Triangle(zp, y, x) - >>> t.area - 1/2 - >>> t.medians[x] - Segment(Point(0, 0), Point(1, 1/2)) - >>> Segment(Point(1, S(1)/2), Point(0, 0)) - Segment(Point(0, 0), Point(1, 1/2)) - >>> m = t.medians - >>> intersection(m[x], m[y], m[zp]) - [Point(2/3, 1/3)] - >>> c = Circle(x, 5) - >>> l = Line(Point(5, -5), Point(5, 5)) - >>> c.is_tangent(l) # is l tangent to c? - True - >>> l = Line(x, y) - >>> c.is_tangent(l) # is l tangent to c? - False - >>> intersection(c, l) - [Point(-5*sqrt(2)/2, -5*sqrt(2)/2), Point(5*sqrt(2)/2, 5*sqrt(2)/2)] - -Intersection of medians ------------------------ -:: - - >>> from sympy import symbols - >>> from sympy.geometry import Point, Triangle, intersection - - >>> a, b = symbols("a,b", positive=True) - - >>> x = Point(0, 0) - >>> y = Point(a, 0) - >>> z = Point(2*a, b) - >>> t = Triangle(x, y, z) - - >>> t.area - a*b/2 - - >>> t.medians[x] - Segment(Point(0, 0), Point(3*a/2, b/2)) - - >>> intersection(t.medians[x], t.medians[y], t.medians[z]) - [Point(a, b/3)] - -An in-depth example: Pappus' Theorem ------------------------------------- -:: - - >>> from sympy import * - >>> from sympy.geometry import * - >>> - >>> l1 = Line(Point(0, 0), Point(5, 6)) - >>> l2 = Line(Point(0, 0), Point(2, -2)) - >>> - >>> def subs_point(l, val): - ... """Take an arbitrary point and make it a fixed point.""" - ... t = Symbol('t', real=True) - ... ap = l.arbitrary_point() - ... return Point(ap.x.subs(t, val), ap.y.subs(t, val)) - ... - >>> p11 = subs_point(l1, 5) - >>> p12 = subs_point(l1, 6) - >>> p13 = subs_point(l1, 11) - >>> - >>> p21 = subs_point(l2, -1) - >>> p22 = subs_point(l2, 2) - >>> p23 = subs_point(l2, 13) - >>> - >>> ll1 = Line(p11, p22) - >>> ll2 = Line(p11, p23) - >>> ll3 = Line(p12, p21) - >>> ll4 = Line(p12, p23) - >>> ll5 = Line(p13, p21) - >>> ll6 = Line(p13, p22) - >>> - >>> pp1 = intersection(ll1, ll3)[0] - >>> pp2 = intersection(ll2, ll5)[0] - >>> pp3 = intersection(ll4, ll6)[0] - >>> - >>> Point.is_collinear(pp1, pp2, pp3) - True - -Miscellaneous Notes -------------------- - -* The area property of Polygon and Triangle may return a positive or - negative value, depending on whether or not the points are oriented - counter-clockwise or clockwise, respectively. If you always want a - positive value be sure to use the ``abs`` function. -* Although Polygon can refer to any type of polygon, the code has been - written for simple polygons. Hence, expect potential problems if dealing - with complex polygons (overlapping sides). -* Since !SymPy is still in its infancy some things may not simplify - properly and hence some things that should return True (e.g., - Point.is_collinear) may not actually do so. Similarly, attempting to find - the intersection of entities that do intersect may result in an empty - result. - -Future Work ------------ - -Truth Setting Expressions -~~~~~~~~~~~~~~~~~~~~~~~~~ - -When one deals with symbolic entities, it often happens that an assertion -cannot be guaranteed. For example, consider the following code: - - >>> from sympy import * - >>> from sympy.geometry import * - >>> x,y,z = list(map(Symbol, 'xyz')) - >>> p1,p2,p3 = Point(x, y), Point(y, z), Point(2*x*y, y) - >>> Point.is_collinear(p1, p2, p3) - False - -Even though the result is currently ``False``, this is not *always* true. If the -quantity `z - y - 2*y*z + 2*y**2 == 0` then the points will be collinear. It -would be really nice to inform the user of this because such a quantity may be -useful to a user for further calculation and, at the very least, being nice to -know. This could be potentially done by returning an object (e.g., -GeometryResult) that the user could use. This actually would not involve an -extensive amount of work. - -Three Dimensions and Beyond -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Currently there are no plans for extending the module to three dimensions, but -it certainly would be a good addition. This would probably involve a fair -amount of work since many of the algorithms used are specific to two -dimensions. - -Geometry Visualization -~~~~~~~~~~~~~~~~~~~~~~ - -The plotting module is capable of plotting geometric entities. See -`Plotting Geometric Entities `_ in -the plotting module entry. - -API Reference -------------- - -Entities -~~~~~~~~ - -.. module:: sympy.geometry.entity - -.. autoclass:: sympy.geometry.entity.GeometryEntity - :members: - -Utils -~~~~~ - -.. module:: sympy.geometry.util - -.. autofunction:: intersection - -.. autofunction:: convex_hull - -.. autofunction:: are_similar - -.. autofunction:: centroid - -Points -~~~~~~ - -.. module:: sympy.geometry.point - -.. autoclass:: Point - :members: - -Lines -~~~~~ - -.. module:: sympy.geometry.line - -.. autoclass:: LinearEntity - :members: - -.. autoclass:: Line - :members: - -.. autoclass:: Ray - :members: - -.. autoclass:: Segment - :members: - -Curves -~~~~~~ - -.. module:: sympy.geometry.curve - -.. autoclass:: Curve - :members: - -Ellipses -~~~~~~~~ - -.. module:: sympy.geometry.ellipse - -.. autoclass:: Ellipse - :members: - -.. autoclass:: Circle - :members: - -Polygons -~~~~~~~~ - -.. module:: sympy.geometry.polygon - -.. autoclass:: Polygon - :members: - -.. autoclass:: RegularPolygon - :members: - -.. autoclass:: Triangle - :members: diff --git a/dev-py3k/_sources/modules/index.txt b/dev-py3k/_sources/modules/index.txt deleted file mode 100644 index 5d5396db2bb..00000000000 --- a/dev-py3k/_sources/modules/index.txt +++ /dev/null @@ -1,59 +0,0 @@ -.. _module-docs: - -SymPy Modules Reference -======================= - -Because every feature of SymPy must have a test case, when you are not sure how -to use something, just look into the ``tests/`` directories, find that feature -and read the tests for it, that will tell you everything you need to know. - -Most of the things are already documented though in this document, that is -automatically generated using SymPy's docstrings. - -Click the "modules" (:ref:`modindex`) link in the top right corner to easily -access any SymPy module, or use this contens: - -.. toctree:: - :maxdepth: 2 - - core.rst - combinatorics/index.rst - ntheory.rst - concrete.rst - evalf.rst - functions/index.rst - geometry.rst - galgebra/index.rst - galgebra/GA/GAsympy.rst - galgebra/latex_ex/latex_ex.rst - integrals/integrals.rst - logic.rst - matrices/index.rst - mpmath/index.rst - polys/index.rst - printing.rst - plotting.rst - assumptions/index.rst - rewriting.rst - series.rst - sets.rst - simplify/simplify.rst - simplify/hyperexpand.rst - statistics.rst - stats.rst - solvers/ode.rst - solvers/solvers.rst - tensor/index.rst - utilities/index.rst - parsing.rst - physics/index.rst - categories.rst - diffgeom.rst - -Contributions to docs ---------------------- - -All contributions are welcome. If you'd like to improve something, look into -the sources if they contain the information you need (if not, please fix them), -otherwise the documentation generation needs to be improved (look in the -``doc/`` directory). diff --git a/dev-py3k/_sources/modules/integrals/g-functions.txt b/dev-py3k/_sources/modules/integrals/g-functions.txt deleted file mode 100644 index 999af68a21a..00000000000 --- a/dev-py3k/_sources/modules/integrals/g-functions.txt +++ /dev/null @@ -1,508 +0,0 @@ -Computing Integrals using Meijer G-Functions -******************************************** - -This text aims do describe in some detail the steps (and subtleties) involved -in using Meijer G-functions for computing definite and indefinite integrals. -We shall ignore proofs completely. - -Overview -======== - -The algorithm to compute `\int f(x) \mathrm{d}x` or -`\int_0^\infty f(x) \mathrm{d}x` generally consists of three steps: - -1. Rewrite the integrand using Meijer G-functions (one or sometimes two). -2. Apply an integration theorem, to get the answer (usually expressed as another - G-function). -3. Expand the result in named special functions. - -Step (3) is implemented in the function hyperexpand (q.v.). Steps (1) and (2) -are described below. Morever, G-functions are usually branched. Thus our treatment -of branched functions is described first. - -Some other integrals (e.g. `\int_{-\infty}^\infty`) can also be computed by first -recasting them into one of the above forms. There is a lot of choice involved -here, and the algorithm is heuristic at best. - -Polar Numbers and Branched Functions -==================================== - -Both Meijer G-Functions and Hypergeometric functions are typically branched -(possible branchpoints being `0`, `\pm 1`, `\infty`). This is not very important -when e.g. expanding a single hypergeometric function into named special functions, -since sorting out the branches can be left to the human user. However this -algorithm manipulates and transforms G-functions, and to do this correctly it needs -at least some crude understanding of the branchings involved. - -To begin, we consider the set -`\mathcal{S} = \{(r, \theta) : r > 0, \theta \in \mathbb{R}\}`. We have a map -`p: \mathcal{S}: \rightarrow \mathbb{C}-\{0\}, (r, \theta) \mapsto r e^{i \theta}`. -Decreeing this to be a local biholomorphism gives `\mathcal{S}` both a topology -and a complex structure. This Riemann Surface is usually referred to as the -Riemann Surface of the logarithm, for the following reason: -We can define maps -`Exp: \mathbb{C} \rightarrow \mathcal{S}, Exp(x + i y) = (exp(x), y)` and -`Log: \mathcal{S} \rightarrow \mathbb{C}, (e^x, y) \mapsto x + iy`. -These can be shown to be both holomorphic, and are indeed mutual inverses. - -We also sometimes formally attach a point "zero" to `\mathcal{S}` and denote the -resulting object `\mathcal{S}_0`. Notably there is no complex structure -defined near `0`. A fundamental system of neighbourhoods is given by -`\{Exp(z) : Re(z) < k\}`, this at least defines a topology. Elements of -`\mathcal{S}_0` shall be called polar numbers. -We further define functions -`Arg: \mathcal{S} \rightarrow \mathbb{R}, (r, \theta) \mapsto \theta` and -`|.|: \mathcal{S}_0 \rightarrow \mathbb{R}_{>0}, (r, \theta) \mapsto r`. -These have evident meaning and are both continuous everywhere. - -Using these maps many operations can be extended from `\mathbb{C}` to -`\mathcal{S}`. We define `Exp(a) Exp(b) = Exp(a + b)` for `a, b \in \mathbb{C}`, -also for `a \in \mathcal{S}` and `b \in \mathbb{C}` we define -`a^b = Exp(b Log(a))`. -It can be checked easily that using these definitions, many algebraic properties -holding for positive reals (e.g. `(ab)^c = a^c b^c`) which hold in `\mathbb{C}` -only for some numbers (because of branch cuts) hold indeed for all polar numbers. - -As one peculiarity it should be mentioned that addition of polar numbers is not -usually defined. However, formal sums of polar numbers can be used to express -branching behaviour. For example, consider the functions `F(z) = \sqrt{1 + z}` -and `G(a, b) = \sqrt{a + b}`, where `a, b, z` are polar numbers. -The general rule is that functions of a single polar variable are defined in -such a way that they are continuous on circles, and agree with the usual -definition for positive reals. Thus if `S(z)` denotes the standard branch of -the square root function on `\mathbb{C}`, we are forced to define - -.. math:: F(z) = \begin{cases} - S(p(z)) &: |z| < 1 \\ - S(p(z)) &: -\pi < Arg(z) + 4\pi n \le \pi \text{ for some } n \in \mathbb{Z} \\ - -S(p(z)) &: \text{else} - \end{cases}. - -(We are omitting `|z| = 1` here, this does not matter for integration.) -Finally we define `G(a, b) = \sqrt{a}F(b/a)`. - -Representing Branched Functions on the Argand Plane -=================================================== - -Suppose `f: \mathcal{S} \to \mathbb{C}` is a holomorphic function. We wish to -define a function `F` on (part of) the complex numbers `\mathbb{C}` that -represents `f` as closely as possible. This process is knows as "introducing -branch cuts". In our situation, there is actually a canonical way of doing this -(which is adhered to in all of SymPy), as follows: Introduce the "cut complex -plane" -`C = \mathbb{C} \setminus \mathbb{R}_{\le 0}`. Define a function -`l: C \to \mathcal{S}` via `re^{i\theta} \mapsto r Exp(i\theta)`. Here `r > 0` -and `-\pi < \theta \le \pi`. Then `l` is holomorphic, and we define -`G = f \circ l`. This called "lifting to the principal branch" throughout the -SymPy documentation. - -Table Lookups and Inverse Mellin Transforms -=========================================== - -Suppose we are given an integrand `f(x)` and are trying to rewrite it as a -single G-function. To do this, we first split `f(x)` into the form `x^s g(x)` -(where `g(x)` is supposed to be simpler than `f(x)`). This is because multiplicative -powers can be absorbed into the G-function later. This splitting is done by -``_split_mul(f, x)``. Then we assemble a tuple of functions that occur in -`f` (e.g. if `f(x) = e^x \cos{x}`, we would assemble the tuple `(\cos, \exp)`). -This is done by the function ``_mytype(f, x)``. Next we index a lookup table -(created using ``_create_lookup_table()``) with this tuple. This (hopefully) -yields a list of Meijer G-function formulae involving these functions, we then -pattern-match all of them. If one fits, we were successful, otherwise not and we -have to try something else. - -Suppose now we want to rewrite as a product of two G-functions. To do this, -we (try to) find all inequivalent ways of splitting `f(x)` into a product -`f_1(x) f_2(x)`. -We could try these splittings in any order, but it is often a good idea to -minimise (a) the number of powers occuring in `f_i(x)` and (b) the number of -different functions occuring in `f_i(x)`. Thus given e.g. -`f(x) = \sin{x}\, e^{x} \sin{2x}` we should try `f_1(x) = \sin{x}\, \sin{2x}`, -`f_2(x) = e^{x}` first. -All of this is done by the function ``_mul_as_two_parts(f)``. - -Finally, we can try a recursive Mellin transform technique. Since the Meijer -G-function is defined essentially as a certain inverse mellin transform, -if we want to write a function `f(x)` as a G-function, we can compute its mellin -transform `F(s)`. If `F(s)` is in the right form, the G-function expression -can be read off. This technique generalises many standard rewritings, e.g. -`e^{ax} e^{bx} = e^{(a + b) x}`. - -One twist is that some functions don't have mellin transforms, even though they -can be written as G-functions. This is true for example for `f(x) = e^x \sin{x}` -(the function grows too rapidly to have a mellin transform). However if the function -is recognised to be analytic, then we can try to compute the mellin-transform of -`f(ax)` for a parameter `a`, and deduce the G-function expression by analytic -continuation. (Checking for analyticity is easy. Since we can only deal with a -certain subset of functions anyway, we only have to filter out those which are -not analyitc.) - -The function ``_rewrite_single`` does the table lookup and recursive mellin -transform. The functions ``_rewrite1`` and ``_rewrite2`` respectively use -above-mentioned helpers and ``_rewrite_single`` to rewrite their argument as -respectively one or two G-functions. - -Applying the Integral Theorems -============================== - -If the integrand has been recast into G-functions, evaluating the integral is -relatively easy. We first do some substitutions to reduce e.g. the exponent -of the argument of the G-function to unity (see ``_rewrite_saxena_1`` and -``_rewrite_saxena``, respectively, for one or two G-functions). Next we go through -a list of conditions under which the integral theorem applies. It can fail for -basically two reasons: either the integral does not exist, or the manipulations -in deriving the theorem may not be allowed (for more details, see this [BlogPost]_). - -Sometimes this can be remedied by reducing the argument of the G-functions -involved. For example it is clear that the G-function representing `e^z` -is satisfies `G(Exp(2 \pi i)z) = G(z)` for all `z \in \mathcal{S}`. The function -``meijerg.get_period()`` can be used to discover this, and the function -``principal_branch(z, period)`` in ``functions/elementary/complexes.py`` can -be used to exploit the information. This is done transparently by the -integration code. - -.. [BlogPost] http://nessgrh.wordpress.com/2011/07/07/tricky-branch-cuts/ - -The G-Function Integration Theorems -*********************************** - -This section intends to display in detail the definite integration theorems -used in the code. The following two formulae go back to Meijer (In fact he -proved more general formulae; indeed in the literature formulae are usually -staded in more general form. However it is very easy to deduce the general -formulae from the ones we give here. It seemed best to keep the theorems as -simple as possible, since they are very complicated anyway.): - -1. .. math:: \int_0^\infty - G_{p, q}^{m, n} \left.\left(\begin{matrix} a_1, \dots, a_p \\ - b_1, \dots, b_q \end{matrix} - \right| \eta x \right) \mathrm{d}x = - \frac{\prod_{j=1}^m \Gamma(b_j + 1) \prod_{j=1}^n \Gamma(-a_j)}{\eta - \prod_{j=m+1}^q \Gamma(-b_j) \prod_{j=n+1}^p \Gamma(a_j + 1)} - -2. .. math:: \int_0^\infty - G_{u, v}^{s, t} \left.\left(\begin{matrix} c_1, \dots, c_u \\ - d_1, \dots, d_v \end{matrix} - \right| \sigma x \right) - G_{p, q}^{m, n} \left.\left(\begin{matrix} a_1, \dots, a_p \\ - b_1, \dots, b_q \end{matrix} - \right| \omega x \right) - \mathrm{d}x = - G_{v+p, u+q}^{m+t, n+s} \left.\left( - \begin{matrix} a_1, \dots, a_n, -d_1, \dots, -d_v, a_{n+1}, \dots, a_p \\ - b_1, \dots, b_m, -c_1, \dots, -c_u, b_{m+1}, \dots, b_q - \end{matrix} - \right| \frac{\omega}{\sigma} \right) - -The more interesting question is under what conditions these formulae are -valid. Below we detail the conditions implemented in SymPy. They are an -amalgamation of conditions found in [Prudnikov1990]_ and [Luke1969]_; please -let us know if you find any errors. - -Conditions of Convergence for Integral (1) -========================================== -.. TODO: Formatting could be improved. - -We can without loss of generality assume `p \le q`, since the G-functions -of indices `m, n, p, q` and of indices `n, m, q, p` can be related easily -(see e.g. [Luke1969]_, section 5.3). We introduce the following notation: - -.. math:: \xi = m + n - p \\ - \delta = m + n - \frac{p + q}{2} - -.. math:: C_3: -\Re(b_j) < 1 \text{ for } j=1, \dots, m \\ - 0 < -\Re(a_j) \text{ for } j=1, \dots, n - -.. math:: C_3^*: -\Re(b_j) < 1 \text{ for } j=1, \dots, q \\ - 0 < -\Re(a_j) \text{ for } j=1, \dots, p - -.. math:: C_4: -\Re(\delta) + \frac{q + 1 - p}{2} > q - p - -The convergence conditions will be detailed in several "cases", numbered one -to five. For later use it will be helpful to separate conditions "at infinity" -from conditions "at zero". By conditions "at infinity" we mean conditions that -only depend on the behaviour of the integrand for large, positive values -of `x`, whereas by conditions "at zero" we mean conditions that only depend on -the behaviour of the integrand on `(0, \epsilon)` for any `\epsilon > 0`. -Since all our conditions are specified in terms of parameters of the -G-functions, this distinction is not immediately visible. They are, however, of -very distinct character mathematically; the conditions at infinity being in -particular much harder to control. - -In order for the integral theorem to be valid, conditions -`n` "at zero" and "at infinity" both have to be fulfilled, for some `n`. - -These are the conditions "at infinity": - -1. .. math:: \delta > 0 \wedge |\arg(\eta)| < \delta \pi \wedge (A \vee B \vee C), - - where - - .. math:: - A = 1 \le n \wedge p < q \wedge 1 \le m - - .. math:: - B = 1 \le p \wedge 1 \le m \wedge q = p+1 \wedge - \neg (n = 0 \wedge m = p + 1 ) - - .. math:: - C = 1 \le n \wedge q = p \wedge |\arg(\eta)| \ne (\delta - 2k)\pi - \text{ for } k = 0, 1, \dots - \left\lceil \frac{\delta}{2} \right\rceil. -2. .. math:: n = 0 \wedge p + 1 \le m \wedge |\arg(\eta)| < \delta \pi -3. .. math:: (p < q \wedge 1 \le m \wedge \delta > 0 \wedge |\arg(\eta)| = \delta \pi) - \vee (p \le q - 2 \wedge \delta = 0 \wedge \arg(\eta) = 0) -4. .. math:: p = q \wedge \delta = 0 \wedge \arg(\eta) = 0 \wedge \eta \ne 0 - \wedge \Re\left(\sum_{j=1}^p b_j - a_j \right) < 0 -5. .. math:: \delta > 0 \wedge |\arg(\eta)| < \delta \pi - -And these are the conditions "at zero": - -1. .. math:: \eta \ne 0 \wedge C_3 -2. .. math:: C_3 -3. .. math:: C_3 \wedge C_4 -4. .. math:: C_3 -5. .. math:: C_3 - -Conditions of Convergence for Integral (2) -========================================== - -We introduce the following notation: - -.. many of the latex expressions below were generated semi-automatically - -.. math:: b^* = s + t - \frac{u + v}{2} -.. math:: c^* = m + n - \frac{p + q}{2} -.. math:: \rho = \sum_{j=1}^v d_j - \sum_{j=1}^u c_j + \frac{u - v}{2} + 1 -.. math:: \mu = \sum_{j=1}^q b_j - \sum_{j=1}^p a_j + \frac{p - q}{2} + 1 -.. math:: \phi = q - p - \frac{u - v}{2} + 1 -.. math:: \eta = 1 - (v - u) - \mu - \rho -.. math:: \psi = \frac{\pi(q - m - n) + |\arg(\omega)|}{q - p} -.. math:: \theta = \frac{\pi(v - s - t) + |\arg(\sigma)|)}{v - u} -.. math:: \lambda_c = (q - p)|\omega|^{1/(q - p)} \cos{\psi} - + (v - u)|\sigma|^{1/(v - u)} \cos{\theta} -.. math:: \lambda_{s0}(c_1, c_2) = c_1 (q - p)|\omega|^{1/(q - p)} \sin{\psi} - + c_2 (v - u)|\sigma|^{1/(v - u)} \sin{\theta} -.. math:: \lambda_s = - \begin{cases} \operatorname{\lambda_{s0}}\left(-1,-1\right) \operatorname{\lambda_{s0}}\left(1,1\right) & \text{for}\: \arg(\omega) = 0 \wedge \arg(\sigma) = 0 \\\operatorname{\lambda_{s0}}\left(\operatorname{sign}\left(\operatorname{\arg}\left(\omega\right)\right),-1\right) \operatorname{\lambda_{s0}}\left(\operatorname{sign}\left(\operatorname{\arg}\left(\omega\right)\right),1\right) & \text{for}\: \arg(\omega) \ne 0 \wedge \arg(\sigma) = 0 \\\operatorname{\lambda_{s0}}\left(-1,\operatorname{sign}\left(\operatorname{\arg}\left(\sigma\right)\right)\right) \operatorname{\lambda_{s0}}\left(1,\operatorname{sign}\left(\operatorname{\arg}\left(\sigma\right)\right)\right) & \text{for}\: \arg(\omega) = 0 \wedge \arg(\sigma) \ne 0) \\\operatorname{\lambda_{s0}}\left(\operatorname{sign}\left(\operatorname{\arg}\left(\omega\right)\right),\operatorname{sign}\left(\operatorname{\arg}\left(\sigma\right)\right)\right) & \text{otherwise} \end{cases} -.. math:: z_0 = \frac{\omega}{\sigma} e^{-i\pi (b^* + c^*)} -.. math:: z_1 = \frac{\sigma}{\omega} e^{-i\pi (b^* + c^*)} - -The following conditions will be helpful: - -.. math:: C_1: (a_i - b_j \notin \mathbb{Z}_{>0} \text{ for } i = 1, \dots, n, j = 1, \dots, m) \\ - \wedge - (c_i - d_j \notin \mathbb{Z}_{>0} \text{ for } i = 1, \dots, t, j = 1, \dots, s) -.. math:: C_2: - \Re(1 + b_i + d_j) > 0 \text{ for } i = 1, \dots, m, j = 1, \dots, s -.. math:: C_3: - \Re(a_i + c_j) < 1 \text{ for } i = 1, \dots, n, j = 1, \dots, t -.. math:: C_4: - (p - q)\Re(c_i) - \Re(\mu) > -\frac{3}{2} \text{ for } i=1, \dots, t -.. math:: C_5: - (p - q)\Re(1 + d_i) - \Re(\mu) > -\frac{3}{2} \text{ for } i=1, \dots, s -.. math:: C_6: - (u - v)\Re(a_i) - \Re(\rho) > -\frac{3}{2} \text{ for } i=1, \dots, n -.. math:: C_7: - (u - v)\Re(1 + b_i) - \Re(\rho) > -\frac{3}{2} \text{ for } i=1, \dots, m -.. math:: C_8: - 0 < \lvert{\phi}\rvert + 2 \Re\left(\left(\mu -1\right) \left(- u + v\right) + \left(- p + q\right) \left(\rho -1\right) + \left(- p + q\right) \left(- u + v\right)\right) -.. math:: C_9: - 0 < \lvert{\phi}\rvert - 2 \Re\left(\left(\mu -1\right) \left(- u + v\right) + \left(- p + q\right) \left(\rho -1\right) + \left(- p + q\right) \left(- u + v\right)\right) -.. math:: C_{10}: - \lvert{\operatorname{arg}\left(\sigma\right)}\rvert < \pi b^{*} -.. math:: C_{11}: - \lvert{\operatorname{arg}\left(\sigma\right)}\rvert = \pi b^{*} -.. math:: C_{12}: - |\arg(\omega)| < c^*\pi -.. math:: C_{13}: - |\arg(\omega)| = c^*\pi -.. math:: C_{14}^1: - \left(z_0 \ne 1 \wedge |\arg(1 - z_0)| < \pi \right) \vee - \left(z_0 = 1 \wedge \Re(\mu + \rho - u + v) < 1 \right) -.. math:: C_{14}^2: - \left(z_1 \ne 1 \wedge |\arg(1 - z_1)| < \pi \right) \vee - \left(z_1 = 1 \wedge \Re(\mu + \rho - p + q) < 1 \right) -.. math:: C_{14}: - \phi = 0 \wedge b^* + c^* \le 1 \wedge (C_{14}^1 \vee C_{14}^2) -.. math:: C_{15}: - \lambda_c > 0 \vee (\lambda_c = 0 \wedge \lambda_s \ne 0 \wedge \Re(\eta) > -1) - \vee (\lambda_c = 0 \wedge \lambda_s = 0 \wedge \Re(\eta) > 0) -.. math:: C_{16}: \int_0^\infty G_{u, v}^{s, t}(\sigma x) \mathrm{d} x - \text{ converges at infinity } -.. math:: C_{17}: \int_0^\infty G_{p, q}^{m, n}(\omega x) \mathrm{d} x - \text{ converges at infinity } - -Note that `C_{16}` and `C_{17}` are the reason we split the convergence conditions for -integral (1). - -With this notation established, the implemented convergence conditions can be enumerated -as follows: - -1. .. math:: m n s t \neq 0 \wedge 0 < b^{*} \wedge 0 < c^{*} \wedge C_{1} \wedge C_{2} \wedge C_{3} \wedge C_{10} \wedge C_{12} -2. .. math:: u = v \wedge b^{*} = 0 \wedge 0 < c^{*} \wedge 0 < \sigma \wedge \Re{\rho} < 1 \wedge C_{1} \wedge C_{2} \wedge C_{3} \wedge C_{12} -3. .. math:: p = q \wedge u = v \wedge b^{*} = 0 \wedge c^{*} = 0 \wedge 0 < \sigma \wedge 0 < \omega \wedge \Re{\mu} < 1 \wedge \Re{\rho} < 1 \wedge \sigma \neq \omega \wedge C_{1} \wedge C_{2} \wedge C_{3} -4. .. math:: p = q \wedge u = v \wedge b^{*} = 0 \wedge c^{*} = 0 \wedge 0 < \sigma \wedge 0 < \omega \wedge \Re\left(\mu + \rho\right) < 1 \wedge \omega \neq \sigma \wedge C_{1} \wedge C_{2} \wedge C_{3} -5. .. math:: p = q \wedge u = v \wedge b^{*} = 0 \wedge c^{*} = 0 \wedge 0 < \sigma \wedge 0 < \omega \wedge \Re\left(\mu + \rho\right) < 1 \wedge \omega \neq \sigma \wedge C_{1} \wedge C_{2} \wedge C_{3} -6. .. math:: q < p \wedge 0 < s \wedge 0 < b^{*} \wedge 0 \leq c^{*} \wedge C_{1} \wedge C_{2} \wedge C_{3} \wedge C_{5} \wedge C_{10} \wedge C_{13} -7. .. math:: p < q \wedge 0 < t \wedge 0 < b^{*} \wedge 0 \leq c^{*} \wedge C_{1} \wedge C_{2} \wedge C_{3} \wedge C_{4} \wedge C_{10} \wedge C_{13} -8. .. math:: v < u \wedge 0 < m \wedge 0 < c^{*} \wedge 0 \leq b^{*} \wedge C_{1} \wedge C_{2} \wedge C_{3} \wedge C_{7} \wedge C_{11} \wedge C_{12} -9. .. math:: u < v \wedge 0 < n \wedge 0 < c^{*} \wedge 0 \leq b^{*} \wedge C_{1} \wedge C_{2} \wedge C_{3} \wedge C_{6} \wedge C_{11} \wedge C_{12} -10. .. math:: q < p \wedge u = v \wedge b^{*} = 0 \wedge 0 \leq c^{*} \wedge 0 < \sigma \wedge \Re{\rho} < 1 \wedge C_{1} \wedge C_{2} \wedge C_{3} \wedge C_{5} \wedge C_{13} -11. .. math:: p < q \wedge u = v \wedge b^{*} = 0 \wedge 0 \leq c^{*} \wedge 0 < \sigma \wedge \Re{\rho} < 1 \wedge C_{1} \wedge C_{2} \wedge C_{3} \wedge C_{4} \wedge C_{13} -12. .. math:: p = q \wedge v < u \wedge 0 \leq b^{*} \wedge c^{*} = 0 \wedge 0 < \omega \wedge \Re{\mu} < 1 \wedge C_{1} \wedge C_{2} \wedge C_{3} \wedge C_{7} \wedge C_{11} -13. .. math:: p = q \wedge u < v \wedge 0 \leq b^{*} \wedge c^{*} = 0 \wedge 0 < \omega \wedge \Re{\mu} < 1 \wedge C_{1} \wedge C_{2} \wedge C_{3} \wedge C_{6} \wedge C_{11} -14. .. math:: p < q \wedge v < u \wedge 0 \leq b^{*} \wedge 0 \leq c^{*} \wedge C_{1} \wedge C_{2} \wedge C_{3} \wedge C_{4} \wedge C_{7} \wedge C_{11} \wedge C_{13} -15. .. math:: q < p \wedge u < v \wedge 0 \leq b^{*} \wedge 0 \leq c^{*} \wedge C_{1} \wedge C_{2} \wedge C_{3} \wedge C_{5} \wedge C_{6} \wedge C_{11} \wedge C_{13} -16. .. math:: q < p \wedge v < u \wedge 0 \leq b^{*} \wedge 0 \leq c^{*} \wedge C_{1} \wedge C_{2} \wedge C_{3} \wedge C_{5} \wedge C_{7} \wedge C_{8} \wedge C_{11} \wedge C_{13} \wedge C_{14} -17. .. math:: p < q \wedge u < v \wedge 0 \leq b^{*} \wedge 0 \leq c^{*} \wedge C_{1} \wedge C_{2} \wedge C_{3} \wedge C_{4} \wedge C_{6} \wedge C_{9} \wedge C_{11} \wedge C_{13} \wedge C_{14} -18. .. math:: t = 0 \wedge 0 < s \wedge 0 < b^{*} \wedge 0 < \phi \wedge C_{1} \wedge C_{2} \wedge C_{10} -19. .. math:: s = 0 \wedge 0 < t \wedge 0 < b^{*} \wedge \phi < 0 \wedge C_{1} \wedge C_{3} \wedge C_{10} -20. .. math:: n = 0 \wedge 0 < m \wedge 0 < c^{*} \wedge \phi < 0 \wedge C_{1} \wedge C_{2} \wedge C_{12} -21. .. math:: m = 0 \wedge 0 < n \wedge 0 < c^{*} \wedge 0 < \phi \wedge C_{1} \wedge C_{3} \wedge C_{12} -22. .. math:: s t = 0 \wedge 0 < b^{*} \wedge 0 < c^{*} \wedge C_{1} \wedge C_{2} \wedge C_{3} \wedge C_{10} \wedge C_{12} -23. .. math:: m n = 0 \wedge 0 < b^{*} \wedge 0 < c^{*} \wedge C_{1} \wedge C_{2} \wedge C_{3} \wedge C_{10} \wedge C_{12} -24. .. math:: p < m + n \wedge t = 0 \wedge \phi = 0 \wedge 0 < s \wedge 0 < b^{*} \wedge c^{*} < 0 \wedge \lvert{\operatorname{arg}\left(\omega\right)}\rvert < \pi \left(m + n - p + 1\right) \wedge C_{1} \wedge C_{2} \wedge C_{10} \wedge C_{14} \wedge C_{15} -25. .. math:: q < m + n \wedge s = 0 \wedge \phi = 0 \wedge 0 < t \wedge 0 < b^{*} \wedge c^{*} < 0 \wedge \lvert{\operatorname{arg}\left(\omega\right)}\rvert < \pi \left(m + n - q + 1\right) \wedge C_{1} \wedge C_{3} \wedge C_{10} \wedge C_{14} \wedge C_{15} -26. .. math:: p = q -1 \wedge t = 0 \wedge \phi = 0 \wedge 0 < s \wedge 0 < b^{*} \wedge 0 \leq c^{*} \wedge \pi c^{*} < \lvert{\operatorname{arg}\left(\omega\right)}\rvert \wedge C_{1} \wedge C_{2} \wedge C_{10} \wedge C_{14} \wedge C_{15} -27. .. math:: p = q + 1 \wedge s = 0 \wedge \phi = 0 \wedge 0 < t \wedge 0 < b^{*} \wedge 0 \leq c^{*} \wedge \pi c^{*} < \lvert{\operatorname{arg}\left(\omega\right)}\rvert \wedge C_{1} \wedge C_{3} \wedge C_{10} \wedge C_{14} \wedge C_{15} -28. .. math:: p < q -1 \wedge t = 0 \wedge \phi = 0 \wedge 0 < s \wedge 0 < b^{*} \wedge 0 \leq c^{*} \wedge \pi c^{*} < \lvert{\operatorname{arg}\left(\omega\right)}\rvert \wedge \lvert{\operatorname{arg}\left(\omega\right)}\rvert < \pi \left(m + n - p + 1\right) \wedge C_{1} \wedge C_{2} \wedge C_{10} \wedge C_{14} \wedge C_{15} -29. .. math:: q + 1 < p \wedge s = 0 \wedge \phi = 0 \wedge 0 < t \wedge 0 < b^{*} \wedge 0 \leq c^{*} \wedge \pi c^{*} < \lvert{\operatorname{arg}\left(\omega\right)}\rvert \wedge \lvert{\operatorname{arg}\left(\omega\right)}\rvert < \pi \left(m + n - q + 1 \right) \wedge C_{1} \wedge C_{3} \wedge C_{10} \wedge C_{14} \wedge C_{15} -30. .. math:: n = 0 \wedge \phi = 0 \wedge 0 < s + t \wedge 0 < m \wedge 0 < c^{*} \wedge b^{*} < 0 \wedge \lvert{\operatorname{arg}\left(\sigma\right)}\rvert < \pi \left(s + t - u + 1\right) \wedge C_{1} \wedge C_{2} \wedge C_{12} \wedge C_{14} \wedge C_{15} -31. .. math:: m = 0 \wedge \phi = 0 \wedge v < s + t \wedge 0 < n \wedge 0 < c^{*} \wedge b^{*} < 0 \wedge \lvert{\operatorname{arg}\left(\sigma\right)}\rvert < \pi \left(s + t - v + 1\right) \wedge C_{1} \wedge C_{3} \wedge C_{12} \wedge C_{14} \wedge C_{15} -32. .. math:: n = 0 \wedge \phi = 0 \wedge u = v -1 \wedge 0 < m \wedge 0 < c^{*} \wedge 0 \leq b^{*} \wedge \pi b^{*} < \lvert{\operatorname{arg}\left(\sigma\right)}\rvert \wedge \lvert{\operatorname{arg}\left(\sigma\right)}\rvert < \pi \left(b^{*} + 1\right) \wedge C_{1} \wedge C_{2} \wedge C_{12} \wedge C_{14} \wedge C_{15} -33. .. math:: m = 0 \wedge \phi = 0 \wedge u = v + 1 \wedge 0 < n \wedge 0 < c^{*} \wedge 0 \leq b^{*} \wedge \pi b^{*} < \lvert{\operatorname{arg}\left(\sigma\right)}\rvert \wedge \lvert{\operatorname{arg}\left(\sigma\right)}\rvert < \pi \left(b^{*} + 1\right) \wedge C_{1} \wedge C_{3} \wedge C_{12} \wedge C_{14} \wedge C_{15} -34. .. math:: n = 0 \wedge \phi = 0 \wedge u < v -1 \wedge 0 < m \wedge 0 < c^{*} \wedge 0 \leq b^{*} \wedge \pi b^{*} < \lvert{\operatorname{arg}\left(\sigma\right)}\rvert \wedge \lvert{\operatorname{arg}\left(\sigma\right)}\rvert < \pi \left(s + t - u + 1\right) \wedge C_{1} \wedge C_{2} \wedge C_{12} \wedge C_{14} \wedge C_{15} -35. .. math:: m = 0 \wedge \phi = 0 \wedge v + 1 < u \wedge 0 < n \wedge 0 < c^{*} \wedge 0 \leq b^{*} \wedge \pi b^{*} < \lvert{\operatorname{arg}\left(\sigma\right)}\rvert \wedge \lvert{\operatorname{arg}\left(\sigma\right)}\rvert < \pi \left(s + t - v + 1 \right) \wedge C_{1} \wedge C_{3} \wedge C_{12} \wedge C_{14} \wedge C_{15} -36. .. math:: C_{17} \wedge t = 0 \wedge u < s \wedge 0 < b^{*} \wedge C_{10} \wedge C_{1} \wedge C_{2} \wedge C_{3} -37. .. math:: C_{17} \wedge s = 0 \wedge v < t \wedge 0 < b^{*} \wedge C_{10} \wedge C_{1} \wedge C_{2} \wedge C_{3} -38. .. math:: C_{16} \wedge n = 0 \wedge p < m \wedge 0 < c^{*} \wedge C_{12} \wedge C_{1} \wedge C_{2} \wedge C_{3} -39. .. math:: C_{16} \wedge m = 0 \wedge q < n \wedge 0 < c^{*} \wedge C_{12} \wedge C_{1} \wedge C_{2} \wedge C_{3} - - -The Inverse Laplace Transform of a G-function -********************************************* - -The inverse laplace transform of a Meijer G-function can be expressed as -another G-function. This is a fairly versatile method for computing this -transform. However, I could not find the details in the literature, so I work -them out here. In [Luke1969]_, section 5.6.3, there is a formula for the inverse -Laplace transform of a G-function of argument `bz`, and convergence conditions -are also given. However, we need a formula for argument `bz^a` for rational `a`. - -We are asked to compute - -.. math :: - f(t) = \frac{1}{2\pi i} \int_{c-i\infty}^{c+i\infty} e^{zt} G(bz^a) \mathrm{d}z, - -for positive real `t`. Three questions arise: - -1. When does this integral converge? -2. How can we compute the integral? -3. When is our computation valid? - - -How to compute the integral -=========================== - -We shall work formally for now. Denote by `\Delta(s)` the product of gamma -functions appearing in the definition of `G`, so that - -.. math :: G(z) = \frac{1}{2\pi i} \int_L \Delta(s) z^s \mathrm{d}s. - -Thus - -.. math :: - f(t) = \frac{1}{(2\pi i)^2} \int_{c - i\infty}^{c + i\infty} \int_L - e^{zt} \Delta(s) b^s z^{as} \mathrm{d}s \mathrm{d}z. - -We interchange the order of integration to get - -.. math :: - f(t) = \frac{1}{2\pi i} \int_L b^s \Delta(s) - \int_{c-i\infty}^{c+i\infty} e^{zt} z^{as} \frac{\mathrm{d}z}{2\pi i} - \mathrm{d}s. - -The inner integral is easily seen to be -`\frac{1}{\Gamma(-as)} \frac{1}{t^{1+as}}`. (Using Cauchy's theorem and Jordan's -lemma deform the contour to run from `-\infty` to `-\infty`, encircling `0` once -in the negative sense. For `as` real and greater than one, -this contour can be pushed onto -the negative real axis and the integral is recognised as a product of a sine and -a gamma function. The formula is then proved using the functional equation of the -gamma function, and extended to the entire domain of convergence of the original -integral by appealing to analytic continuation.) -Hence we find - -.. math :: - f(t) = \frac{1}{t} \frac{1}{2\pi i} \int_L \Delta(s) \frac{1}{\Gamma(-as)} - \left(\frac{b}{t^a}\right)^s \mathrm{d}s, - -which is a so-called Fox H function (of argument `\frac{b}{t^a}`). For rational -`a`, this can be expressed as a Meijer G-function using the gamma function -multiplication theorem. - -When this computation is valid -============================== - -There are a number of obstacles in this computation. Interchange of integrals -is only valid if all integrals involved are absolutely convergent. In -particular the inner integral has to converge. Also, for our identification of -the final integral as a Fox H / Meijer G-function to be correct, the poles of -the newly obtained gamma function must be separated properly. - -It is easy to check that the inner integal converges absolutely for -`Re(as) < -1`. Thus the contour `L` has to run left of the line `Re(as) = -1`. -Under this condition, the poles of the newly-introduced gamma function are -separated properly. - -It remains to observe that the Meijer G-function is an analytic, unbranched -function of its parameters, and of the coefficient `b`. Hence so is `f(t)`. -Thus the final computation remains valid as long as the initial integral -converges, and if there exists a changed set of parameters where the computation -is valid. If we assume w.l.o.g. that `a > 0`, then the latter condition is -fulfilled if `G` converges along contours (2) or (3) of [Luke1969]_, -section 5.2, i.e. either `\delta >= \frac{a}{2}` or `p \ge 1, p \ge q`. - -When the integral exists -======================== - -Using [Luke1969]_, section 5.10, for any given meijer G-function we can find a -dominant term of the form `z^a e^{bz^c}` (although this expression might not be -the best possible, because of cancellation). - -We must thus investigate - -.. math :: \lim_{T \to \infty} \int_{c-iT}^{c+iT} - e^{zt} z^a e^{bz^c} \mathrm{d}z. - -(This principal value integral is the exact statement used in the Laplace -inversion theorem.) We write `z = c + i \tau`. Then -`arg(z) \to \pm \frac{\pi}{2}`, and so `e^{zt} \sim e^{it \tau}` (where `\sim` -shall always mean "asymptotically equivalent up to a positive real -multiplicative constant"). Also -`z^{x + iy} \sim |\tau|^x e^{i y \log{|\tau|}} e^{\pm x i \frac{\pi}{2}}.` - -Set `\omega_{\pm} = b e^{\pm i Re(c) \frac{\pi}{2}}`. We have three cases: - -1. `b=0` or `Re(c) \le 0`. - In this case the integral converges if `Re(a) \le -1`. -2. `b \ne 0`, `Im(c) = 0`, `Re(c) > 0`. - In this case the integral converges if `Re(\omega_{\pm}) < 0`. -3. `b \ne 0`, `Im(c) = 0`, `Re(c) > 0`, `Re(\omega_{\pm}) \le 0`, and at least - one of `Re(\omega_{\pm}) = 0`. - Here the same condition as in (1) applies. - -Implemented G-Function Formulae -******************************* - -An important part of the algorithm is a table expressing various functions -as Meijer G-functions. This is essentially a table of Mellin Transforms in -disguise. The following automatically generated table shows the formulae -currently implemented in SymPy. An entry "generated" means that the -corresponding G-function has a variable number of parameters. -This table is intended to shrink in future, when the algorithm's capabilities -of deriving new formulae improve. Of course it has to grow whenever a new class -of special functions is to be dealt with. - -.. automodule:: sympy.integrals.meijerint_doc diff --git a/dev-py3k/_sources/modules/integrals/integrals.txt b/dev-py3k/_sources/modules/integrals/integrals.txt deleted file mode 100644 index a6f9b0d5a1c..00000000000 --- a/dev-py3k/_sources/modules/integrals/integrals.txt +++ /dev/null @@ -1,117 +0,0 @@ -Integrals -========== - -.. module:: sympy.integrals - -The *integrals* module in SymPy implements methods to calculate definite and indefinite integrals of expressions. - -Principal method in this module is integrate() - - - integrate(f, x) returns the indefinite integral :math:`\int f\,dx` - - integrate(f, (x, a, b)) returns the definite integral :math:`\int_{a}^{b} f\,dx` - -Examples --------- -SymPy can integrate a vast array of functions. It can integrate polynomial functions:: - - >>> from sympy import * - >>> init_printing(use_unicode=False, wrap_line=False, no_global=True) - >>> x = Symbol('x') - >>> integrate(x**2 + x + 1, x) - 3 2 - x x - -- + -- + x - 3 2 - -Rational functions:: - - >>> integrate(x/(x**2+2*x+1), x) - 1 - log(x + 1) + ----- - x + 1 - - -Exponential-polynomial functions. Multiplicative combinations of polynomials and the functions exp, cos and sin can be integrated by hand using repeated integration by parts, which is an extremely tedious process. Happily, SymPy will deal with these integrals. - -:: - - >>> integrate(x**2 * exp(x) * cos(x), x) - 2 x 2 x x x - x *e *sin(x) x *e *cos(x) x e *sin(x) e *cos(x) - ------------ + ------------ - x*e *sin(x) + --------- - --------- - 2 2 2 2 - - - -even a few nonelementary integrals (in particular, some integrals involving the error function) can be evaluated:: - - >>> integrate(exp(-x**2)*erf(x), x) - ____ 2 - \/ pi *erf (x) - -------------- - 4 - - -Integral Transforms -------------------- - -.. module:: sympy.integrals.transforms - -SymPy has special support for definite integrals, and integral transforms. - -.. autofunction:: mellin_transform -.. autofunction:: inverse_mellin_transform -.. autofunction:: laplace_transform -.. autofunction:: inverse_laplace_transform -.. autofunction:: fourier_transform -.. autofunction:: inverse_fourier_transform -.. autofunction:: sine_transform -.. autofunction:: inverse_sine_transform -.. autofunction:: cosine_transform -.. autofunction:: inverse_cosine_transform -.. autofunction:: hankel_transform -.. autofunction:: inverse_hankel_transform - - -Internals ---------- - -There is a general method for calculating antiderivatives of elementary functions, called the Risch algorithm. The Risch algorithm is a decision procedure that can determine whether an elementary solution exists, and in that case calculate it. It can be extended to handle many nonelementary functions in addition to the elementary ones. - -SymPy currently uses a simplified version of the Risch algorithm, called the Risch-Norman algorithm. This algorithm is much faster, but may fail to find an antiderivative, although it is still very powerful. SymPy also uses pattern matching and heuristics to speed up evaluation of some types of integrals, e.g. polynomials. - -For non-elementary definite integrals, sympy uses so-called Meijer G-functions. -Details are described here: - -.. toctree:: - :maxdepth: 1 - - g-functions.rst - -API reference -------------- - -.. automethod:: sympy.integrals.integrate - -.. automethod:: sympy.integrals.line_integrate - -.. automethod:: sympy.integrals.deltaintegrate - -.. automethod:: sympy.integrals.ratint - -.. automethod:: sympy.integrals.heurisch - -.. automethod:: sympy.integrals.trigintegrate - -Class Integral represents an unevaluated integral and has some methods that help in the integration of an expression. - -.. autoclass:: sympy.integrals.Integral - :members: - - .. data:: is_commutative - - Returns whether all the free symbols in the integral are commutative. - -TODO and Bugs -------------- -There are still lots of functions that sympy does not know how to integrate. For bugs related to this module, see http://code.google.com/p/sympy/issues/list?q=label:Integration diff --git a/dev-py3k/_sources/modules/logic.txt b/dev-py3k/_sources/modules/logic.txt deleted file mode 100644 index 0750839dc1c..00000000000 --- a/dev-py3k/_sources/modules/logic.txt +++ /dev/null @@ -1,92 +0,0 @@ -Logic Module -=============== - -.. module:: sympy.logic - -Introduction ------------- - -The logic module for SymPy allows to form and manipulate logic expressions -using symbolic and boolean values. - -Forming logical expressions ---------------------------- - -You can build boolean expressions with the standard python operators ``&`` -(:class:`And`), ``|`` (:class:`Or`), ``~`` (:class:`Not`):: - - >>> from sympy import * - >>> x, y = symbols('x,y') - >>> y | (x & y) - Or(And(x, y), y) - >>> x | y - Or(x, y) - >>> ~x - Not(x) - -You can also form implications with ``>>`` and ``<<``:: - - >>> x >> y - Implies(x, y) - >>> x << y - Implies(y, x) - -Like most types in SymPy, Boolean expressions inherit from :class:`Basic`:: - - >>> (y & x).subs({x: True, y: True}) - True - >>> (x | y).atoms() - set([x, y]) - -Boolean functions ------------------------ - -.. autofunction:: sympy.logic.boolalg.to_cnf - -.. autoclass:: sympy.logic.boolalg.And - -.. autoclass:: sympy.logic.boolalg.Or - -.. autoclass:: sympy.logic.boolalg.Not - -.. autoclass:: sympy.logic.boolalg.Xor - -.. autoclass:: sympy.logic.boolalg.Nand - -.. autoclass:: sympy.logic.boolalg.Nor - -.. autoclass:: sympy.logic.boolalg.Implies - -.. autoclass:: sympy.logic.boolalg.Equivalent - -.. autoclass:: sympy.logic.boolalg.ITE - -Inference ---------- - -.. module: sympy.logic.inference - -This module implements some inference routines in propositional logic. - -The function satisfiable will test that a given boolean expression is satisfiable, -that is, you can assign values to the variables to make the sentence True. - -For example, the expression x & ~x is not satisfiable, since there are no values -for x that make this sentence True. On the other hand, (x | y) & (x | ~y) & (~x | y) -is satisfiable with both x and y being True. - - >>> from sympy.logic.inference import satisfiable - >>> from sympy import Symbol - >>> x = Symbol('x') - >>> y = Symbol('y') - >>> satisfiable(x & ~x) - False - >>> satisfiable((x | y) & (x | ~y) & (~x | y)) - {x: True, y: True} - -As you see, when a sentence is satisfiable, it returns a model that makes that -sentence True. If it is not satisfiable it will return False - -.. autofunction:: sympy.logic.inference.satisfiable - -.. TODO: write about CNF file format diff --git a/dev-py3k/_sources/modules/matrices/dense.txt b/dev-py3k/_sources/modules/matrices/dense.txt deleted file mode 100644 index f3d9cb22647..00000000000 --- a/dev-py3k/_sources/modules/matrices/dense.txt +++ /dev/null @@ -1,15 +0,0 @@ -Dense Matrices -============== - -Matrix Class Reference ----------------------- - -.. autoclass:: sympy.matrices.dense.MutableDenseMatrix - :members: - -ImmutableMatrix Class Reference -------------------------------- - -.. autoclass:: sympy.matrices.immutable.ImmutableMatrix - :members: - :noindex: diff --git a/dev-py3k/_sources/modules/matrices/expressions.txt b/dev-py3k/_sources/modules/matrices/expressions.txt deleted file mode 100644 index c2ab11ed1b0..00000000000 --- a/dev-py3k/_sources/modules/matrices/expressions.txt +++ /dev/null @@ -1,62 +0,0 @@ -Matrix Expressions -================== - -.. module:: sympy.matrices.expressions - -The Matrix expression module allows users to write down statements like - - >>> from sympy import MatrixSymbol, Matrix - >>> X = MatrixSymbol('X', 3, 3) - >>> Y = MatrixSymbol('Y', 3, 3) - >>> (X.T*X).I*Y - X^-1*X'^-1*Y - - >>> Matrix(X) - [X_00, X_01, X_02] - [X_10, X_11, X_12] - [X_20, X_21, X_22] - - >>> (X*Y)[1, 2] - X_10*Y_02 + X_11*Y_12 + X_12*Y_22 - -where ``X`` and ``Y`` are :class:`MatrixSymbol`'s rather than scalar symbols. - -Matrix Expressions Core Reference ---------------------------------- -.. autoclass:: MatrixExpr - :members: -.. autoclass:: MatrixSymbol - :members: -.. autoclass:: MatAdd - :members: -.. autoclass:: MatMul - :members: -.. autoclass:: MatPow - :members: -.. autoclass:: Inverse - :members: -.. autoclass:: Transpose - :members: -.. autoclass:: Trace - :members: -.. autoclass:: FunctionMatrix - :members: -.. autoclass:: Identity - :members: -.. autoclass:: ZeroMatrix - :members: - -Block Matrices --------------- - -Block matrices allow you to construct larger matrices out of smaller -sub-blocks. They can work with :class:`MatrixExpr` or -:class:`ImmutableMatrix` objects. - -.. module:: sympy.matrices.expressions.blockmatrix - -.. autoclass:: BlockMatrix - :members: -.. autoclass:: BlockDiagMatrix - :members: -.. autofunction:: block_collapse diff --git a/dev-py3k/_sources/modules/matrices/immutablematrices.txt b/dev-py3k/_sources/modules/matrices/immutablematrices.txt deleted file mode 100644 index bfe57172703..00000000000 --- a/dev-py3k/_sources/modules/matrices/immutablematrices.txt +++ /dev/null @@ -1,41 +0,0 @@ -Immutable Matrices -================== - -.. module:: sympy - -The standard :class:`Matrix` class in SymPy is mutable. This is important for -performance reasons but means that standard matrices can not interact well with -the rest of SymPy. This is because the :class:`Basic` object, from which most -SymPy classes inherit, is immutable. - -The mission of the :class:`ImmutableMatrix` class is to bridge the tension -between performance/mutability and safety/immutability. Immutable matrices can -do almost everything that normal matrices can do but they inherit from -:class:`Basic` and can thus interact more naturally with the rest of SymPy. -:class:`ImmutableMatrix` also inherits from :class:`MatrixExpr`, allowing it to -interact freely with SymPy's Matrix Expression module. - -You can turn any Matrix-like object into an :class:`ImmutableMatrix` by calling -the constructor - - >>> from sympy import Matrix, ImmutableMatrix - >>> M = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - >>> M[1, 1] = 0 - >>> IM = ImmutableMatrix(M) - >>> IM - [1, 2, 3] - [4, 0, 6] - [7, 8, 9] - >>> IM[1, 1] = 5 - Traceback (most recent call last): - ... - TypeError: Can not set values in Immutable Matrix. Use Matrix instead. - - -ImmutableMatrix Class Reference -------------------------------- - -.. module:: sympy.matrices.immutable - -.. autoclass:: ImmutableMatrix - :members: diff --git a/dev-py3k/_sources/modules/matrices/index.txt b/dev-py3k/_sources/modules/matrices/index.txt deleted file mode 100644 index c3c7ff76363..00000000000 --- a/dev-py3k/_sources/modules/matrices/index.txt +++ /dev/null @@ -1,18 +0,0 @@ -.. _matrices-docs: - -======== -Matrices -======== - -.. automodule:: sympy.matrices - -Contents: - -.. toctree:: - :maxdepth: 2 - - matrices.rst - dense.rst - sparse.rst - immutablematrices.rst - expressions.rst diff --git a/dev-py3k/_sources/modules/matrices/matrices.txt b/dev-py3k/_sources/modules/matrices/matrices.txt deleted file mode 100644 index aedd22e5027..00000000000 --- a/dev-py3k/_sources/modules/matrices/matrices.txt +++ /dev/null @@ -1,570 +0,0 @@ -Matrices (linear algebra) -========================= - -.. module:: sympy.matrices.matrices - -Creating Matrices ------------------ - -The linear algebra module is designed to be as simple as possible. First, we -import and declare our first Matrix object: - - >>> from sympy.interactive.printing import init_printing - >>> init_printing(use_unicode=False, wrap_line=False, no_global=True) - >>> from sympy.matrices import Matrix, eye, zeros, ones, diag, GramSchmidt - >>> M = Matrix([[1,0,0], [0,0,0]]); M - [1 0 0] - [ ] - [0 0 0] - >>> Matrix([M, (0, 0, -1)]) - [1 0 0 ] - [ ] - [0 0 0 ] - [ ] - [0 0 -1] - >>> Matrix([[1, 2, 3]]) - [1 2 3] - >>> Matrix([1, 2, 3]) - [1] - [ ] - [2] - [ ] - [3] - -In addition to creating a matrix from a list of appropriately-sized lists -and/or matrices, SymPy also supports more advanced methods of matrix creation -including a single list of values and dimension inputs: - - >>> Matrix(2, 3, [1, 2, 3, 4, 5, 6]) - [1 2 3] - [ ] - [4 5 6] - -More interesting (and useful), is the ability to use a 2-variable function -(or lambda) to create a matrix. Here we create an indicator function which -is 1 on the diagonal and then use it to make the identity matrix: - - >>> def f(i,j): - ... if i == j: - ... return 1 - ... else: - ... return 0 - ... - >>> Matrix(4, 4, f) - [1 0 0 0] - [ ] - [0 1 0 0] - [ ] - [0 0 1 0] - [ ] - [0 0 0 1] - -Finally let's use lambda to create a 1-line matrix with 1's in the even -permutation entries: - - >>> Matrix(3, 4, lambda i,j: 1 - (i+j) % 2) - [1 0 1 0] - [ ] - [0 1 0 1] - [ ] - [1 0 1 0] - -There are also a couple of special constructors for quick matrix construction: -``eye`` is the identity matrix, ``zeros`` and ``ones`` for matrices of all -zeros and ones, respectively, and ``diag`` to put matrices or elements along -the diagonal: - - >>> eye(4) - [1 0 0 0] - [ ] - [0 1 0 0] - [ ] - [0 0 1 0] - [ ] - [0 0 0 1] - >>> zeros(2) - [0 0] - [ ] - [0 0] - >>> zeros(2, 5) - [0 0 0 0 0] - [ ] - [0 0 0 0 0] - >>> ones(3) - [1 1 1] - [ ] - [1 1 1] - [ ] - [1 1 1] - >>> ones(1, 3) - [1 1 1] - >>> diag(1, Matrix([[1, 2], [3, 4]])) - [1 0 0] - [ ] - [0 1 2] - [ ] - [0 3 4] - - -Basic Manipulation ------------------- - -While learning to work with matrices, let's choose one where the entries are -readily identifiable. One useful thing to know is that while matrices are -2-dimensional, the storage is not and so it is allowable - though one should be -careful - to access the entries as if they were a 1-d list. - - >>> M = Matrix(2, 3, [1, 2, 3, 4, 5, 6]) - >>> M[4] - 5 - -Now, the more standard entry access is a pair of indices which will always -return the value at the corresponding row and column of the matrix: - - >>> M[1, 2] - 6 - >>> M[0, 0] - 1 - >>> M[1, 1] - 5 - -Since this is Python we're also able to slice submatrices; slices always -give a matrix in return, even if the dimension is 1 x 1:: - - >>> M[0:2, 0:2] - [1 2] - [ ] - [4 5] - >>> M[2:2, 2] - [] - >>> M[:, 2] - [3] - [ ] - [6] - >>> M[:1, 2] - [3] - -In the 2nd example above notice that the slice 2:2 gives an empty range. Note -also (in keeping with 0-based indexing of Python) the first row/column is 0. - -You cannot access rows or columns that are not present unless they -are in a slice: - - >>> M[:, 10] # the 10-th column (not there) - Traceback (most recent call last): - ... - IndexError: Index out of range: a[[0, 10]] - >>> M[:, 10:11] # the 10-th column (if there) - [] - >>> M[:, :10] # all columns up to the 10-th - [1 2 3] - [ ] - [4 5 6] - -Slicing an empty matrix works as long as you use a slice for the coordinate -that has no size: - - >>> Matrix(0, 3, [])[:, 1] - [] - -Slicing gives a copy of what is sliced, so modifications of one object -do not affect the other: - - >>> M2 = M[:, :] - >>> M2[0, 0] = 100 - >>> M[0, 0] == 100 - False - -Notice that changing M2 didn't change M. Since we can slice, we can also assign -entries: - - >>> M = Matrix(([1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16])) - >>> M - [1 2 3 4 ] - [ ] - [5 6 7 8 ] - [ ] - [9 10 11 12] - [ ] - [13 14 15 16] - >>> M[2,2] = M[0,3] = 0 - >>> M - [1 2 3 0 ] - [ ] - [5 6 7 8 ] - [ ] - [9 10 0 12] - [ ] - [13 14 15 16] - -as well as assign slices: - - >>> M = Matrix(([1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16])) - >>> M[2:,2:] = Matrix(2,2,lambda i,j: 0) - >>> M - [1 2 3 4] - [ ] - [5 6 7 8] - [ ] - [9 10 0 0] - [ ] - [13 14 0 0] - -All the standard arithmetic operations are supported: - - >>> M = Matrix(([1,2,3],[4,5,6],[7,8,9])) - >>> M - M - [0 0 0] - [ ] - [0 0 0] - [ ] - [0 0 0] - >>> M + M - [2 4 6 ] - [ ] - [8 10 12] - [ ] - [14 16 18] - >>> M * M - [30 36 42 ] - [ ] - [66 81 96 ] - [ ] - [102 126 150] - >>> M2 = Matrix(3,1,[1,5,0]) - >>> M*M2 - [11] - [ ] - [29] - [ ] - [47] - >>> M**2 - [30 36 42 ] - [ ] - [66 81 96 ] - [ ] - [102 126 150] - -As well as some useful vector operations: - - >>> M.row_del(0) - >>> M - [4 5 6] - [ ] - [7 8 9] - >>> M.col_del(1) - >>> M - [4 6] - [ ] - [7 9] - >>> v1 = Matrix([1,2,3]) - >>> v2 = Matrix([4,5,6]) - >>> v3 = v1.cross(v2) - >>> v1.dot(v2) - 32 - >>> v2.dot(v3) - 0 - >>> v1.dot(v3) - 0 - -Recall that the row_del() and col_del() operations don't return a value - they -simply change the matrix object. We can also ''glue'' together matrices of the -appropriate size: - - >>> M1 = eye(3) - >>> M2 = zeros(3, 4) - >>> M1.row_join(M2) - [1 0 0 0 0 0 0] - [ ] - [0 1 0 0 0 0 0] - [ ] - [0 0 1 0 0 0 0] - >>> M3 = zeros(4, 3) - >>> M1.col_join(M3) - [1 0 0] - [ ] - [0 1 0] - [ ] - [0 0 1] - [ ] - [0 0 0] - [ ] - [0 0 0] - [ ] - [0 0 0] - [ ] - [0 0 0] - - -Operations on entries ---------------------- - -We are not restricted to having multiplication between two matrices: - - >>> M = eye(3) - >>> 2*M - [2 0 0] - [ ] - [0 2 0] - [ ] - [0 0 2] - >>> 3*M - [3 0 0] - [ ] - [0 3 0] - [ ] - [0 0 3] - -but we can also apply functions to our matrix entries using applyfunc(). Here we'll declare a function that double any input number. Then we apply it to the 3x3 identity matrix: - - >>> f = lambda x: 2*x - >>> eye(3).applyfunc(f) - [2 0 0] - [ ] - [0 2 0] - [ ] - [0 0 2] - -One more useful matrix-wide entry application function is the substitution function. Let's declare a matrix with symbolic entries then substitute a value. Remember we can substitute anything - even another symbol!: - - >>> from sympy import Symbol - >>> x = Symbol('x') - >>> M = eye(3) * x - >>> M - [x 0 0] - [ ] - [0 x 0] - [ ] - [0 0 x] - >>> M.subs(x, 4) - [4 0 0] - [ ] - [0 4 0] - [ ] - [0 0 4] - >>> y = Symbol('y') - >>> M.subs(x, y) - [y 0 0] - [ ] - [0 y 0] - [ ] - [0 0 y] - - -Linear algebra --------------- - -Now that we have the basics out of the way, let's see what we can do with the -actual matrices. Of course, one of the first things that comes to mind is the -determinant: - - >>> M = Matrix(( [1, 2, 3], [3, 6, 2], [2, 0, 1] )) - >>> M.det() - -28 - >>> M2 = eye(3) - >>> M2.det() - 1 - >>> M3 = Matrix(( [1, 0, 0], [1, 0, 0], [1, 0, 0] )) - >>> M3.det() - 0 - -Another common operation is the inevers: In SymPy, this is computed by Gaussian -elimination by default (for dense matrices) but we can specify it be done by LU -decomposition as well: - - >>> M2.inv() - [1 0 0] - [ ] - [0 1 0] - [ ] - [0 0 1] - >>> M2.inv(method="LU") - [1 0 0] - [ ] - [0 1 0] - [ ] - [0 0 1] - >>> M.inv(method="LU") - [-3/14 1/14 1/2 ] - [ ] - [-1/28 5/28 -1/4] - [ ] - [ 3/7 -1/7 0 ] - >>> M * M.inv(method="LU") - [1 0 0] - [ ] - [0 1 0] - [ ] - [0 0 1] - -We can perform a QR factorization which is handy for solving systems: - - >>> A = Matrix([[1,1,1],[1,1,3],[2,3,4]]) - >>> Q, R = A.QRdecomposition() - >>> Q - [ ___ ___ ___] - [\/ 6 -\/ 3 -\/ 2 ] - [----- ------ ------] - [ 6 3 2 ] - [ ] - [ ___ ___ ___ ] - [\/ 6 -\/ 3 \/ 2 ] - [----- ------ ----- ] - [ 6 3 2 ] - [ ] - [ ___ ___ ] - [\/ 6 \/ 3 ] - [----- ----- 0 ] - [ 3 3 ] - >>> R - [ ___ ] - [ ___ 4*\/ 6 ___] - [\/ 6 ------- 2*\/ 6 ] - [ 3 ] - [ ] - [ ___ ] - [ \/ 3 ] - [ 0 ----- 0 ] - [ 3 ] - [ ] - [ ___ ] - [ 0 0 \/ 2 ] - >>> Q*R - [1 1 1] - [ ] - [1 1 3] - [ ] - [2 3 4] - - -In addition to the solvers in the solver.py file, we can solve the system Ax=b -by passing the b vector to the matrix A's LUsolve function. Here we'll cheat a -little choose A and x then multiply to get b. Then we can solve for x and check -that it's correct: - - >>> A = Matrix([ [2, 3, 5], [3, 6, 2], [8, 3, 6] ]) - >>> x = Matrix(3,1,[3,7,5]) - >>> b = A*x - >>> soln = A.LUsolve(b) - >>> soln - [3] - [ ] - [7] - [ ] - [5] - -There's also a nice Gram-Schmidt orthogonalizer which will take a set of -vectors and orthogonalize then with respect to another another. There is an -optional argument which specifies whether or not the output should also be -normalized, it defaults to False. Let's take some vectors and orthogonalize -them - one normalized and one not: - - >>> L = [Matrix([2,3,5]), Matrix([3,6,2]), Matrix([8,3,6])] - >>> out1 = GramSchmidt(L) - >>> out2 = GramSchmidt(L, True) - -Let's take a look at the vectors: - - >>> for i in out1: - ... print(i) - ... - [2] - [3] - [5] - [ 23/19] - [ 63/19] - [-47/19] - [ 1692/353] - [-1551/706] - [ -423/706] - >>> for i in out2: - ... print(i) - ... - [ sqrt(38)/19] - [3*sqrt(38)/38] - [5*sqrt(38)/38] - [ 23*sqrt(6707)/6707] - [ 63*sqrt(6707)/6707] - [-47*sqrt(6707)/6707] - [ 12*sqrt(706)/353] - [-11*sqrt(706)/706] - [ -3*sqrt(706)/706] - -We can spot-check their orthogonality with dot() and their normality with -norm(): - - >>> out1[0].dot(out1[1]) - 0 - >>> out1[0].dot(out1[2]) - 0 - >>> out1[1].dot(out1[2]) - 0 - >>> out2[0].norm() - 1 - >>> out2[1].norm() - 1 - >>> out2[2].norm() - 1 - -So there is quite a bit that can be done with the module including eigenvalues, -eigenvectors, nullspace calculation, cofactor expansion tools, and so on. From -here one might want to look over the matrices.py file for all functionality. - -MatrixBase Class Reference --------------------------- -.. autoclass:: MatrixBase - :members: - -Matrix Exceptions Reference ---------------------------- - -.. autoclass:: MatrixError - -.. autoclass:: ShapeError - -.. autoclass:: NonSquareMatrixError - - -Matrix Functions Reference --------------------------- - -.. autofunction:: classof - -.. autofunction:: sympy.matrices.dense.matrix_multiply_elementwise - -.. autofunction:: sympy.matrices.dense.zeros - -.. autofunction:: sympy.matrices.dense.ones - -.. autofunction:: sympy.matrices.dense.eye - -.. autofunction:: sympy.matrices.dense.diag - -.. autofunction:: sympy.matrices.dense.jordan_cell - -.. autofunction:: sympy.matrices.dense.hessian - -.. autofunction:: sympy.matrices.dense.GramSchmidt - -.. autofunction:: sympy.matrices.dense.wronskian - -.. autofunction:: sympy.matrices.dense.casoratian - -.. autofunction:: sympy.matrices.dense.randMatrix - -Numpy Utility Functions Reference ---------------------------------- - -.. autofunction:: sympy.matrices.dense.list2numpy - -.. autofunction:: sympy.matrices.dense.matrix2numpy - -.. autofunction:: sympy.matrices.dense.symarray - -.. autofunction:: sympy.matrices.dense.rot_axis1 - -.. autofunction:: sympy.matrices.dense.rot_axis2 - -.. autofunction:: sympy.matrices.dense.rot_axis3 - -.. autofunction:: a2idx diff --git a/dev-py3k/_sources/modules/matrices/sparse.txt b/dev-py3k/_sources/modules/matrices/sparse.txt deleted file mode 100644 index 734476fa1c9..00000000000 --- a/dev-py3k/_sources/modules/matrices/sparse.txt +++ /dev/null @@ -1,16 +0,0 @@ -Sparse Matrices -=============== - -.. module:: sympy.matrices.sparse - -SparseMatrix Class Reference ----------------------------- -.. autoclass:: SparseMatrix - :members: -.. autoclass:: MutableSparseMatrix - :members: - -ImmutableSparseMatrix Class Reference -------------------------------------- -.. autoclass:: sympy.matrices.immutable.ImmutableSparseMatrix - :members: diff --git a/dev-py3k/_sources/modules/mpmath/basics.txt b/dev-py3k/_sources/modules/mpmath/basics.txt deleted file mode 100644 index 18cab996a74..00000000000 --- a/dev-py3k/_sources/modules/mpmath/basics.txt +++ /dev/null @@ -1,227 +0,0 @@ -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/dev-py3k/_sources/modules/mpmath/calculus/approximation.txt b/dev-py3k/_sources/modules/mpmath/calculus/approximation.txt deleted file mode 100644 index c6b530969d4..00000000000 --- a/dev-py3k/_sources/modules/mpmath/calculus/approximation.txt +++ /dev/null @@ -1,23 +0,0 @@ -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/dev-py3k/_sources/modules/mpmath/calculus/differentiation.txt b/dev-py3k/_sources/modules/mpmath/calculus/differentiation.txt deleted file mode 100644 index 5660cf16977..00000000000 --- a/dev-py3k/_sources/modules/mpmath/calculus/differentiation.txt +++ /dev/null @@ -1,19 +0,0 @@ -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/dev-py3k/_sources/modules/mpmath/calculus/index.txt b/dev-py3k/_sources/modules/mpmath/calculus/index.txt deleted file mode 100644 index d491dbb0fad..00000000000 --- a/dev-py3k/_sources/modules/mpmath/calculus/index.txt +++ /dev/null @@ -1,13 +0,0 @@ -Numerical calculus -================== - -.. toctree:: - :maxdepth: 2 - - polynomials.rst - optimization.rst - sums_limits.rst - differentiation.rst - integration.rst - odes.rst - approximation.rst diff --git a/dev-py3k/_sources/modules/mpmath/calculus/integration.txt b/dev-py3k/_sources/modules/mpmath/calculus/integration.txt deleted file mode 100644 index f260b3f60e2..00000000000 --- a/dev-py3k/_sources/modules/mpmath/calculus/integration.txt +++ /dev/null @@ -1,31 +0,0 @@ -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/dev-py3k/_sources/modules/mpmath/calculus/odes.txt b/dev-py3k/_sources/modules/mpmath/calculus/odes.txt deleted file mode 100644 index 7c2ae83b090..00000000000 --- a/dev-py3k/_sources/modules/mpmath/calculus/odes.txt +++ /dev/null @@ -1,7 +0,0 @@ -Ordinary differential equations -------------------------------- - -Solving the ODE initial value problem (``odefun``) -.................................................. - -.. autofunction:: mpmath.odefun diff --git a/dev-py3k/_sources/modules/mpmath/calculus/optimization.txt b/dev-py3k/_sources/modules/mpmath/calculus/optimization.txt deleted file mode 100644 index b42b8efae8c..00000000000 --- a/dev-py3k/_sources/modules/mpmath/calculus/optimization.txt +++ /dev/null @@ -1,29 +0,0 @@ -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/dev-py3k/_sources/modules/mpmath/calculus/polynomials.txt b/dev-py3k/_sources/modules/mpmath/calculus/polynomials.txt deleted file mode 100644 index 304fdd99775..00000000000 --- a/dev-py3k/_sources/modules/mpmath/calculus/polynomials.txt +++ /dev/null @@ -1,15 +0,0 @@ -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/dev-py3k/_sources/modules/mpmath/calculus/sums_limits.txt b/dev-py3k/_sources/modules/mpmath/calculus/sums_limits.txt deleted file mode 100644 index f8c932bccc5..00000000000 --- a/dev-py3k/_sources/modules/mpmath/calculus/sums_limits.txt +++ /dev/null @@ -1,58 +0,0 @@ -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 diff --git a/dev-py3k/_sources/modules/mpmath/contexts.txt b/dev-py3k/_sources/modules/mpmath/contexts.txt deleted file mode 100644 index 7a84b2bd6aa..00000000000 --- a/dev-py3k/_sources/modules/mpmath/contexts.txt +++ /dev/null @@ -1,306 +0,0 @@ -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.2360679774997898 - >>> fp.sqrt(-5) - 2.2360679774997898j - >>> fp.sin(10) - -0.54402111088936977 - >>> fp.power(-1, 0.25) - (0.70710678118654757+0.70710678118654746j) - >>> (-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/dev-py3k/_sources/modules/mpmath/functions/bessel.txt b/dev-py3k/_sources/modules/mpmath/functions/bessel.txt deleted file mode 100644 index 19def1b1793..00000000000 --- a/dev-py3k/_sources/modules/mpmath/functions/bessel.txt +++ /dev/null @@ -1,185 +0,0 @@ -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/dev-py3k/_sources/modules/mpmath/functions/constants.txt b/dev-py3k/_sources/modules/mpmath/functions/constants.txt deleted file mode 100644 index 42c5de7ac7c..00000000000 --- a/dev-py3k/_sources/modules/mpmath/functions/constants.txt +++ /dev/null @@ -1,85 +0,0 @@ -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/dev-py3k/_sources/modules/mpmath/functions/elliptic.txt b/dev-py3k/_sources/modules/mpmath/functions/elliptic.txt deleted file mode 100644 index f8d2e1d6d2e..00000000000 --- a/dev-py3k/_sources/modules/mpmath/functions/elliptic.txt +++ /dev/null @@ -1,96 +0,0 @@ -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/dev-py3k/_sources/modules/mpmath/functions/expintegrals.txt b/dev-py3k/_sources/modules/mpmath/functions/expintegrals.txt deleted file mode 100644 index fe01ca4e4b7..00000000000 --- a/dev-py3k/_sources/modules/mpmath/functions/expintegrals.txt +++ /dev/null @@ -1,104 +0,0 @@ -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/dev-py3k/_sources/modules/mpmath/functions/gamma.txt b/dev-py3k/_sources/modules/mpmath/functions/gamma.txt deleted file mode 100644 index db4a679fa53..00000000000 --- a/dev-py3k/_sources/modules/mpmath/functions/gamma.txt +++ /dev/null @@ -1,112 +0,0 @@ -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/dev-py3k/_sources/modules/mpmath/functions/hyperbolic.txt b/dev-py3k/_sources/modules/mpmath/functions/hyperbolic.txt deleted file mode 100644 index e48b7e795a7..00000000000 --- a/dev-py3k/_sources/modules/mpmath/functions/hyperbolic.txt +++ /dev/null @@ -1,56 +0,0 @@ -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/dev-py3k/_sources/modules/mpmath/functions/hypergeometric.txt b/dev-py3k/_sources/modules/mpmath/functions/hypergeometric.txt deleted file mode 100644 index 4ac3a721adf..00000000000 --- a/dev-py3k/_sources/modules/mpmath/functions/hypergeometric.txt +++ /dev/null @@ -1,115 +0,0 @@ -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/dev-py3k/_sources/modules/mpmath/functions/index.txt b/dev-py3k/_sources/modules/mpmath/functions/index.txt deleted file mode 100644 index 9e637a8c1f5..00000000000 --- a/dev-py3k/_sources/modules/mpmath/functions/index.txt +++ /dev/null @@ -1,21 +0,0 @@ -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/dev-py3k/_sources/modules/mpmath/functions/numtheory.txt b/dev-py3k/_sources/modules/mpmath/functions/numtheory.txt deleted file mode 100644 index 65c628ecdd8..00000000000 --- a/dev-py3k/_sources/modules/mpmath/functions/numtheory.txt +++ /dev/null @@ -1,80 +0,0 @@ -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) - - -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/dev-py3k/_sources/modules/mpmath/functions/orthogonal.txt b/dev-py3k/_sources/modules/mpmath/functions/orthogonal.txt deleted file mode 100644 index 27791ca54b8..00000000000 --- a/dev-py3k/_sources/modules/mpmath/functions/orthogonal.txt +++ /dev/null @@ -1,79 +0,0 @@ -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/dev-py3k/_sources/modules/mpmath/functions/powers.txt b/dev-py3k/_sources/modules/mpmath/functions/powers.txt deleted file mode 100644 index a5820d2b0cc..00000000000 --- a/dev-py3k/_sources/modules/mpmath/functions/powers.txt +++ /dev/null @@ -1,85 +0,0 @@ -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/dev-py3k/_sources/modules/mpmath/functions/qfunctions.txt b/dev-py3k/_sources/modules/mpmath/functions/qfunctions.txt deleted file mode 100644 index 4b1ae05ef21..00000000000 --- a/dev-py3k/_sources/modules/mpmath/functions/qfunctions.txt +++ /dev/null @@ -1,29 +0,0 @@ -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/dev-py3k/_sources/modules/mpmath/functions/trigonometric.txt b/dev-py3k/_sources/modules/mpmath/functions/trigonometric.txt deleted file mode 100644 index 1c2b87e3a97..00000000000 --- a/dev-py3k/_sources/modules/mpmath/functions/trigonometric.txt +++ /dev/null @@ -1,117 +0,0 @@ -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/dev-py3k/_sources/modules/mpmath/functions/zeta.txt b/dev-py3k/_sources/modules/mpmath/functions/zeta.txt deleted file mode 100644 index d797ce9ec1e..00000000000 --- a/dev-py3k/_sources/modules/mpmath/functions/zeta.txt +++ /dev/null @@ -1,104 +0,0 @@ -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/dev-py3k/_sources/modules/mpmath/general.txt b/dev-py3k/_sources/modules/mpmath/general.txt deleted file mode 100644 index 2d7e930926b..00000000000 --- a/dev-py3k/_sources/modules/mpmath/general.txt +++ /dev/null @@ -1,228 +0,0 @@ -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:`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/dev-py3k/_sources/modules/mpmath/identification.txt b/dev-py3k/_sources/modules/mpmath/identification.txt deleted file mode 100644 index a66bfa9cd73..00000000000 --- a/dev-py3k/_sources/modules/mpmath/identification.txt +++ /dev/null @@ -1,31 +0,0 @@ -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/dev-py3k/_sources/modules/mpmath/index.txt b/dev-py3k/_sources/modules/mpmath/index.txt deleted file mode 100644 index 7f2e6748635..00000000000 --- a/dev-py3k/_sources/modules/mpmath/index.txt +++ /dev/null @@ -1,53 +0,0 @@ -.. 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 :ref:`genindex`, 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/dev-py3k/_sources/modules/mpmath/matrices.txt b/dev-py3k/_sources/modules/mpmath/matrices.txt deleted file mode 100644 index a64e6d0be80..00000000000 --- a/dev-py3k/_sources/modules/mpmath/matrices.txt +++ /dev/null @@ -1,375 +0,0 @@ -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] - -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/dev-py3k/_sources/modules/mpmath/plotting.txt b/dev-py3k/_sources/modules/mpmath/plotting.txt deleted file mode 100644 index 2e68307cbd3..00000000000 --- a/dev-py3k/_sources/modules/mpmath/plotting.txt +++ /dev/null @@ -1,32 +0,0 @@ -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/dev-py3k/_sources/modules/mpmath/references.txt b/dev-py3k/_sources/modules/mpmath/references.txt deleted file mode 100644 index e65239cc863..00000000000 --- a/dev-py3k/_sources/modules/mpmath/references.txt +++ /dev/null @@ -1,50 +0,0 @@ -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 - -.. [Bailey] D H Bailey. "Tanh-Sinh High-Precision Quadrature", http://crd.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/P115.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 - -.. [WolframFunctions] Wolfram Research, Inc. *The Wolfram Functions Site*. http://functions.wolfram.com/ diff --git a/dev-py3k/_sources/modules/mpmath/setup.txt b/dev-py3k/_sources/modules/mpmath/setup.txt deleted file mode 100644 index e8c1d4ec2a7..00000000000 --- a/dev-py3k/_sources/modules/mpmath/setup.txt +++ /dev/null @@ -1,178 +0,0 @@ -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_12.2/ -(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 - '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/dev-py3k/_sources/modules/mpmath/technical.txt b/dev-py3k/_sources/modules/mpmath/technical.txt deleted file mode 100644 index c36c6cc92de..00000000000 --- a/dev-py3k/_sources/modules/mpmath/technical.txt +++ /dev/null @@ -1,159 +0,0 @@ -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 `_):: - - >>> 0.1 - 0.10000000000000001 - -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/dev-py3k/_sources/modules/ntheory.txt b/dev-py3k/_sources/modules/ntheory.txt deleted file mode 100644 index 2f1c3a8d78e..00000000000 --- a/dev-py3k/_sources/modules/ntheory.txt +++ /dev/null @@ -1,98 +0,0 @@ -Number Theory -==================== - -.. module:: sympy.ntheory.generate - -Ntheory Class Reference ------------------------ -.. autoclass:: Sieve - :members: - -Ntheory Functions Reference ---------------------------- - -.. autofunction:: prime - -.. autofunction:: primepi - -.. autofunction:: nextprime - -.. autofunction:: prevprime - -.. autofunction:: primerange - -.. autofunction:: randprime - -.. autofunction:: primorial - -.. autofunction:: cycle_length - -.. module:: sympy.ntheory.factor_ - -.. autofunction:: smoothness - -.. autofunction:: smoothness_p - -.. autofunction:: trailing - -.. autofunction:: multiplicity - -.. autofunction:: perfect_power - -.. autofunction:: pollard_rho - -.. autofunction:: pollard_pm1 - -.. autofunction:: factorint - -.. autofunction:: primefactors - -.. autofunction:: divisors - -.. autofunction:: divisor_count - -.. autofunction:: totient - -.. module:: sympy.ntheory.modular - -.. autofunction:: symmetric_residue - -.. autofunction:: crt - -.. autofunction:: crt1 - -.. autofunction:: crt2 - -.. autofunction:: solve_congruence - -.. module:: sympy.ntheory.multinomial - -.. autofunction:: binomial_coefficients - -.. autofunction:: binomial_coefficients_list - -.. autofunction:: multinomial_coefficients - -.. autofunction:: multinomial_coefficients_iterator - -.. module:: sympy.ntheory.partitions_ - -.. autofunction:: npartitions - -.. module:: sympy.ntheory.primetest - -.. autofunction:: mr - -.. autofunction:: isprime - -.. module:: sympy.ntheory.residue_ntheory - -.. autofunction:: n_order - -.. autofunction:: is_primitive_root - -.. autofunction:: is_quad_residue - -.. autofunction:: legendre_symbol - -.. autofunction:: jacobi_symbol diff --git a/dev-py3k/_sources/modules/parsing.txt b/dev-py3k/_sources/modules/parsing.txt deleted file mode 100644 index 55e5e72d13a..00000000000 --- a/dev-py3k/_sources/modules/parsing.txt +++ /dev/null @@ -1,32 +0,0 @@ -Parsing input -============= - -Parsing Functions Reference ---------------------------- - -.. autofunction:: sympy.parsing.sympy_parser.parse_expr - -.. autofunction:: sympy.parsing.sympy_tokenize.printtoken - -.. autofunction:: sympy.parsing.sympy_tokenize.tokenize - -.. autofunction:: sympy.parsing.sympy_tokenize.untokenize - -.. autofunction:: sympy.parsing.sympy_tokenize.generate_tokens - -.. autofunction:: sympy.parsing.sympy_tokenize.group - -.. autofunction:: sympy.parsing.sympy_tokenize.any - -.. autofunction:: sympy.parsing.sympy_tokenize.maybe - -.. autofunction:: sympy.parsing.maxima.parse_maxima - -.. autofunction:: sympy.parsing.mathematica.mathematica - -Parsing Exceptions Reference ----------------------------- - -.. autoclass:: sympy.parsing.sympy_tokenize.TokenError - -.. autoclass:: sympy.parsing.sympy_tokenize.StopTokenizing diff --git a/dev-py3k/_sources/modules/physics/gaussopt.txt b/dev-py3k/_sources/modules/physics/gaussopt.txt deleted file mode 100644 index 36743fa0418..00000000000 --- a/dev-py3k/_sources/modules/physics/gaussopt.txt +++ /dev/null @@ -1,6 +0,0 @@ -=============== -Gaussian Optics -=============== - -.. automodule:: sympy.physics.gaussopt - :members: diff --git a/dev-py3k/_sources/modules/physics/hydrogen.txt b/dev-py3k/_sources/modules/physics/hydrogen.txt deleted file mode 100644 index 368d3dfdf47..00000000000 --- a/dev-py3k/_sources/modules/physics/hydrogen.txt +++ /dev/null @@ -1,6 +0,0 @@ -====================== -Hydrogen Wavefunctions -====================== - -.. automodule:: sympy.physics.hydrogen - :members: diff --git a/dev-py3k/_sources/modules/physics/index.txt b/dev-py3k/_sources/modules/physics/index.txt deleted file mode 100644 index bfefeea8f65..00000000000 --- a/dev-py3k/_sources/modules/physics/index.txt +++ /dev/null @@ -1,25 +0,0 @@ -.. _physics-docs: - -============== -Physics Module -============== - -.. automodule:: sympy.physics - -Contents -======== - -.. toctree:: - :maxdepth: 3 - - gaussopt.rst - hydrogen.rst - matrices.rst - paulialgebra.rst - qho_1d.rst - sho.rst - secondquant.rst - wigner.rst - units.rst - mechanics/index.rst - quantum/index.rst diff --git a/dev-py3k/_sources/modules/physics/matrices.txt b/dev-py3k/_sources/modules/physics/matrices.txt deleted file mode 100644 index 8d70f309946..00000000000 --- a/dev-py3k/_sources/modules/physics/matrices.txt +++ /dev/null @@ -1,6 +0,0 @@ -======== -Matrices -======== - -.. automodule:: sympy.physics.matrices - :members: diff --git a/dev-py3k/_sources/modules/physics/mechanics/advanced.txt b/dev-py3k/_sources/modules/physics/mechanics/advanced.txt deleted file mode 100644 index cc27e88f8ce..00000000000 --- a/dev-py3k/_sources/modules/physics/mechanics/advanced.txt +++ /dev/null @@ -1,206 +0,0 @@ -============================================================================ -Potential Issues/Advanced Topics/Future Features in Physics/Mechanics Module -============================================================================ - -This document will describe some of the more advanced functionality that this -module offers but which is not part of the "official" interface. Here, some of -the features that will be implemented in the future will also be covered, along -with unanswered questions about proper functionality. Also, common problems -will be discussed, along with some solutions. - -Common Issues -============= -Here issues with numerically integrating code, choice of `dynamicsymbols` for -coordinate and speed representation, printing, differentiating, and -substitution will occur. - -Numerically Integrating Code ----------------------------- -See Future Features: Code Output - -Choice of Coordinates and Speeds --------------------------------- -The Kane object is set up with the assumption that the generalized speeds are -not the same symbol as the time derivatives of the generalized coordinates. -This isn't to say that they can't be the same, just that they have to have a -different symbol. If you did this: :: - - >> KM.coords([q1, q2, q3]) - >> KM.speeds([q1d, q2d, q3d]) - -Your code would not work. Currently, kinematic differential equations are -required to be provided. It is at this point that we hope the user will -discover they should not attempt the behavior shown in the code above. - -This behavior might not be true for other methods of forming the equations of -motion though. - -Printing --------- -The default printing options are to use sorting for ``Vector`` and ``Dyad`` -measure numbers, and have unsorted output from the ``mprint``, ``mpprint``, and -``mlatex`` functions. If you are printing something large, please use one of -those functions, as the sorting can increase printing time from seconds to -minutes. - -Differentiating ---------------- -Differentiation of very large expressions can take some time in SymPy; it is -possible for large expressions to take minutes for the derivative to be -evaluated. This will most commonly come up in linearization. - -Substitution ------------- -Substitution into large expressions can be slow, and take a few minutes. - -Linearization -------------- -Currently, the ``Kane`` object's ``linearize`` method doesn't support cases -where there are non-coordinate, non-speed dynamic symbols outside of the -"dynamic equations". It also does not support cases where time derivatives of -these types of dynamic symbols show up. This means if you have kinematic -differential equations which have a non-coordinate, non-speed dynamic symbol, -it will not work. It also means if you have defined a system parameter (say a -length or distance or mass) as a dynamic symbol, its time derivative is likely -to show up in the dynamic equations, and this will prevent linearization. - -Acceleration of Points ----------------------- -At a minimum, points need to have their velocities defined, as the acceleration -can be calculated by taking the time derivative of the velocity in the same -frame. If the 1 point or 2 point theorems were used to compute the velocity, -the time derivative of the velocity expression will most likely be more complex -than if you were to use the acceleration level 1 point and 2 point theorems. -Using the acceleration level methods can result in shorted expressions at this -point, which will result in shorter expressions later (such as when forming -Kane's equations). - - -Advanced Interfaces -=================== - -Here we will cover advanced options in: ``ReferenceFrame``, ``dynamicsymbols``, -and some associated functionality. - -ReferenceFrame --------------- -``ReferenceFrame`` is shown as having a ``.name`` attribute and ``.x``, ``.y``, -and ``.z`` attributes for accessing the basis vectors, as well as a fairly -rigidly defined print output. If you wish to have a different set of indices -defined, there is an option for this. This will also require a different -interface for accessing the basis vectors. :: - - >>> from sympy.physics.mechanics import ReferenceFrame, mprint, mpprint, mlatex - >>> N = ReferenceFrame('N', indices=['i', 'j', 'k']) - >>> N['i'] - N['i'] - >>> N.x - N['i'] - >>> mlatex(N.x) - '\\mathbf{\\hat{n}_{i}}' - -Also, the latex output can have custom strings; rather than just indices -though, the entirety of each basis vector can be specified. The custom latex -strings can occur without custom indices, and also overwrites the latex string -that would be used if there were custom indices. :: - - >>> from sympy.physics.mechanics import ReferenceFrame, mlatex - >>> N = ReferenceFrame('N', latexs=['n1','\mathbf{n}_2','cat']) - >>> mlatex(N.x) - 'n1' - >>> mlatex(N.y) - '\\mathbf{n}_2' - >>> mlatex(N.z) - 'cat' - -dynamicsymbols --------------- -The ``dynamicsymbols`` function also has 'hidden' functionality; the variable -which is associated with time can be changed, as well as the notation for -printing derivatives. :: - - >>> from sympy import symbols - >>> from sympy.physics.mechanics import dynamicsymbols, mprint - >>> q1 = dynamicsymbols('q1') - >>> q1 - q1(t) - >>> dynamicsymbols._t = symbols('T') - >>> q2 = dynamicsymbols('q2') - >>> q2 - q2(T) - >>> q1 - q1(t) - >>> q1d = dynamicsymbols('q1', 1) - >>> mprint(q1d) - q1' - >>> dynamicsymbols._str = 'd' - >>> mprint(q1d) - q1d - >>> dynamicsymbols._str = '\'' - >>> dynamicsymbols._t = symbols('t') - - -Note that only dynamic symbols created after the change are different. The same -is not true for the `._str` attribute; this affects the printing output only, -so dynamic symbols created before or after will print the same way. - -Also note that ``Vector``'s ``.dt`` method uses the ``._t`` attribute of -``dynamicsymbols``, along with a number of other important functions and -methods. Don't mix and match symbols representing time. - -Advanced Functionality ----------------------- -Remember that the ``Kane`` object supports bodies which have time-varying -masses and inertias, although this functionality isn't completely compatible -with the linearization method. - -Operators were discussed earlier as a potential way to do mathematical -operations on ``Vector`` and ``Dyad`` objects. The majority of the code in this -module is actually coded with them, as it can (subjectively) result in cleaner, -shorter, more readable code. If using this interface in your code, remember to -take care and use parentheses; the default order of operations in Python -results in addition occurring before some of the vector products, so use -parentheses liberally. - - -Future Features -=============== - -This will cover the planned features to be added to this submodule. - -Code Output ------------ -A function for generating code output for numerical integration is the highest -priority feature to implement next. There are a number of considerations here. - -Code output for C (using the GSL libraries), Fortran 90 (using LSODA), MATLAB, -and SciPy is the goal. Things to be considered include: use of ``cse`` on large -expressions for MATLAB and SciPy, which are interpretive. It is currently unclear -whether compiled languages will benefit from common subexpression elimination, -especially considering that it is a common part of compiler optimization, and -there can be a significant time penalty when calling ``cse``. - -Care needs to be taken when constructing the strings for these expressions, as -well as handling of input parameters, and other dynamic symbols. How to deal -with output quantities when integrating also needs to be decided, with the -potential for multiple options being considered. - -Additional Options on Initialization of Kane, RigidBody, and Particle ---------------------------------------------------------------------- -This would allow a user to specify all relevant information using keyword -arguments when creating these objects. This is fairly clear for ``RigidBody`` -and ``Point``. For ``Kane``, everything but the force and body lists will be -able to be entered, as computation of Fr and Fr* can take a while, and produce -an output. - -Additional Methods for RigidBody and Particle ---------------------------------------------- -For ``RigidBody`` and ``Particle`` (not all methods for ``Particle`` though), -add methods for getting: momentum, angular momentum, and kinetic energy. -Additionally, adding a attribute and method for defining potential energy would -allow for a total energy method/property. - -Also possible is including the method which creates a transformation matrix for -3D animations; this would require a "reference orientation" for a camera as -well as a "reference point" for distance to the camera. Development of this -could also be tied into code output. diff --git a/dev-py3k/_sources/modules/physics/mechanics/api/essential.txt b/dev-py3k/_sources/modules/physics/mechanics/api/essential.txt deleted file mode 100644 index 4828a1bdc37..00000000000 --- a/dev-py3k/_sources/modules/physics/mechanics/api/essential.txt +++ /dev/null @@ -1,23 +0,0 @@ -================================= -Essential Components (Docstrings) -================================= - -ReferenceFrame -============== - -.. autoclass:: sympy.physics.mechanics.essential.ReferenceFrame - :members: - - -Vector -====== - -.. autoclass:: sympy.physics.mechanics.essential.Vector - :members: - - -Dyadic -====== - -.. autoclass:: sympy.physics.mechanics.essential.Dyadic - :members: diff --git a/dev-py3k/_sources/modules/physics/mechanics/api/functions.txt b/dev-py3k/_sources/modules/physics/mechanics/api/functions.txt deleted file mode 100644 index f67ca1f057c..00000000000 --- a/dev-py3k/_sources/modules/physics/mechanics/api/functions.txt +++ /dev/null @@ -1,32 +0,0 @@ -======================================== -Related Essential Functions (Docstrings) -======================================== - -dynamicsymbols -============== - -.. autofunction:: sympy.physics.mechanics.essential.dynamicsymbols - - -dot -=== - -.. autofunction:: sympy.physics.mechanics.functions.dot - - -cross -===== - -.. autofunction:: sympy.physics.mechanics.functions.cross - - -outer -===== - -.. autofunction:: sympy.physics.mechanics.functions.outer - - -express -======= - -.. autofunction:: sympy.physics.mechanics.functions.express diff --git a/dev-py3k/_sources/modules/physics/mechanics/api/kane.txt b/dev-py3k/_sources/modules/physics/mechanics/api/kane.txt deleted file mode 100644 index 0d1424f245a..00000000000 --- a/dev-py3k/_sources/modules/physics/mechanics/api/kane.txt +++ /dev/null @@ -1,22 +0,0 @@ -==================================================================== -Kane's Method, Lagrange's Method & Associated Functions (Docstrings) -==================================================================== - -Kane -==== - -.. automodule:: sympy.physics.mechanics.kane - :members: - - -partial_velocity -================ - -.. automodule:: sympy.physics.mechanics.functions - :members: partial_velocity - -LagrangesMethod -=============== - -.. automodule:: sympy.physics.mechanics.lagrange - :members: diff --git a/dev-py3k/_sources/modules/physics/mechanics/api/kinematics.txt b/dev-py3k/_sources/modules/physics/mechanics/api/kinematics.txt deleted file mode 100644 index e3135261fd5..00000000000 --- a/dev-py3k/_sources/modules/physics/mechanics/api/kinematics.txt +++ /dev/null @@ -1,16 +0,0 @@ -======================= -Kinematics (Docstrings) -======================= - -Point -===== - -.. automodule:: sympy.physics.mechanics.point - :members: - - -kinematic_equations -=================== - -.. automodule:: sympy.physics.mechanics.functions - :members: kinematic_equations diff --git a/dev-py3k/_sources/modules/physics/mechanics/api/part_bod.txt b/dev-py3k/_sources/modules/physics/mechanics/api/part_bod.txt deleted file mode 100644 index 5fd0200876f..00000000000 --- a/dev-py3k/_sources/modules/physics/mechanics/api/part_bod.txt +++ /dev/null @@ -1,55 +0,0 @@ -===================================================== -Masses, Inertias & Particles, RigidBodys (Docstrings) -===================================================== - -Particle -======== - -.. automodule:: sympy.physics.mechanics.particle - :members: - - -RigidBody -========= - -.. automodule:: sympy.physics.mechanics.rigidbody - :members: - - -inertia -======= - -.. autofunction:: sympy.physics.mechanics.functions.inertia - - -inertia_of_point_mass -===================== - -.. autofunction:: sympy.physics.mechanics.functions.inertia_of_point_mass - - -linear_momentum -=============== - -.. autofunction:: sympy.physics.mechanics.functions.linear_momentum - - -angular_momentum -================ - -.. autofunction:: sympy.physics.mechanics.functions.angular_momentum - -kinetic_energy -============== - -.. autofunction:: sympy.physics.mechanics.functions.kinetic_energy - -potential_energy -================ - -.. autofunction:: sympy.physics.mechanics.functions.potential_energy - -Lagrangian -========== - -.. autofunction:: sympy.physics.mechanics.functions.Lagrangian diff --git a/dev-py3k/_sources/modules/physics/mechanics/api/printing.txt b/dev-py3k/_sources/modules/physics/mechanics/api/printing.txt deleted file mode 100644 index 99ba010e23e..00000000000 --- a/dev-py3k/_sources/modules/physics/mechanics/api/printing.txt +++ /dev/null @@ -1,26 +0,0 @@ -===================== -Printing (Docstrings) -===================== - -mechanics_printing -================== - -.. autofunction:: sympy.physics.mechanics.functions.mechanics_printing - - -mprint -====== - -.. autofunction:: sympy.physics.mechanics.functions.mprint - - -mpprint -======= - -.. autofunction:: sympy.physics.mechanics.functions.mpprint - - -mlatex -====== - -.. autofunction:: sympy.physics.mechanics.functions.mlatex diff --git a/dev-py3k/_sources/modules/physics/mechanics/examples.txt b/dev-py3k/_sources/modules/physics/mechanics/examples.txt deleted file mode 100644 index 025a002d519..00000000000 --- a/dev-py3k/_sources/modules/physics/mechanics/examples.txt +++ /dev/null @@ -1,577 +0,0 @@ -============================== -Examples for Physics.Mechanics -============================== - -This module has been developed to aid in formulation of equations of motion; -here, examples follow to help display how to use this module. - -The Rolling Disc -================ - -The first example is that of a rolling disc. The disc is assumed to be -infinitely thin, in contact with the ground at only 1 point, and it is rolling -without slip on the ground. See the image below. - -.. image:: ex_rd.* - :height: 550 - :width: 550 - :align: center - -Here the definition of the rolling disc's kinematics is formed from the contact -point up, removing the need to introduce generalized speeds. Only 3 -configuration and three speed variables are need to describe this system, along -with the disc's mass and radius, and the local gravity (note that mass will -drop out). :: - - >>> from sympy import symbols, sin, cos, tan - >>> from sympy.physics.mechanics import * - >>> q1, q2, q3, u1, u2, u3 = dynamicsymbols('q1 q2 q3 u1 u2 u3') - >>> q1d, q2d, q3d, u1d, u2d, u3d = dynamicsymbols('q1 q2 q3 u1 u2 u3', 1) - >>> r, m, g = symbols('r m g') - >>> mechanics_printing() - -The kinematics are formed by a series of simple rotations. Each simple rotation -creates a new frame, and the next rotation is defined by the new frame's basis -vectors. This example uses a 3-1-2 series of rotations, or Z, X, Y series of -rotations. Angular velocity for this is defined using the second frame's basis -(the lean frame); it is for this reason that we defined intermediate frames, -rather than using a body-three orientation. :: - - >>> N = ReferenceFrame('N') - >>> Y = N.orientnew('Y', 'Axis', [q1, N.z]) - >>> L = Y.orientnew('L', 'Axis', [q2, Y.x]) - >>> R = L.orientnew('R', 'Axis', [q3, L.y]) - >>> R.set_ang_vel(N, u1 * L.x + u2 * L.y + u3 * L.z) - >>> R.set_ang_acc(N, R.ang_vel_in(N).dt(R) + (R.ang_vel_in(N) ^ R.ang_vel_in(N))) - -This is the translational kinematics. We create a point with no velocity -in N; this is the contact point between the disc and ground. Next we form -the position vector from the contact point to the disc's center of mass. -Finally we form the velocity and acceleration of the disc. :: - - >>> C = Point('C') - >>> C.set_vel(N, 0) - >>> Dmc = C.locatenew('Dmc', r * L.z) - >>> Dmc.v2pt_theory(C, N, R) - r*u2*L.x - r*u1*L.y - >>> Dmc.a2pt_theory(C, N, R) - (r*u1*u3 + r*u2')*L.x + (-r*(-u3*q3' + u1') + r*u2*u3)*L.y + (-r*u1**2 - r*u2**2)*L.z - -This is a simple way to form the inertia dyadic. The inertia of the disc does -not change within the lean frame as the disc rolls; this will make for simpler -equations in the end. :: - - >>> I = inertia(L, m / 4 * r**2, m / 2 * r**2, m / 4 * r**2) - >>> mprint(I) - m*r**2/4*(L.x|L.x) + m*r**2/2*(L.y|L.y) + m*r**2/4*(L.z|L.z) - -Kinematic differential equations; how the generalized coordinate time -derivatives relate to generalized speeds. Here these were computed by hand. :: - - >>> kd = [q1d - u3/cos(q2), q2d - u1, q3d - u2 + u3 * tan(q2)] - -Creation of the force list; it is the gravitational force at the center of mass of -the disc. Then we create the disc by assigning a Point to the center of mass -attribute, a ReferenceFrame to the frame attribute, and mass and inertia. Then -we form the body list. :: - - >>> ForceList = [(Dmc, - m * g * Y.z)] - >>> BodyD = RigidBody('BodyD', Dmc, R, m, (I, Dmc)) - >>> BodyList = [BodyD] - -Finally we form the equations of motion, using the same steps we did before. -Specify inertial frame, supply generalized coordinates and speeds, supply -kinematic differential equation dictionary, compute Fr from the force list and -Fr* from the body list, compute the mass matrix and forcing terms, then solve -for the u dots (time derivatives of the generalized speeds). :: - - >>> KM = KanesMethod(N, q_ind=[q1, q2, q3], u_ind=[u1, u2, u3], kd_eqs=kd) - >>> (fr, frstar) = KM.kanes_equations(ForceList, BodyList) - >>> MM = KM.mass_matrix - >>> forcing = KM.forcing - >>> rhs = MM.inv() * forcing - >>> kdd = KM.kindiffdict() - >>> rhs = rhs.subs(kdd) - >>> rhs.simplify() - >>> mprint(rhs) - [(4*g*sin(q2)/5 + 2*r*u2*u3 - r*u3**2*tan(q2))/r] - [ -2*u1*u3/3] - [ (-2*u2 + u3*tan(q2))*u1] - -This concludes the rolling disc example. - -The Rolling Disc, again -======================= - -We will now revisit the rolling disc example, except this time we are bringing -the non-contributing (constraint) forces into evidence. See [Kane1985]_ for a -more thorough explanation of this. Here, we will turn on the automatic -simplifcation done when doing vector operations. It makes the outputs nicer for -small problems, but can cause larger vector operations to hang. :: - - >>> from sympy.physics.mechanics import * - >>> mechanics_printing() - >>> Vector.simp = True - >>> q1, q2, q3, u1, u2, u3 = dynamicsymbols('q1 q2 q3 u1 u2 u3') - >>> q1d, q2d, q3d, u1d, u2d, u3d = dynamicsymbols('q1 q2 q3 u1 u2 u3', 1) - >>> r, m, g = symbols('r m g') - -These two lines introduce the extra quantities needed to find the constraint -forces. :: - - >>> u4, u5, u6, f1, f2, f3 = dynamicsymbols('u4 u5 u6 f1 f2 f3') - -Most of the main code is the same as before. :: - - >>> N = ReferenceFrame('N') - >>> Y = N.orientnew('Y', 'Axis', [q1, N.z]) - >>> L = Y.orientnew('L', 'Axis', [q2, Y.x]) - >>> R = L.orientnew('R', 'Axis', [q3, L.y]) - >>> R.set_ang_vel(N, u1 * L.x + u2 * L.y + u3 * L.z) - >>> R.set_ang_acc(N, R.ang_vel_in(N).dt(R) + (R.ang_vel_in(N) ^ R.ang_vel_in(N))) - -The definition of rolling without slip necessitates that the velocity of the -contact point is zero; as part of bringing the constraint forces into evidence, -we have to introduce speeds at this point, which will by definition always be -zero. They are normal to the ground, along the path which the disc is rolling, -and along the ground in an perpendicular direction. :: - - >>> C = Point('C') - >>> C.set_vel(N, u4 * L.x + u5 * (Y.z ^ L.x) + u6 * Y.z) - >>> Dmc = C.locatenew('Dmc', r * L.z) - >>> vel = Dmc.v2pt_theory(C, N, R) - >>> acc = Dmc.a2pt_theory(C, N, R) - >>> I = inertia(L, m / 4 * r**2, m / 2 * r**2, m / 4 * r**2) - >>> kd = [q1d - u3/cos(q2), q2d - u1, q3d - u2 + u3 * tan(q2)] - -Just as we previously introduced three speeds as part of this process, we also -introduce three forces; they are in the same direction as the speeds, and -represent the constraint forces in those directions. :: - - >>> ForceList = [(Dmc, - m * g * Y.z), (C, f1 * L.x + f2 * (Y.z ^ L.x) + f3 * Y.z)] - >>> BodyD = RigidBody('BodyD', Dmc, R, m, (I, Dmc)) - >>> BodyList = [BodyD] - - >>> KM = KanesMethod(N, q_ind=[q1, q2, q3], u_ind=[u1, u2, u3], kd_eqs=kd, - ... u_auxiliary=[u4, u5, u6]) - >>> (fr, frstar) = KM.kanes_equations(ForceList, BodyList) - >>> MM = KM.mass_matrix - >>> forcing = KM.forcing - >>> rhs = MM.inv() * forcing - >>> kdd = KM.kindiffdict() - >>> rhs = rhs.subs(kdd) - >>> rhs.simplify() - >>> mprint(rhs) - [(4*g*sin(q2)/5 + 2*r*u2*u3 - r*u3**2*tan(q2))/r] - [ -2*u1*u3/3] - [ (-2*u2 + u3*tan(q2))*u1] - >>> from sympy import signsimp, factor_terms - >>> mprint(KM.auxiliary_eqs.applyfunc(lambda w: factor_terms(signsimp(w)))) - [ m*r*(u1*u3 + u2') - f1] - [ m*r*((u1**2 + u2**2)*sin(q2) + (u2*u3 + u3*q3' - u1')*cos(q2)) - f2] - [g*m - m*r*((u1**2 + u2**2)*cos(q2) - (u2*u3 + u3*q3' - u1')*sin(q2)) - f3] - -The Bicycle -=========== - -The bicycle is an interesting system in that it has multiple rigid bodies, -non-holonomic constraints, and a holonomic constraint. The linearized equations -of motion are presented in [Meijaard2007]_. This example will go through -construction of the equations of motion in :mod:`mechanics`. :: - - >>> from sympy import * - >>> from sympy.physics.mechanics import * - >>> print('Calculation of Linearized Bicycle \"A\" Matrix, ' - ... 'with States: Roll, Steer, Roll Rate, Steer Rate') - Calculation of Linearized Bicycle "A" Matrix, with States: Roll, Steer, Roll Rate, Steer Rate - - -Note that this code has been crudely ported from Autolev, which is the reason -for some of the unusual naming conventions. It was purposefully as similar as -possible in order to aid initial porting & debugging. We also turn off -Vector.simp (turned on in the last example) to avoid hangups when doing -computations in this problem. :: - - >>> Vector.simp = False - >>> mechanics_printing() - -Declaration of Coordinates & Speeds: -A Simple definitions for qdots: (qd = u) is used in this code. Speeds are: yaw -frame ang. rate, roll frame ang. rate, rear wheel frame ang. rate (spinning -motion), frame ang. rate (pitching motion), steering frame ang. rate, and front -wheel ang. rate (spinning motion). Wheel positions are ignorable coordinates, -so they are not introduced. :: - - >>> q1, q2, q4, q5 = dynamicsymbols('q1 q2 q4 q5') - >>> q1d, q2d, q4d, q5d = dynamicsymbols('q1 q2 q4 q5', 1) - >>> u1, u2, u3, u4, u5, u6 = dynamicsymbols('u1 u2 u3 u4 u5 u6') - >>> u1d, u2d, u3d, u4d, u5d, u6d = dynamicsymbols('u1 u2 u3 u4 u5 u6', 1) - -Declaration of System's Parameters: -The below symbols should be fairly self-explanatory. :: - - >>> WFrad, WRrad, htangle, forkoffset = symbols('WFrad WRrad htangle forkoffset') - >>> forklength, framelength, forkcg1 = symbols('forklength framelength forkcg1') - >>> forkcg3, framecg1, framecg3, Iwr11 = symbols('forkcg3 framecg1 framecg3 Iwr11') - >>> Iwr22, Iwf11, Iwf22, Iframe11 = symbols('Iwr22 Iwf11 Iwf22 Iframe11') - >>> Iframe22, Iframe33, Iframe31, Ifork11 = \ - ... symbols('Iframe22 Iframe33 Iframe31 Ifork11') - >>> Ifork22, Ifork33, Ifork31, g = symbols('Ifork22 Ifork33 Ifork31 g') - >>> mframe, mfork, mwf, mwr = symbols('mframe mfork mwf mwr') - -Set up reference frames for the system: -N - inertial -Y - yaw -R - roll -WR - rear wheel, rotation angle is ignorable coordinate so not oriented -Frame - bicycle frame -TempFrame - statically rotated frame for easier reference inertia definition -Fork - bicycle fork -TempFork - statically rotated frame for easier reference inertia definition -WF - front wheel, again posses a ignorable coordinate :: - - >>> N = ReferenceFrame('N') - >>> Y = N.orientnew('Y', 'Axis', [q1, N.z]) - >>> R = Y.orientnew('R', 'Axis', [q2, Y.x]) - >>> Frame = R.orientnew('Frame', 'Axis', [q4 + htangle, R.y]) - >>> WR = ReferenceFrame('WR') - >>> TempFrame = Frame.orientnew('TempFrame', 'Axis', [-htangle, Frame.y]) - >>> Fork = Frame.orientnew('Fork', 'Axis', [q5, Frame.x]) - >>> TempFork = Fork.orientnew('TempFork', 'Axis', [-htangle, Fork.y]) - >>> WF = ReferenceFrame('WF') - - -Kinematics of the Bicycle: -First block of code is forming the positions of the relevant points rear wheel -contact -> rear wheel's center of mass -> frame's center of mass + frame/fork connection --> fork's center of mass + front wheel's center of mass -> front wheel contact point. :: - - >>> WR_cont = Point('WR_cont') - >>> WR_mc = WR_cont.locatenew('WR_mc', WRrad * R.z) - >>> Steer = WR_mc.locatenew('Steer', framelength * Frame.z) - >>> Frame_mc = WR_mc.locatenew('Frame_mc', -framecg1 * Frame.x + framecg3 * Frame.z) - >>> Fork_mc = Steer.locatenew('Fork_mc', -forkcg1 * Fork.x + forkcg3 * Fork.z) - >>> WF_mc = Steer.locatenew('WF_mc', forklength * Fork.x + forkoffset * Fork.z) - >>> WF_cont = WF_mc.locatenew('WF_cont', WFrad*(dot(Fork.y, Y.z)*Fork.y - \ - ... Y.z).normalize()) - -Set the angular velocity of each frame: -Angular accelerations end up being calculated automatically by differentiating -the angular velocities when first needed. :: -u1 is yaw rate -u2 is roll rate -u3 is rear wheel rate -u4 is frame pitch rate -u5 is fork steer rate -u6 is front wheel rate :: - - >>> Y.set_ang_vel(N, u1 * Y.z) - >>> R.set_ang_vel(Y, u2 * R.x) - >>> WR.set_ang_vel(Frame, u3 * Frame.y) - >>> Frame.set_ang_vel(R, u4 * Frame.y) - >>> Fork.set_ang_vel(Frame, u5 * Fork.x) - >>> WF.set_ang_vel(Fork, u6 * Fork.y) - -Form the velocities of the points, using the 2-point theorem. Accelerations -again are calculated automatically when first needed. :: - - >>> WR_cont.set_vel(N, 0) - >>> WR_mc.v2pt_theory(WR_cont, N, WR) - WRrad*(u1*sin(q2) + u3 + u4)*R.x - WRrad*u2*R.y - >>> Steer.v2pt_theory(WR_mc, N, Frame) - WRrad*(u1*sin(q2) + u3 + u4)*R.x - WRrad*u2*R.y + framelength*(u1*sin(q2) + u4)*Frame.x - framelength*(-u1*sin(htangle + q4)*cos(q2) + u2*cos(htangle + q4))*Frame.y - >>> Frame_mc.v2pt_theory(WR_mc, N, Frame) - WRrad*(u1*sin(q2) + u3 + u4)*R.x - WRrad*u2*R.y + framecg3*(u1*sin(q2) + u4)*Frame.x + (-framecg1*(u1*cos(htangle + q4)*cos(q2) + u2*sin(htangle + q4)) - framecg3*(-u1*sin(htangle + q4)*cos(q2) + u2*cos(htangle + q4)))*Frame.y + framecg1*(u1*sin(q2) + u4)*Frame.z - >>> Fork_mc.v2pt_theory(Steer, N, Fork) - WRrad*(u1*sin(q2) + u3 + u4)*R.x - WRrad*u2*R.y + framelength*(u1*sin(q2) + u4)*Frame.x - framelength*(-u1*sin(htangle + q4)*cos(q2) + u2*cos(htangle + q4))*Frame.y + forkcg3*((sin(q2)*cos(q5) + sin(q5)*cos(htangle + q4)*cos(q2))*u1 + u2*sin(htangle + q4)*sin(q5) + u4*cos(q5))*Fork.x + (-forkcg1*((-sin(q2)*sin(q5) + cos(htangle + q4)*cos(q2)*cos(q5))*u1 + u2*sin(htangle + q4)*cos(q5) - u4*sin(q5)) - forkcg3*(-u1*sin(htangle + q4)*cos(q2) + u2*cos(htangle + q4) + u5))*Fork.y + forkcg1*((sin(q2)*cos(q5) + sin(q5)*cos(htangle + q4)*cos(q2))*u1 + u2*sin(htangle + q4)*sin(q5) + u4*cos(q5))*Fork.z - >>> WF_mc.v2pt_theory(Steer, N, Fork) - WRrad*(u1*sin(q2) + u3 + u4)*R.x - WRrad*u2*R.y + framelength*(u1*sin(q2) + u4)*Frame.x - framelength*(-u1*sin(htangle + q4)*cos(q2) + u2*cos(htangle + q4))*Frame.y + forkoffset*((sin(q2)*cos(q5) + sin(q5)*cos(htangle + q4)*cos(q2))*u1 + u2*sin(htangle + q4)*sin(q5) + u4*cos(q5))*Fork.x + (forklength*((-sin(q2)*sin(q5) + cos(htangle + q4)*cos(q2)*cos(q5))*u1 + u2*sin(htangle + q4)*cos(q5) - u4*sin(q5)) - forkoffset*(-u1*sin(htangle + q4)*cos(q2) + u2*cos(htangle + q4) + u5))*Fork.y - forklength*((sin(q2)*cos(q5) + sin(q5)*cos(htangle + q4)*cos(q2))*u1 + u2*sin(htangle + q4)*sin(q5) + u4*cos(q5))*Fork.z - >>> WF_cont.v2pt_theory(WF_mc, N, WF) - WRrad*(u1*sin(q2) + u3 + u4)*R.x - WRrad*u2*R.y + framelength*(u1*sin(q2) + u4)*Frame.x - framelength*(-u1*sin(htangle + q4)*cos(q2) + u2*cos(htangle + q4))*Frame.y + (-WFrad*(sin(q2)*cos(q5) + sin(q5)*cos(htangle + q4)*cos(q2))*((-sin(q2)*sin(q5) + cos(htangle + q4)*cos(q2)*cos(q5))*u1 + u2*sin(htangle + q4)*cos(q5) - u4*sin(q5))/sqrt((-sin(q2)*cos(q5) - sin(q5)*cos(htangle + q4)*cos(q2))*(sin(q2)*cos(q5) + sin(q5)*cos(htangle + q4)*cos(q2)) + 1) + forkoffset*((sin(q2)*cos(q5) + sin(q5)*cos(htangle + q4)*cos(q2))*u1 + u2*sin(htangle + q4)*sin(q5) + u4*cos(q5)))*Fork.x + (forklength*((-sin(q2)*sin(q5) + cos(htangle + q4)*cos(q2)*cos(q5))*u1 + u2*sin(htangle + q4)*cos(q5) - u4*sin(q5)) - forkoffset*(-u1*sin(htangle + q4)*cos(q2) + u2*cos(htangle + q4) + u5))*Fork.y + (WFrad*(sin(q2)*cos(q5) + sin(q5)*cos(htangle + q4)*cos(q2))*(-u1*sin(htangle + q4)*cos(q2) + u2*cos(htangle + q4) + u5)/sqrt((-sin(q2)*cos(q5) - sin(q5)*cos(htangle + q4)*cos(q2))*(sin(q2)*cos(q5) + sin(q5)*cos(htangle + q4)*cos(q2)) + 1) - forklength*((sin(q2)*cos(q5) + sin(q5)*cos(htangle + q4)*cos(q2))*u1 + u2*sin(htangle + q4)*sin(q5) + u4*cos(q5)))*Fork.z - WFrad*((-sin(q2)*sin(q5)*cos(htangle + q4) + cos(q2)*cos(q5))*u6 + u4*cos(q2) + u5*sin(htangle + q4)*sin(q2))/sqrt((-sin(q2)*cos(q5) - sin(q5)*cos(htangle + q4)*cos(q2))*(sin(q2)*cos(q5) + sin(q5)*cos(htangle + q4)*cos(q2)) + 1)*Y.x + WFrad*(u2 + u5*cos(htangle + q4) + u6*sin(htangle + q4)*sin(q5))/sqrt((-sin(q2)*cos(q5) - sin(q5)*cos(htangle + q4)*cos(q2))*(sin(q2)*cos(q5) + sin(q5)*cos(htangle + q4)*cos(q2)) + 1)*Y.y - - -Sets the inertias of each body. Uses the inertia frame to construct the inertia -dyadics. Wheel inertias are only defined by principal moments of inertia, and -are in fact constant in the frame and fork reference frames; it is for this -reason that the orientations of the wheels does not need to be defined. The -frame and fork inertias are defined in the 'Temp' frames which are fixed to the -appropriate body frames; this is to allow easier input of the reference values -of the benchmark paper. Note that due to slightly different orientations, the -products of inertia need to have their signs flipped; this is done later when -entering the numerical value. :: - - >>> Frame_I = (inertia(TempFrame, Iframe11, Iframe22, Iframe33, 0, 0, - ... Iframe31), Frame_mc) - >>> Fork_I = (inertia(TempFork, Ifork11, Ifork22, Ifork33, 0, 0, Ifork31), Fork_mc) - >>> WR_I = (inertia(Frame, Iwr11, Iwr22, Iwr11), WR_mc) - >>> WF_I = (inertia(Fork, Iwf11, Iwf22, Iwf11), WF_mc) - -Declaration of the RigidBody containers. :: - - >>> BodyFrame = RigidBody('BodyFrame', Frame_mc, Frame, mframe, Frame_I) - >>> BodyFork = RigidBody('BodyFork', Fork_mc, Fork, mfork, Fork_I) - >>> BodyWR = RigidBody('BodyWR', WR_mc, WR, mwr, WR_I) - >>> BodyWF = RigidBody('BodyWF', WF_mc, WF, mwf, WF_I) - - >>> print('Before Forming the List of Nonholonomic Constraints.') - Before Forming the List of Nonholonomic Constraints. - -The kinematic differential equations; they are defined quite simply. Each entry -in this list is equal to zero. :: - - >>> kd = [q1d - u1, q2d - u2, q4d - u4, q5d - u5] - -The nonholonomic constraints are the velocity of the front wheel contact point -dotted into the X, Y, and Z directions; the yaw frame is used as it is "closer" -to the front wheel (1 less DCM connecting them). These constraints force the -velocity of the front wheel contact point to be 0 in the inertial frame; the X -and Y direction constraints enforce a "no-slip" condition, and the Z direction -constraint forces the front wheel contact point to not move away from the -ground frame, essentially replicating the holonomic constraint which does not -allow the frame pitch to change in an invalid fashion. :: - - >>> conlist_speed = [WF_cont.vel(N) & Y.x, - ... WF_cont.vel(N) & Y.y, - ... WF_cont.vel(N) & Y.z] - -The holonomic constraint is that the position from the rear wheel contact point -to the front wheel contact point when dotted into the normal-to-ground plane -direction must be zero; effectively that the front and rear wheel contact -points are always touching the ground plane. This is actually not part of the -dynamic equations, but instead is necessary for the linearization process. :: - - >>> conlist_coord = [WF_cont.pos_from(WR_cont) & Y.z] - -The force list; each body has the appropriate gravitational force applied at -its center of mass. :: - - >>> FL = [(Frame_mc, -mframe * g * Y.z), (Fork_mc, -mfork * g * Y.z), - ... (WF_mc, -mwf * g * Y.z), (WR_mc, -mwr * g * Y.z)] - >>> BL = [BodyFrame, BodyFork, BodyWR, BodyWF] - -The N frame is the inertial frame, coordinates are supplied in the order of -independent, dependent coordinates. The kinematic differential equations are -also entered here. Here the independent speeds are specified, followed by the -dependent speeds, along with the non-holonomic constraints. The dependent -coordinate is also provided, with the holonomic constraint. Again, this is only -comes into play in the linearization process, but is necessary for the -linearization to correctly work. :: - - >>> KM = KanesMethod(N, q_ind=[q1, q2, q3], - ... q_dependent=[q4], configuration_constraints=conlist_coord, - ... u_ind=[u2, u3, u5], - ... u_dependent=[u1, u4, u6], velocity_constraints=conlist_speed, - ... kd_eqs=kd) - >>> print('Before Forming Generalized Active and Inertia Forces, Fr and Fr*') - Before Forming Generalized Active and Inertia Forces, Fr and Fr* - >>> (fr, frstar) = KM.kanes_equations(FL, BL) - >>> print('Base Equations of Motion Computed') - Base Equations of Motion Computed - -This is the start of entering in the numerical values from the benchmark paper -to validate the eigenvalues of the linearized equations from this model to the -reference eigenvalues. Look at the aforementioned paper for more information. -Some of these are intermediate values, used to transform values from the paper -into the coordinate systems used in this model. :: - - >>> PaperRadRear = 0.3 - >>> PaperRadFront = 0.35 - >>> HTA = evalf.N(pi/2-pi/10) - >>> TrailPaper = 0.08 - >>> rake = evalf.N(-(TrailPaper*sin(HTA)-(PaperRadFront*cos(HTA)))) - >>> PaperWb = 1.02 - >>> PaperFrameCgX = 0.3 - >>> PaperFrameCgZ = 0.9 - >>> PaperForkCgX = 0.9 - >>> PaperForkCgZ = 0.7 - >>> FrameLength = evalf.N(PaperWb*sin(HTA) - (rake - \ - ... (PaperRadFront - PaperRadRear)*cos(HTA))) - >>> FrameCGNorm = evalf.N((PaperFrameCgZ - PaperRadRear - \ - ... (PaperFrameCgX/sin(HTA))*cos(HTA))*sin(HTA)) - >>> FrameCGPar = evalf.N((PaperFrameCgX / sin(HTA) + \ - ... (PaperFrameCgZ - PaperRadRear - \ - ... PaperFrameCgX / sin(HTA) * cos(HTA)) * cos(HTA))) - >>> tempa = evalf.N((PaperForkCgZ - PaperRadFront)) - >>> tempb = evalf.N((PaperWb-PaperForkCgX)) - >>> tempc = evalf.N(sqrt(tempa**2 + tempb**2)) - >>> PaperForkL = evalf.N((PaperWb*cos(HTA) - \ - ... (PaperRadFront - PaperRadRear)*sin(HTA))) - >>> ForkCGNorm = evalf.N(rake + (tempc * sin(pi/2 - \ - ... HTA - acos(tempa/tempc)))) - >>> ForkCGPar = evalf.N(tempc * cos((pi/2 - HTA) - \ - ... acos(tempa/tempc)) - PaperForkL) - -Here is the final assembly of the numerical values. The symbol 'v' is the -forward speed of the bicycle (a concept which only makes sense in the upright, -static equilibrium case?). These are in a dictionary which will later be -substituted in. Again the sign on the *product* of inertia values is flipped -here, due to different orientations of coordinate systems. :: - - >>> v = Symbol('v') - >>> val_dict = { - ... WFrad: PaperRadFront, - ... WRrad: PaperRadRear, - ... htangle: HTA, - ... forkoffset: rake, - ... forklength: PaperForkL, - ... framelength: FrameLength, - ... forkcg1: ForkCGPar, - ... forkcg3: ForkCGNorm, - ... framecg1: FrameCGNorm, - ... framecg3: FrameCGPar, - ... Iwr11: 0.0603, - ... Iwr22: 0.12, - ... Iwf11: 0.1405, - ... Iwf22: 0.28, - ... Ifork11: 0.05892, - ... Ifork22: 0.06, - ... Ifork33: 0.00708, - ... Ifork31: 0.00756, - ... Iframe11: 9.2, - ... Iframe22: 11, - ... Iframe33: 2.8, - ... Iframe31: -2.4, - ... mfork: 4, - ... mframe: 85, - ... mwf: 3, - ... mwr: 2, - ... g: 9.81, - ... q1: 0, - ... q2: 0, - ... q4: 0, - ... q5: 0, - ... u1: 0, - ... u2: 0, - ... u3: v/PaperRadRear, - ... u4: 0, - ... u5: 0, - ... u6: v/PaperRadFront} - >>> kdd = KM.kindiffdict() - >>> print('Before Linearization of the \"Forcing\" Term') - Before Linearization of the "Forcing" Term - -Linearizes the forcing vector; the equations are set up as MM udot = forcing, -where MM is the mass matrix, udot is the vector representing the time -derivatives of the generalized speeds, and forcing is a vector which contains -both external forcing terms and internal forcing terms, such as centripetal or -Coriolis forces. This actually returns a matrix with as many rows as *total* -coordinates and speeds, but only as many columns as independent coordinates and -speeds. (Note that below this is commented out, as it takes a few minutes to -run, which is not good when performing the doctests) :: - - >>> # forcing_lin = KM.linearize()[0].subs(sub_dict) - -As mentioned above, the size of the linearized forcing terms is expanded to -include both q's and u's, so the mass matrix must have this done as well. This -will likely be changed to be part of the linearized process, for future -reference. :: - - >>> MM_full = (KM._k_kqdot).row_join(zeros(4, 6)).col_join( - ... (zeros(6, 4)).row_join(KM.mass_matrix)) - >>> print('Before Substitution of Numerical Values') - Before Substitution of Numerical Values - -I think this is pretty self explanatory. It takes a really long time though. -I've experimented with using evalf with substitution, this failed due to -maximum recursion depth being exceeded; I also tried lambdifying this, and it -is also not successful. (again commented out due to speed) :: - - >>> # MM_full = MM_full.subs(val_dict) - >>> # forcing_lin = forcing_lin.subs(val_dict) - >>> # print('Before .evalf() call') - - >>> # MM_full = MM_full.evalf() - >>> # forcing_lin = forcing_lin.evalf() - -Finally, we construct an "A" matrix for the form xdot = A x (x being the state -vector, although in this case, the sizes are a little off). The following line -extracts only the minimum entries required for eigenvalue analysis, which -correspond to rows and columns for lean, steer, lean rate, and steer rate. -(this is all commented out due to being dependent on the above code, which is -also commented out):: - - >>> # Amat = MM_full.inv() * forcing_lin - >>> # A = Amat.extract([1,2,4,6],[1,2,3,5]) - >>> # print(A) - >>> # print('v = 1') - >>> # print(A.subs(v, 1).eigenvals()) - >>> # print('v = 2') - >>> # print(A.subs(v, 2).eigenvals()) - >>> # print('v = 3') - >>> # print(A.subs(v, 3).eigenvals()) - >>> # print('v = 4') - >>> # print(A.subs(v, 4).eigenvals()) - >>> # print('v = 5') - >>> # print(A.subs(v, 5).eigenvals()) - -Upon running the above code yourself, enabling the commented out lines, compare -the computed eigenvalues to those is the referenced paper. This concludes the -bicycle example. - -The Rolling Disc Example using Lagranges Method -=============================================== - -Here the rolling disc is formed from the contact point up, removing the -need to introduce generalized speeds. Only 3 configuration and 3 -speed variables are needed to describe this system, along with the -disc's mass and radius, and the local gravity. :: - - >>> from sympy import symbols, cos, sin - >>> from sympy.physics.mechanics import * - >>> mechanics_printing() - >>> q1, q2, q3 = dynamicsymbols('q1 q2 q3') - >>> q1d, q2d, q3d = dynamicsymbols('q1 q2 q3', 1) - >>> r, m, g = symbols('r m g') - -The kinematics are formed by a series of simple rotations. Each simple -rotation creates a new frame, and the next rotation is defined by the new -frame's basis vectors. This example uses a 3-1-2 series of rotations, or -Z, X, Y series of rotations. Angular velocity for this is defined using -the second frame's basis (the lean frame). :: - - >>> N = ReferenceFrame('N') - >>> Y = N.orientnew('Y', 'Axis', [q1, N.z]) - >>> L = Y.orientnew('L', 'Axis', [q2, Y.x]) - >>> R = L.orientnew('R', 'Axis', [q3, L.y]) - -This is the translational kinematics. We create a point with no velocity -in N; this is the contact point between the disc and ground. Next we form -the position vector from the contact point to the disc's center of mass. -Finally we form the velocity and acceleration of the disc. :: - - >>> C = Point('C') - >>> C.set_vel(N, 0) - >>> Dmc = C.locatenew('Dmc', r * L.z) - >>> Dmc.v2pt_theory(C, N, R) - r*(sin(q2)*q1' + q3')*L.x - r*q2'*L.y - -Forming the inertia dyadic. :: - - >>> I = inertia(L, m / 4 * r**2, m / 2 * r**2, m / 4 * r**2) - >>> mprint(I) - m*r**2/4*(L.x|L.x) + m*r**2/2*(L.y|L.y) + m*r**2/4*(L.z|L.z) - >>> BodyD = RigidBody('BodyD', Dmc, R, m, (I, Dmc)) - -We then set the potential energy and determine the Lagrangian of the rolling -disc. :: - - >>> BodyD.set_potential_energy(- m * g * r * cos(q2)) - >>> Lag = Lagrangian(N, BodyD) - -Then the equations of motion are generated by initializing the -``LagrangesMethod`` object. Finally we solve for the generalized -accelerations(q double dots) with the ``rhs`` method. :: - - >>> q = [q1, q2, q3] - >>> l = LagrangesMethod(Lag, q) - >>> l.form_lagranges_equations() - [5*m*r**2*(sin(q2)*q1' + q3')*cos(q2)*q2'/4 + 5*m*r**2*(sin(q2)*q1'' + cos(q2)*q1'*q2' + q3'')*sin(q2)/4 + m*r**2*sin(q2)*q3''/4 + m*r**2*cos(q2)**2*q1''/8 + m*r**2*cos(q2)*q2'*q3'/4 + (m*r**2*sin(q2)**2/4 + m*r**2*cos(q2)**2/8)*q1''] - [ g*m*r*sin(q2) - 5*m*r**2*(sin(q2)*q1' + q3')*cos(q2)*q1'/4 - m*r**2*cos(q2)*q1'*q3'/4 + 5*m*r**2*q2''/4] - [ m*r**2*(sin(q2)*q1'' + cos(q2)*q1'*q2' + q3'')/4 + m*r**2*(2*sin(q2)*q1'' + 2*cos(q2)*q1'*q2' + 2*q3'')/2 + m*r**2*sin(q2)*q1''/4 + m*r**2*cos(q2)*q1'*q2'/4 + m*r**2*q3''/4] - >>> l.rhs() - [ q1'] - [ q2'] - [ q3'] - [(24*sin(q2)**2/(m*r**2*(5*sin(q2)**2 + 1)*cos(q2)**2) + 4/(m*r**2*(5*sin(q2)**2 + 1)))*(-5*m*r**2*(sin(q2)*q1' + q3')*cos(q2)*q2'/4 - 5*m*r**2*sin(q2)*cos(q2)*q1'*q2'/4 - m*r**2*cos(q2)*q2'*q3'/4) + 6*sin(q2)*q1'*q2'/cos(q2)] - [ 4*(-g*m*r*sin(q2) + 5*m*r**2*(sin(q2)*q1' + q3')*cos(q2)*q1'/4 + m*r**2*cos(q2)*q1'*q3'/4)/(5*m*r**2)] - [ -(5*sin(q2)**2 + 1)*q1'*q2'/cos(q2) - 4*(-5*m*r**2*(sin(q2)*q1' + q3')*cos(q2)*q2'/4 - 5*m*r**2*sin(q2)*cos(q2)*q1'*q2'/4 - m*r**2*cos(q2)*q2'*q3'/4)*sin(q2)/(m*r**2*cos(q2)**2)] diff --git a/dev-py3k/_sources/modules/physics/mechanics/index.txt b/dev-py3k/_sources/modules/physics/mechanics/index.txt deleted file mode 100644 index 60da9a54745..00000000000 --- a/dev-py3k/_sources/modules/physics/mechanics/index.txt +++ /dev/null @@ -1,50 +0,0 @@ -=================== -Classical Mechanics -=================== - -:Authors: Gilbert Gede, Luke Peterson, Angadh Nanjangud - -.. topic:: Abstract - - In this documentation many components of the physics/mechanics module will - be discussed. :mod:`mechanics` has been written to allow for creation of - symbolic equations of motion for complicated multibody systems. - -Mechanics -========= - -In physics, mechanics describes conditions of rest or motion; statics or -dynamics. First, an idealized representation of a system is described. Next, we -use physical laws to generate equations that define the system's behaviors. -Then, we solve these equations, in multiple possible ways. Finally, we extract -information from these equations and solutions. Mechanics is currently set up -to create equations of motion, for dynamics. It is also limited to rigid bodies -and particles. - - -Guide to Mechanics -================== - -.. toctree:: - :maxdepth: 2 - - vectors.rst - kinematics.rst - masses.rst - kane.rst - examples.rst - advanced.rst - reference.rst - -Mechanics API -============= - -.. toctree:: - :maxdepth: 2 - - api/essential.rst - api/functions.rst - api/kinematics.rst - api/part_bod.rst - api/kane.rst - api/printing.rst diff --git a/dev-py3k/_sources/modules/physics/mechanics/kane.txt b/dev-py3k/_sources/modules/physics/mechanics/kane.txt deleted file mode 100644 index 467bdfb6936..00000000000 --- a/dev-py3k/_sources/modules/physics/mechanics/kane.txt +++ /dev/null @@ -1,302 +0,0 @@ -================================== -Kane's Method in Physics/Mechanics -================================== - -:mod:`mechanics` has been written for use with Kane's method of forming -equations of motion [Kane1985]_. This document will describe Kane's Method -as used in this module, but not how the equations are actually derived. - -Structure of Equations -====================== - -In :mod:`mechanics` we are assuming there are 5 basic sets of equations needed -to describe a system. They are: holonomic constraints, non-holonomic -constraints, kinematic differential equations, dynamic equations, and -differentiated non-holonomic equations. - -.. math:: - \mathbf{f_h}(q, t) &= 0\\ - \mathbf{k_{nh}}(q, t) u + \mathbf{f_{nh}}(q, t) &= 0\\ - \mathbf{k_{k\dot{q}}}(q, t) \dot{q} + \mathbf{k_{ku}}(q, t) u + - \mathbf{f_k}(q, t) &= 0\\ - \mathbf{k_d}(q, t) \dot{u} + \mathbf{f_d}(q, \dot{q}, u, t) &= 0\\ - \mathbf{k_{dnh}}(q, t) \dot{u} + \mathbf{f_{dnh}}(q, \dot{q}, u, t) &= 0\\ - -In :mod:`mechanics` holonomic constraints are only used for the linearization -process; it is assumed that they will be too complicated to solve for the -dependent coordinate(s). If you are able to easily solve a holonomic -constraint, you should consider redefining your problem in terms of a smaller -set of coordinates. Alternatively, the time-differentiated holonomic -constraints can be supplied. - -Kane's method forms two expressions, :math:`F_r` and :math:`F_r^*`, whose sum -is zero. In this module, these expressions are rearranged into the following -form: - - :math:`\mathbf{M}(q, t) \dot{u} = \mathbf{f}(q, \dot{q}, u, t)` - -For a non-holonomic system with `o` total speeds and `m` motion constraints, we -will get o - m equations. The mass-matrix/forcing equations are then augmented -in the following fashion: - -.. math:: - \mathbf{M}(q, t) &= \begin{bmatrix} \mathbf{k_d}(q, t) \\ - \mathbf{k_{dnh}}(q, t) \end{bmatrix}\\ - \mathbf{_{(forcing)}}(q, \dot{q}, u, t) &= \begin{bmatrix} - - \mathbf{f_d}(q, \dot{q}, u, t) \\ - \mathbf{f_{dnh}}(q, \dot{q}, u, t) - \end{bmatrix}\\ - - -Kane's Method in Physics/Mechanics -================================== - -The formulation of the equations of motion in :mod:`mechanics` starts with -creation of a ``KanesMethod`` object. Upon initialization of the -``KanesMethod`` object, an inertial reference frame needs to be supplied. along -with some basic system information, suchs as coordinates and speeds :: - - >>> from sympy.physics.mechanics import * - >>> N = ReferenceFrame('N') - >>> q1, q2, u1, u2 = dynamicsymbols('q1 q2 u1 u2') - >>> q1d, q2d, u1d, u2d = dynamicsymbols('q1 q2 u1 u2', 1) - >>> KM = KanesMethod(N, [q1, q2], [u1, u2]) - -It is also important to supply the order of coordinates and speeds properly if -there are dependent coordinates and speeds. They must be supplied after -independent coordinates and speeds or as a keyword argument; this is shown -later. :: - - >>> q1, q2, q3, q4 = dynamicsymbols('q1 q2 q3 q4') - >>> u1, u2, u3, u4 = dynamicsymbols('u1 u2 u3 u4') - >>> # Here we will assume q2 is dependent, and u2 and u3 are dependent - >>> # We need the constraint equations to enter them though - >>> KM = KanesMethod(N, [q1, q3, q4], [u1, u4]) - -Additionally, if there are auxiliary speeds, they need to be identified here. -See the examples for more information on this. In this example u4 is the -auxiliary speed. :: - - >>> KM = KanesMethod(N, [q1, q3, q4], [u1, u2, u3], u_auxiliary=[u4]) - -Kinematic differential equations must also be supplied; there are to be -provided as a list of expressions which are each equal to zero. A trivial -example follows: :: - - >>> kd = [q1d - u1, q2d - u2] - -Turning on ``mechanics_printing()`` makes the expressions significantly -shorter and is recommended. Alternatively, the ``mprint`` and ``mpprint`` -commands can be used. - -If there are non-holonomic constraints, dependent speeds need to be specified -(and so do dependent coordinates, but they only come into play when linearizing -the system). The constraints need to be supplied in a list of expressions which -are equal to zero, trivial motion and configuration constraints are shown -below: :: - - >>> N = ReferenceFrame('N') - >>> q1, q2, q3, q4 = dynamicsymbols('q1 q2 q3 q4') - >>> q1d, q2d, q3d, q4d = dynamicsymbols('q1 q2 q3 q4', 1) - >>> u1, u2, u3, u4 = dynamicsymbols('u1 u2 u3 u4') - >>> #Here we will assume q2 is dependent, and u2 and u3 are dependent - >>> speed_cons = [u2 - u1, u3 - u1 - u4] - >>> coord_cons = [q2 - q1] - >>> q_ind = [q1, q3, q4] - >>> q_dep = [q2] - >>> u_ind = [u1, u4] - >>> u_dep = [u2, u3] - >>> kd = [q1d - u1, q2d - u2, q3d - u3, q4d - u4] - >>> KM = KanesMethod(N, q_ind, u_ind, kd, - ... q_dependent=q_dep, - ... configuration_constraints=coord_cons, - ... u_dependent=u_dep, - ... velocity_constraints=speed_cons) - -A dictionary returning the solved :math:`\dot{q}`'s can also be solved for: :: - - >>> mechanics_printing() - >>> KM.kindiffdict() - {q1': u1, q2': u2, q3': u3, q4': u4} - -The final step in forming the equations of motion is supplying a list of -bodies and particles, and a list of 2-tuples of the form ``(Point, Vector)`` -or ``(ReferenceFrame, Vector)`` to represent applied forces and torques. :: - - >>> N = ReferenceFrame('N') - >>> q, u = dynamicsymbols('q u') - >>> qd, ud = dynamicsymbols('q u', 1) - >>> P = Point('P') - >>> P.set_vel(N, u * N.x) - >>> Pa = Particle('Pa', P, 5) - >>> BL = [Pa] - >>> FL = [(P, 7 * N.x)] - >>> KM = KanesMethod(N, [q], [u], [qd - u]) - >>> (fr, frstar) = KM.kanes_equations(FL, BL) - >>> KM.mass_matrix - [5] - >>> KM.forcing - [7] - -When there are motion constraints, the mass matrix is augmented by the -:math:`k_{dnh}(q, t)` matrix, and the forcing vector by the :math:`f_{dnh}(q, -\dot{q}, u, t)` vector. - -There are also the "full" mass matrix and "full" forcing vector terms, these -include the kinematic differential equations; the mass matrix is of size (n + -o) x (n + o), or square and the size of all coordinates and speeds. :: - - >>> KM.mass_matrix_full - [1, 0] - [0, 5] - >>> KM.forcing_full - [u] - [7] - -The forcing vector can be linearized as well; its Jacobian is taken only with -respect to the independent coordinates and speeds. The linearized forcing -vector is of size (n + o) x (n - l + o - m), where l is the number of -configuration constraints and m is the number of motion constraints. Two -matrices are returned; the first is an "A" matrix, or the Jacobian with respect -to the independent states, the second is a "B" matrix, or the Jacobian with -respect to 'forces'; this can be an empty matrix if there are no 'forces'. -Forces here are undefined functions of time (dynamic symbols); they are only -allowed to be in the forcing vector and their derivatives are not allowed to be -present. If dynamic symbols appear in the mass matrix or kinematic differential -equations, an error with be raised. :: - - >>> KM.linearize()[0] - [0, 1] - [0, 0] - -Exploration of the provided examples is encouraged in order to gain more -understanding of the ``KanesMethod`` object. - -====================================== -Lagrange's Method in Physics/Mechanics -====================================== - -Structure of Equations -====================== - -In :mod:`mechanics` we are assuming there are 3 basic sets of equations needed -to describe a system; the constraint equations, the time differentiated -constraint equations and the dynamic equations. - -.. math:: - \mathbf{m_{c}}(q, t) \dot{q} + \mathbf{f_{c}}(q, t) &= 0\\ - \mathbf{m_{dc}}(\dot{q}, q, t) \ddot{q} + \mathbf{f_{dc}}(\dot{q}, q, t) &= 0\\ - \mathbf{m_d}(\dot{q}, q, t) \ddot{q} + \mathbf{\Lambda_c}(q, t) - \lambda + \mathbf{f_d}(\dot{q}, q, t) &= 0\\ - -In this module, the expressions formed by using Lagrange's equations of the -second kind are rearranged into the following form: - - :math:`\mathbf{M}(q, t) x = \mathbf{f}(q, \dot{q}, t)` - -where in the case of a system without constraints: - - :math:`x = \ddot{q}` - -For a constrained system with `n` generalized speeds and `m` constraints, we -will get n - m equations. The mass-matrix/forcing equations are then augmented -in the following fashion: - -.. math:: - x = \begin{bmatrix} \ddot{q} \\ \lambda \end{bmatrix} \\ - \mathbf{M}(q, t) &= \begin{bmatrix} \mathbf{m_d}(q, t) & - \mathbf{\Lambda_c}(q, t) \end{bmatrix}\\ - \mathbf{F}(\dot{q}, q, t) &= \begin{bmatrix} \mathbf{f_d}(q, \dot{q}, t) - \end{bmatrix}\\ - - -Lagrange's Method in Physics/Mechanics -====================================== - -The formulation of the equations of motion in :mod:`mechanics` using -Lagrange's Method starts with the creation of generalized coordinates and a -Lagrangian. The Lagrangian can either be created with the ``Lagrangian`` -function or can be a user supplied function. In this case we will supply the -Lagrangian. :: - - >>> from sympy.physics.mechanics import * - >>> q1, q2 = dynamicsymbols('q1 q2') - >>> q1d, q2d = dynamicsymbols('q1 q2', 1) - >>> L = q1d**2 + q2d**2 - -To formulate the equations of motion we create a ``LagrangesMethod`` -object. The Lagrangian and generalized coordinates need to be supplied upon -initialization. :: - - >>> LM = LagrangesMethod(L, [q1, q2]) - -With that the equations of motion can be formed. :: - - >>> mechanics_printing() - >>> LM.form_lagranges_equations() - [2*q1''] - [2*q2''] - -It is possible to obtain the mass matrix and the forcing vector. :: - - >>> LM.mass_matrix - [2, 0] - [0, 2] - - >>> LM.forcing - [0] - [0] - -If there are any holonomic or non-holonomic constraints, they must be supplied -as keyword arguments in a list of expressions which are equal to zero. It -should be noted that :mod:`mechanics` requires that the holonomic constraint -equations must be supplied as velocity level constraint equations i.e. the -holonomic constraint equations must be supplied after they have been -differentiated with respect to time. Modifying the example above, the equations -of motion can then be generated: :: - - >>> LM = LagrangesMethod(L, [q1, q2], coneqs = [q1d - q2d]) - -When the equations of motion are generated in this case, the Lagrange -multipliers are introduced; they are represented by ``lam1`` in this case. In -general, there will be as many multipliers as there are constraint equations. :: - - >>> LM.form_lagranges_equations() - [ lam1 + 2*q1''] - [-lam1 + 2*q2''] - -Also in the case of systems with constraints, the 'full' mass matrix is -augmented by the :math:`k_{dc}(q, t)` matrix, and the forcing vector by the -:math:`f_{dc}(q, \dot{q}, t)` vector. The 'full' mass matrix is of size -(2n + o) x (2n + o), i.e. it's a square matrix. :: - - >>> LM.mass_matrix_full - [1, 0, 0, 0, 0] - [0, 1, 0, 0, 0] - [0, 0, 2, 0, -1] - [0, 0, 0, 2, 1] - [0, 0, 1, -1, 0] - >>> LM.forcing_full - [q1'] - [q2'] - [ 0] - [ 0] - [ 0] - -If there are any non-conservative forces or moments acting on the system, -they must also be supplied as keyword arguments in a list of 2-tuples of the -form ``(Point, Vector)`` or ``(ReferenceFrame, Vector)`` where the ``Vector`` -represents the non-conservative forces and torques. Along with this 2-tuple, -the inertial frame must also be specified as a keyword argument. This is shown -below by modifying the example above: :: - - >>> N = ReferenceFrame('N') - >>> P = Point('P') - >>> P.set_vel(N, q1d * N.x) - >>> FL = [(P, 7 * N.x)] - >>> LM = LagrangesMethod(L, [q1, q2], forcelist = FL, frame = N) - >>> LM.form_lagranges_equations() - [2*q1'' - 7] - [ 2*q2''] - -Exploration of the provided examples is encouraged in order to gain more -understanding of the ``LagrangesMethod`` object. diff --git a/dev-py3k/_sources/modules/physics/mechanics/kinematics.txt b/dev-py3k/_sources/modules/physics/mechanics/kinematics.txt deleted file mode 100644 index ca8a91471d8..00000000000 --- a/dev-py3k/_sources/modules/physics/mechanics/kinematics.txt +++ /dev/null @@ -1,572 +0,0 @@ -===================== -Mechanics: Kinematics -===================== - -In mechanics, kinematics refers to the motion of bodies (or particles). -It is the 'a' in 'f=ma'. This document will give some mathematical background -to describing a system's kinematics as well as how to represent the kinematics -in :mod:`mechanics`. - -Introduction to Kinematics -========================== - -The first topic is rigid motion kinematics. A rigid body is an idealized -representation of a physical object which has mass and rotational inertia. -Rigid bodies are obviously not flexible. We can break down rigid body motion -into translational motion, and rotational motion (when dealing with particles, we -only have translational motion). Rotational motion can further be broken down -into simple rotations and general rotations. - -Translation of a rigid body is defined as a motion where the orientation of the -body does not change during the motion; or during the motion any line segment -would be parallel to itself at the start of the motion. - -Simple rotations are rotations in which the orientation of the body may change, -but there is always one line which remains parallel to itself at the start of -the motion. - -General rotations are rotations which there is not always one line parallel to -itself at the start of the motion. - -Angular Velocity ----------------- - -The angular velocity of a rigid body refers to the rate of change of its -orientation. The angular velocity of a body is written down as: -:math:`^{\mathbf{N}}\mathbf{\omega}^{\mathbf{B}}`, or the angular velocity of -:math:`\mathbf{B}` in :math:`\mathbf{N}`, which is a vector. Note that here, -the term rigid body was used, but reference frames can also have angular -velocities. Further discussion of the distinction between a rigid body and a -reference frame will occur later when describing the code representation. - -Angular velocity is defined as being positive in the direction which causes the -orientation angles to increase (for simple rotations, or series of simple -rotations). - -.. image:: kin_angvel1.* - :height: 350 - :width: 250 - :align: center - -The angular velocity vector represents the time derivative of the orientation. -As a time derivative vector quantity, like those covered in the Vector & -ReferenceFrame documentation, this quantity (angular velocity) needs to be -defined in a reference frame. That is what the :math:`\mathbf{N}` is in the -above definition of angular velocity; the frame in which the angular velocity -is defined in. - -The angular velocity of :math:`\mathbf{B}` in :math:`\mathbf{N}` can also be -defined by: - -.. math:: - ^{\mathbf{N}}\mathbf{\omega}^{\mathbf{B}} = - (\frac{^{\mathbf{N}}d \mathbf{\hat{b}_y}}{dt}\cdot\mathbf{\hat{b}_z} - )\mathbf{\hat{b}_x} + (\frac{^{\mathbf{N}}d \mathbf{\hat{b}_z}}{dt}\cdot - \mathbf{\hat{b}_x})\mathbf{\hat{b}_y} + (\frac{^{\mathbf{N}}d - \mathbf{\hat{b}_x}}{dt}\cdot\mathbf{\hat{b}_y})\mathbf{\hat{b}_z} - -It is also common for a body's angular velocity to be written as: - -.. math:: - ^{\mathbf{N}}\mathbf{\omega}^{\mathbf{B}} = w_x \mathbf{\hat{b}_x} + - w_y \mathbf{\hat{b}_y} + w_z \mathbf{\hat{b}_z} - -There are a few additional important points relating to angular velocity. The -first is the addition theorem for angular velocities, a way of relating the -angular velocities of multiple bodies and frames. The theorem follows: - -.. math:: - ^{\mathbf{N}}\mathbf{\omega}^{\mathbf{D}} = - ^{\mathbf{N}}\mathbf{\omega}^{\mathbf{A}} + - ^{\mathbf{A}}\mathbf{\omega}^{\mathbf{B}} + - ^{\mathbf{B}}\mathbf{\omega}^{\mathbf{C}} + - ^{\mathbf{C}}\mathbf{\omega}^{\mathbf{D}} - -This is also shown in the following example: - -.. image:: kin_angvel2.* - :height: 300 - :width: 450 - :align: center - -.. math:: - ^{\mathbf{N}}\mathbf{\omega}^{\mathbf{A}} &= 0\\ - ^{\mathbf{A}}\mathbf{\omega}^{\mathbf{B}} &= \dot{q_1} \mathbf{\hat{a}_x}\\ - ^{\mathbf{B}}\mathbf{\omega}^{\mathbf{C}} &= - \dot{q_2} \mathbf{\hat{b}_z}\\ - ^{\mathbf{C}}\mathbf{\omega}^{\mathbf{D}} &= \dot{q_3} \mathbf{\hat{c}_y}\\ - ^{\mathbf{N}}\mathbf{\omega}^{\mathbf{D}} &= \dot{q_1} \mathbf{\hat{a}_x} - - \dot{q_2} \mathbf{\hat{b}_z} + \dot{q_3} \mathbf{\hat{c}_y}\\ - -Note the signs used in the angular velocity definitions, which are related to -how the displacement angle is defined in this case. - - -This theorem makes defining angular velocities of multibody systems much -easier, as the angular velocity of a body in a chain needs to only be defined -to the previous body in order to be fully defined (and the first body needs -to be defined in the desired reference frame). The following figure shows an -example of when using this theorem can make things easier. - -.. image:: kin_angvel3.* - :height: 250 - :width: 400 - :align: center - -Here we can easily write the angular velocity of the body -:math:`\mathbf{D}` in the reference frame of the first body :math:`\mathbf{A}`: - -.. math:: - ^\mathbf{A}\mathbf{\omega}^\mathbf{D} = w_1 \mathbf{\hat{p_1}} + - w_2 \mathbf{\hat{p_2}} + w_3 \mathbf{\hat{p_3}}\\ - -It is very important to remember to only use this with angular velocities; you -cannot use this theorem with the velocities of points. - -There is another theorem commonly used: the derivative theorem. It provides an -alternative method (which can be easier) to calculate the time derivative of a -vector in a reference frame: - -.. math:: - \frac{^{\mathbf{N}} d \mathbf{v}}{dt} = \frac{^{\mathbf{B}} d \mathbf{v}}{dt} - + ^{\mathbf{N}}\mathbf{\omega}^{\mathbf{B}} \times \mathbf{v} - -The vector :math:`\mathbf{v}` can be any vector quantity: a position vector, -a velocity vector, angular velocity vector, etc. Instead of taking the time -derivative of the vector in :math:`\mathbf{N}`, we take it in -:math:`\mathbf{B}`, where :math:`\mathbf{B}` can be any reference frame or -body, usually one in which it is easy to take the derivative on -:math:`\mathbf{v}` in (:math:`\mathbf{v}` is usually composed only of the basis -vector set belonging to :math:`\mathbf{B}`). Then we add the cross product of -the angular velocity of our newer frame, -:math:`^{\mathbf{N}}\mathbf{\omega}^{\mathbf{B}}` and our vector quantity -:math:`\mathbf{v}`. Again, you can choose any alternative frame for this. -Examples follow: - -.. % need multiple examples here showing the derivative theorem - - -Angular Acceleration --------------------- -Angular acceleration refers to the time rate of change of the angular velocity -vector. Just as the angular velocity vector is for a body and is specified in a -frame, the angular acceleration vector is for a body and is specified in a -frame: :math:`^{\mathbf{N}}\mathbf{\alpha}^{\mathbf{B}}`, or the angular -acceleration of :math:`\mathbf{B}` in :math:`\mathbf{N}`, which is a vector. - -Calculating the angular acceleration is relatively straight forward: - -.. math:: - ^{\mathbf{N}}\mathbf{\alpha}^{\mathbf{B}} = - \frac{^{\mathbf{N}} d ^{\mathbf{N}}\mathbf{\omega}^{\mathbf{B}}}{dt} - -Note that this can be calculated with the derivative theorem, and when the -angular velocity is defined in a body fixed frame, becomes quite simple: - -.. math:: - - ^{\mathbf{N}}\mathbf{\alpha}^{\mathbf{B}} &= - \frac{^{\mathbf{N}} d ^{\mathbf{N}}\mathbf{\omega}^{\mathbf{B}}}{dt}\\ - - ^{\mathbf{N}}\mathbf{\alpha}^{\mathbf{B}} &= - \frac{^{\mathbf{B}} d ^{\mathbf{N}}\mathbf{\omega}^{\mathbf{B}}}{dt} - + ^{\mathbf{N}}\mathbf{\omega}^{\mathbf{B}} \times - ^{\mathbf{N}}\mathbf{\omega}^{\mathbf{B}}\\ - - \textrm{if } ^{\mathbf{N}}\mathbf{\omega}^{\mathbf{B}} &= - w_x \mathbf{\hat{b}_x} + w_y \mathbf{\hat{b}_y} + w_z \mathbf{\hat{b}_z}\\ - - \textrm{then } ^{\mathbf{N}}\mathbf{\alpha}^{\mathbf{B}} &= - \frac{^{\mathbf{B}} d ^{\mathbf{N}}\mathbf{\omega}^{\mathbf{B}}}{dt} - + \underbrace{^{\mathbf{N}}\mathbf{\omega}^{\mathbf{B}} \times - ^{\mathbf{N}}\mathbf{\omega}^{\mathbf{B}}}_{ - \textrm{this is 0 by definition}}\\ - - ^{\mathbf{N}}\mathbf{\alpha}^{\mathbf{B}}&=\frac{d w_x}{dt}\mathbf{\hat{b}_x} - + \frac{d w_y}{dt}\mathbf{\hat{b}_y} + \frac{d w_z}{dt}\mathbf{\hat{b}_z}\\ - - ^{\mathbf{N}}\mathbf{\alpha}^{\mathbf{B}}&= \dot{w_x}\mathbf{\hat{b}_x} + - \dot{w_y}\mathbf{\hat{b}_y} + \dot{w_z}\mathbf{\hat{b}_z}\\ - -Again, this is only for the case in which the angular velocity of the body is -defined in body fixed components. - - - -Point Velocity & Acceleration ------------------------------ - -Consider a point, :math:`P`: we can define some characteristics of the point. -First, we can define a position vector from some other point to :math:`P`. -Second, we can define the velocity vector of :math:`P` in a reference frame of -our choice. Third, we can define the acceleration vector of :math:`P` in a -reference frame of our choice. - -These three quantities are read as: - -.. math:: - \mathbf{r}^{OP} \textrm{, the position vector from } O - \textrm{ to }P\\ - ^{\mathbf{N}}\mathbf{v}^P \textrm{, the velocity of } P - \textrm{ in the reference frame } \mathbf{N}\\ - ^{\mathbf{N}}\mathbf{a}^P \textrm{, the acceleration of } P - \textrm{ in the reference frame } \mathbf{N}\\ - -Note that the position vector does not have a frame associated with it; this is -because there is no time derivative involved, unlike the velocity and -acceleration vectors. - -We can find these quantities for a simple example easily: - -.. image:: kin_1.* - :height: 300 - :width: 300 - :align: center - -.. math:: - \textrm{Let's define: } - \mathbf{r}^{OP} &= q_x \mathbf{\hat{n}_x} + q_y \mathbf{\hat{n}_y}\\ - ^{\mathbf{N}}\mathbf{v}^P &= \frac{^{\mathbf{N}} d \mathbf{r}^{OP}}{dt}\\ - \textrm{then we can calculate: } - ^{\mathbf{N}}\mathbf{v}^P &= \dot{q}_x\mathbf{\hat{n}_x} + - \dot{q}_y\mathbf{\hat{n}_y}\\ - \textrm{and :} - ^{\mathbf{N}}\mathbf{a}^P &= \frac{^{\mathbf{N}} d - ^{\mathbf{N}}\mathbf{v}^P}{dt}\\ - ^{\mathbf{N}}\mathbf{a}^P &= \ddot{q}_x\mathbf{\hat{n}_x} + - \ddot{q}_y\mathbf{\hat{n}_y}\\ - -It is critical to understand in the above example that the point :math:`O` is -fixed in the reference frame :math:`\mathbf{N}`. There is no addition theorem -for translational velocities; alternatives will be discussed later though. -Also note that the position of every point might not -always need to be defined to form the dynamic equations of motion. -When you don't want to define the position vector of a point, you can start by -just defining the velocity vector. For the above example: - -.. math:: - \textrm{Let us instead define the velocity vector as: } - ^{\mathbf{N}}\mathbf{v}^P &= u_x \mathbf{\hat{n}_x} + - u_y \mathbf{\hat{n}_y}\\ - \textrm{then acceleration can be written as: } - ^{\mathbf{N}}\mathbf{a}^P &= \dot{u}_x \mathbf{\hat{n}_x} + - \dot{u}_y \mathbf{\hat{n}_y}\\ - - -There will often be cases when the velocity of a point is desired and a related -point's velocity is known. For the cases in which we have two points fixed on a -rigid body, we use the 2-Point Theorem: - -.. image:: kin_2pt.* - :height: 300 - :width: 300 - :align: center - -Let's say we know the velocity of the point :math:`S` and the angular -velocity of the body :math:`\mathbf{B}`, both defined in the reference frame -:math:`\mathbf{N}`. We can calculate the velocity and acceleration -of the point :math:`P` in :math:`\mathbf{N}` as follows: - -.. math:: - ^{\mathbf{N}}\mathbf{v}^P &= ^\mathbf{N}\mathbf{v}^S + - ^\mathbf{N}\mathbf{\omega}^\mathbf{B} \times \mathbf{r}^{SP}\\ - ^{\mathbf{N}}\mathbf{a}^P &= ^\mathbf{N}\mathbf{a}^S + - ^\mathbf{N}\mathbf{\alpha}^\mathbf{B} \times \mathbf{r}^{SP} + - ^\mathbf{N}\mathbf{\omega}^\mathbf{B} \times - (^\mathbf{N}\mathbf{\omega}^\mathbf{B} \times \mathbf{r}^{SP})\\ - -When only one of the two points is fixed on a body, the 1 point theorem is used -instead. - -.. image:: kin_1pt.* - :height: 400 - :width: 400 - :align: center - -Here, the velocity of point :math:`S` is known in the frame :math:`\mathbf{N}`, -the angular velocity of :math:`\mathbf{B}` is known in :math:`\mathbf{N}`, and -the velocity of the point :math:`P` is known in the frame associated with body -:math:`\mathbf{B}`. We can then write the velocity and acceleration of -:math:`P` in :math:`\mathbf{N}` as: - -.. math:: - ^{\mathbf{N}}\mathbf{v}^P &= ^\mathbf{B}\mathbf{v}^P + - ^\mathbf{N}\mathbf{v}^S + ^\mathbf{N}\mathbf{\omega}^\mathbf{B} \times - \mathbf{r}^{SP}\\ - - ^{\mathbf{N}}\mathbf{a}^P &= ^\mathbf{B}\mathbf{a}^S + - ^\mathbf{N}\mathbf{a}^O + ^\mathbf{N}\mathbf{\alpha}^\mathbf{B} - \times \mathbf{r}^{SP} + ^\mathbf{N}\mathbf{\omega}^\mathbf{B} \times - (^\mathbf{N}\mathbf{\omega}^\mathbf{B} \times \mathbf{r}^{SP}) + - 2 ^\mathbf{N}\mathbf{\omega}^\mathbf{B} \times ^\mathbf{B} \mathbf{v}^P \\ - - -Examples of applications of the 1 point and 2 point theorem follow. - -.. image:: kin_2.* - :height: 300 - :width: 400 - :align: center - -This example has a disc translating and rotating in a plane. We can easily -define the angular velocity of the body :math:`\mathbf{B}` and velocity of the -point :math:`O`: - -.. math:: - ^\mathbf{N}\mathbf{\omega}^\mathbf{B} &= u_3 \mathbf{\hat{n}_z} = u_3 - \mathbf{\hat{b}_z}\\ - ^\mathbf{N}\mathbf{v}^O &= u_1 \mathbf{\hat{n}_x} + u_2 \mathbf{\hat{n}_y}\\ - -and accelerations can be written as: - -.. math:: - ^\mathbf{N}\mathbf{\alpha}^\mathbf{B} &= \dot{u_3} \mathbf{\hat{n}_z} = - \dot{u_3} \mathbf{\hat{b}_z}\\ - ^\mathbf{N}\mathbf{a}^O &= \dot{u_1} \mathbf{\hat{n}_x} + \dot{u_2} - \mathbf{\hat{n}_y}\\ - -We can use the 2 point theorem to calculate the velocity and acceleration of -point :math:`P` now. - -.. math:: - \mathbf{r}^{OP} &= R \mathbf{\hat{b}_x}\\ - ^\mathbf{N}\mathbf{v}^P &= ^\mathbf{N}\mathbf{v}^O + - ^\mathbf{N}\mathbf{\omega}^\mathbf{B} \times \mathbf{r}^{OP}\\ - ^\mathbf{N}\mathbf{v}^P &= u_1 \mathbf{\hat{n}_x} + u_2 \mathbf{\hat{n}_y} - + u_3 \mathbf{\hat{b}_z} \times R \mathbf{\hat{b}_x} = u_1 - \mathbf{\hat{n}_x} + u_2 \mathbf{\hat{n}_y} + u_3 R \mathbf{\hat{b}_y}\\ - ^{\mathbf{N}}\mathbf{a}^P &= ^\mathbf{N}\mathbf{a}^O + - ^\mathbf{N}\mathbf{\alpha}^\mathbf{B} \times \mathbf{r}^{OP} + - ^\mathbf{N}\mathbf{\omega}^\mathbf{B} \times - (^\mathbf{N}\mathbf{\omega}^\mathbf{B} \times \mathbf{r}^{OP})\\ - ^{\mathbf{N}}\mathbf{a}^P &= \dot{u_1} \mathbf{\hat{n}_x} + \dot{u_2} - \mathbf{\hat{n}_y} + \dot{u_3}\mathbf{\hat{b}_z}\times R \mathbf{\hat{b}_x} - +u_3\mathbf{\hat{b}_z}\times(u_3\mathbf{\hat{b}_z}\times - R\mathbf{\hat{b}_x})\\ - ^{\mathbf{N}}\mathbf{a}^P &= \dot{u_1} \mathbf{\hat{n}_x} + \dot{u_2} - \mathbf{\hat{n}_y} + R\dot{u_3}\mathbf{\hat{b}_y} - R u_3^2 - \mathbf{\hat{b}_x}\\ - -.. image:: kin_3.* - :height: 200 - :width: 200 - :align: center - - -In this example we have a double pendulum. We can use the two point theorem -twice here in order to find the velocity of points :math:`Q` and :math:`P`; -point :math:`O`'s velocity is zero in :math:`\mathbf{N}`. - -.. math:: - \mathbf{r}^{OQ} &= l \mathbf{\hat{b}_x}\\ - \mathbf{r}^{QP} &= l \mathbf{\hat{c}_x}\\ - ^\mathbf{N}\mathbf{\omega}^\mathbf{B} &= u_1 \mathbf{\hat{b}_z}\\ - ^\mathbf{N}\mathbf{\omega}^\mathbf{C} &= u_2 \mathbf{\hat{c}_z}\\ - ^\mathbf{N}\mathbf{v}^Q &= ^\mathbf{N}\mathbf{v}^O + - ^\mathbf{N}\mathbf{\omega}^\mathbf{B} \times \mathbf{r}^{OQ}\\ - ^\mathbf{N}\mathbf{v}^Q &= u_1 l \mathbf{\hat{b}_y}\\ - ^\mathbf{N}\mathbf{v}^P &= ^\mathbf{N}\mathbf{v}^Q + - ^\mathbf{N}\mathbf{\omega}^\mathbf{C} \times \mathbf{r}^{QP}\\ - ^\mathbf{N}\mathbf{v}^Q &= u_1 l \mathbf{\hat{b}_y} +u_2 \mathbf{\hat{c}_z} - \times l \mathbf{\hat{c}_x}\\ - ^\mathbf{N}\mathbf{v}^Q &= u_1 l\mathbf{\hat{b}_y}+u_2 l\mathbf{\hat{c}_y}\\ - -.. image:: kin_4.* - :height: 400 - :width: 300 - :align: center - -In this example we have a particle moving on a ring; the ring is supported by a -rod which can rotate about the :math:`\mathbf{\hat{n}_x}` axis. First we use -the two point theorem to find the velocity of the center point of the ring, -:math:`Q`, then use the 1 point theorem to find the velocity of the particle on -the ring. - -.. math:: - ^\mathbf{N}\mathbf{\omega}^\mathbf{C} &= u_1 \mathbf{\hat{n}_x}\\ - \mathbf{r}^{OQ} &= -l \mathbf{\hat{c}_z}\\ - ^\mathbf{N}\mathbf{v}^Q &= u_1 l \mathbf{\hat{c}_y}\\ - \mathbf{r}^{QP} &= R(cos(q_2) \mathbf{\hat{c}_x} - + sin(q_2) \mathbf{\hat{c}_y} )\\ - ^\mathbf{C}\mathbf{v}^P &= R u_2 (-sin(q_2) \mathbf{\hat{c}_x} - + cos(q_2) \mathbf{\hat{c}_y} )\\ - ^\mathbf{N}\mathbf{v}^P &= ^\mathbf{C}\mathbf{v}^P +^\mathbf{N}\mathbf{v}^Q - + ^\mathbf{N}\mathbf{\omega}^\mathbf{C} \times \mathbf{r}^{QP}\\ - ^\mathbf{N}\mathbf{v}^P &= R u_2 (-sin(q_2) \mathbf{\hat{c}_x} - + cos(q_2) \mathbf{\hat{c}_y} ) + u_1 l \mathbf{\hat{c}_y} + - u_1 \mathbf{\hat{c}_x} \times R(cos(q_2) \mathbf{\hat{c}_x} - + sin(q_2) \mathbf{\hat{c}_y}\\ - ^\mathbf{N}\mathbf{v}^P &= - R u_2 sin(q_2) \mathbf{\hat{c}_x} - + (R u_2 cos(q_2)+u_1 l)\mathbf{\hat{c}_y} + R u_1 sin(q_2) - \mathbf{\hat{c}_z}\\ - -A final topic in the description of velocities of points is that of rolling, or -rather, rolling without slip. Two bodies are said to be rolling without slip if -and only if the point of contact on each body has the same velocity in another -frame. See the following figure: - -.. image:: kin_rolling.* - :height: 250 - :width: 450 - :align: center - -This is commonly used to form the velocity of a point on one object rolling on -another fixed object, such as in the following example: - -.. % rolling disc kinematics here - - -Kinematics in SymPy's Mechanics -=============================== - -It should be clear by now that the topic of kinematics here has been mostly -describing the correct way to manipulate vectors into representing the -velocities of points. Within :mod:`mechanics` there are convenient methods for -storing these velocities associated with frames and points. We'll now revisit -the above examples and show how to represent them in :mod:`sympy`. - -The topic of reference frame creation has already been covered. When a -``ReferenceFrame`` is created though, it automatically calculates the angular -velocity of the frame using the time derivative of the DCM and the angular -velocity definition. :: - - >>> from sympy import Symbol, sin, cos - >>> from sympy.physics.mechanics import * - >>> mechanics_printing() - >>> N = ReferenceFrame('N') - >>> q1 = dynamicsymbols('q1') - >>> A = N.orientnew('A', 'Axis', [q1, N.x]) - >>> A.ang_vel_in(N) - q1'*N.x - -Note that the angular velocity can be defined in an alternate way: :: - - >>> B = ReferenceFrame('B') - >>> u1 = dynamicsymbols('u1') - >>> B.set_ang_vel(N, u1 * B.y) - >>> B.ang_vel_in(N) - u1*B.y - >>> N.ang_vel_in(B) - - u1*B.y - -Both upon frame creation during ``orientnew`` and when calling ``set_ang_vel``, -the angular velocity is set in both frames involved, as seen above. - -.. image:: kin_angvel2.* - :height: 300 - :width: 450 - :align: center - -Here we have multiple bodies with angular velocities defined relative to each -other. This is coded as: :: - - >>> N = ReferenceFrame('N') - >>> A = ReferenceFrame('A') - >>> B = ReferenceFrame('B') - >>> C = ReferenceFrame('C') - >>> D = ReferenceFrame('D') - >>> u1, u2, u3 = dynamicsymbols('u1 u2 u3') - >>> A.set_ang_vel(N, 0) - >>> B.set_ang_vel(A, u1 * A.x) - >>> C.set_ang_vel(B, -u2 * B.z) - >>> D.set_ang_vel(C, u3 * C.y) - >>> D.ang_vel_in(N) - u3*C.y - u2*B.z + u1*A.x - -In :mod:`mechanics` the shortest path between two frames is used when finding -the angular velocity. That would mean if we went back and set: :: - - >>> D.set_ang_vel(N, 0) - >>> D.ang_vel_in(N) - 0 - -The path that was just defined is what is used. -This can cause problems though, as now the angular -velocity definitions are inconsistent. It is recommended that you avoid -doing this. - -.. % put some stuff to go with derivative theorem here - -Points are a translational analog to the rotational ``ReferenceFrame``. -Creating a ``Point`` can be done in two ways, like ``ReferenceFrame``: :: - - >>> O = Point('O') - >>> P = O.locatenew('P', 3 * N.x + N.y) - >>> P.pos_from(O) - 3*N.x + N.y - >>> Q = Point('Q') - >>> Q.set_pos(P, N.z) - >>> Q.pos_from(P) - N.z - >>> Q.pos_from(O) - 3*N.x + N.y + N.z - -Similar to ``ReferenceFrame``, the position vector between two points is found -by the shortest path (number of intermediate points) between them. Unlike -rotational motion, there is no addition theorem for the velocity of points. In -order to have the velocity of a ``Point`` in a ``ReferenceFrame``, you have to -set the value. :: - - >>> O = Point('O') - >>> O.set_vel(N, u1*N.x) - >>> O.vel(N) - u1*N.x - -For both translational and rotational accelerations, the value is computed by -taking the time derivative of the appropriate velocity, unless the user sets it -otherwise. - - >>> O.acc(N) - u1'*N.x - >>> O.set_acc(N, u2*u1*N.y) - >>> O.acc(N) - u1*u2*N.y - - -Next is a description of the 2 point and 1 point theorems, as used in -``sympy``. - -.. image:: kin_2.* - :height: 300 - :width: 400 - :align: center - -First is the translating, rotating disc. :: - - >>> N = ReferenceFrame('N') - >>> u1, u2, u3 = dynamicsymbols('u1 u2 u3') - >>> R = Symbol('R') - >>> B = ReferenceFrame('B') - >>> O = Point('O') - >>> O.set_vel(N, u1 * N.x + u2 * N.y) - >>> P = O.locatenew('P', R * B.x) - >>> B.set_ang_vel(N, u3 * B.z) - >>> P.v2pt_theory(O, N, B) - u1*N.x + u2*N.y + R*u3*B.y - >>> P.a2pt_theory(O, N, B) - u1'*N.x + u2'*N.y - R*u3**2*B.x + R*u3'*B.y - -We will also cover implementation of the 1 point theorem. - -.. image:: kin_4.* - :height: 400 - :width: 300 - :align: center - -This is the particle moving on a ring, again. :: - - >>> N = ReferenceFrame('N') - >>> u1, u2 = dynamicsymbols('u1 u2') - >>> q1, q2 = dynamicsymbols('q1 q2') - >>> l = Symbol('l') - >>> R = Symbol('R') - >>> C = N.orientnew('C', 'Axis', [q1, N.x]) - >>> C.set_ang_vel(N, u1 * N.x) - >>> O = Point('O') - >>> O.set_vel(N, 0) - >>> Q = O.locatenew('Q', -l * C.z) - >>> P = Q.locatenew('P', R * (cos(q2) * C.x + sin(q2) * C.y)) - >>> P.set_vel(C, R * u2 * (-sin(q2) * C.x + cos(q2) * C.y)) - >>> Q.v2pt_theory(O, N, C) - l*u1*C.y - >>> P.v1pt_theory(Q, N, C) - - R*u2*sin(q2)*C.x + (R*u2*cos(q2) + l*u1)*C.y + R*u1*sin(q2)*C.z diff --git a/dev-py3k/_sources/modules/physics/mechanics/masses.txt b/dev-py3k/_sources/modules/physics/mechanics/masses.txt deleted file mode 100644 index 4e10111d692..00000000000 --- a/dev-py3k/_sources/modules/physics/mechanics/masses.txt +++ /dev/null @@ -1,395 +0,0 @@ -======================================================= -Mechanics: Masses, Inertias, Particles and Rigid Bodies -======================================================= - -This document will describe how to represent masses and inertias in -:mod:`mechanics` and use of the ``RigidBody`` and ``Particle`` classes. - -It is assumed that the reader is familiar with the basics of these topics, such -as finding the center of mass for a system of particles, how to manipulate an -inertia tensor, and the definition of a particle and rigid body. Any advanced -dynamics text can provide a reference for these details. - -Mass -==== - -The only requirement for a mass is that it needs to be a ``sympify``-able -expression. Keep in mind that masses can be time varying. - -Particle -======== - -Particles are created with the class ``Particle`` in :mod:`mechanics`. -A ``Particle`` object has an associated point and an associated mass which are -the only two attributes of the object.:: - - >>> from sympy.physics.mechanics import Particle, Point - >>> from sympy import Symbol - >>> m = Symbol('m') - >>> po = Point('po') - >>> # create a particle container - >>> pa = Particle('pa', po, m) - -The associated point contains the position, velocity and acceleration of the -particle. :mod:`mechanics` allows one to perform kinematic analysis of points -separate from their association with masses. - -Inertia -======= - -Dyadics are used to define the inertia of bodies within :mod:`mechanics`. -Inertia dyadics can be defined explicitly but the ``inertia`` function is -typically much more convenient for the user:: - - >>> from sympy.physics.mechanics import ReferenceFrame, inertia - >>> N = ReferenceFrame('N') - - Supply a reference frame and the moments of inertia if the object - is symmetrical: - - >>> inertia(N, 1, 2, 3) - (N.x|N.x) + 2*(N.y|N.y) + 3*(N.z|N.z) - - Supply a reference frame along with the products and moments of inertia - for a general object: - - >>> inertia(N, 1, 2, 3, 4, 5, 6) - (N.x|N.x) + 4*(N.x|N.y) + 6*(N.x|N.z) + 4*(N.y|N.x) + 2*(N.y|N.y) + - 5*(N.y|N.z) + 6*(N.z|N.x) + 5*(N.z|N.y) + 3*(N.z|N.z) - -Notice that the ``inertia`` function returns a dyadic with each component -represented as two unit vectors separated by a ``|``. Refer to the -:ref:`Dyadic` section for more information about dyadics. - -Rigid Body -========== - -Rigid bodies are created in a similar fashion as particles. The ``RigidBody`` -class generates objects with four attributes: mass, center of mass, a reference -frame, and an inertia tuple:: - - >>> from sympy import Symbol - >>> from sympy.physics.mechanics import ReferenceFrame, Point, RigidBody - >>> from sympy.physics.mechanics import outer - >>> m = Symbol('m') - >>> A = ReferenceFrame('A') - >>> P = Point('P') - >>> I = outer(A.x, A.x) - >>> # create a rigid body - >>> B = RigidBody('B', P, A, m, (I, P)) - -The mass is specified exactly as is in a particle. Similar to the -``Particle``'s ``.point``, the ``RigidBody``'s center of mass, ``.masscenter`` -must be specified. The reference frame is stored in an analogous fashion and -holds information about the body's orientation and angular velocity. Finally, -the inertia for a rigid body needs to be specified about a point. In -:mod:`mechanics`, you are allowed to specify any point for this. The most -common is the center of mass, as shown in the above code. If a point is selected -which is not the center of mass, ensure that the position between the point and -the center of mass has been defined. The inertia is specified as a tuple of length -two with the first entry being a ``Dyadic`` and the second entry being a -``Point`` of which the inertia dyadic is defined about. - -.. _Dyadic: - -Dyadic -====== - -In :mod:`mechanics`, dyadics are used to represent inertia ([Kane1985]_, -[WikiDyadics]_, [WikiDyadicProducts]_). A dyadic is a linear polynomial of -component unit dyadics, similar to a vector being a linear polynomial of -component unit vectors. A dyadic is the outer product between two vectors which -returns a new quantity representing the juxtaposition of these two vectors. For -example: - -.. math:: - \mathbf{\hat{a}_x} \otimes \mathbf{\hat{a}_x} &= \mathbf{\hat{a}_x} - \mathbf{\hat{a}_x}\\ - \mathbf{\hat{a}_x} \otimes \mathbf{\hat{a}_y} &= \mathbf{\hat{a}_x} - \mathbf{\hat{a}_y}\\ - -Where :math:`\mathbf{\hat{a}_x}\mathbf{\hat{a}_x}` and -`\mathbf{\hat{a}_x}\mathbf{\hat{a}_y}` are the outer products obtained by -multiplying the left side as a column vector by the right side as a row vector. -Note that the order is significant. - -Some additional properties of a dyadic are: - -.. math:: - (x \mathbf{v}) \otimes \mathbf{w} &= \mathbf{v} \otimes (x \mathbf{w}) = x - (\mathbf{v} \otimes \mathbf{w})\\ - \mathbf{v} \otimes (\mathbf{w} + \mathbf{u}) &= \mathbf{v} \otimes \mathbf{w} - + \mathbf{v} \otimes \mathbf{u}\\ - (\mathbf{v} + \mathbf{w}) \otimes \mathbf{u} &= \mathbf{v} \otimes \mathbf{u} - + \mathbf{w} \otimes \mathbf{u}\\ - -A vector in a reference frame can be represented as -:math:`\begin{bmatrix}a\\b\\c\end{bmatrix}` or :math:`a \mathbf{\hat{i}} + b -\mathbf{\hat{j}} + c \mathbf{\hat{k}}`. Similarly, a dyadic can be represented -in tensor form: - -.. math:: - \begin{bmatrix} - a_{11} & a_{12} & a_{13} \\ - a_{21} & a_{22} & a_{23} \\ - a_{31} & a_{32} & a_{33} - \end{bmatrix}\\ - -or in dyadic form: - -.. math:: - a_{11} \mathbf{\hat{a}_x}\mathbf{\hat{a}_x} + - a_{12} \mathbf{\hat{a}_x}\mathbf{\hat{a}_y} + - a_{13} \mathbf{\hat{a}_x}\mathbf{\hat{a}_z} + - a_{21} \mathbf{\hat{a}_y}\mathbf{\hat{a}_x} + - a_{22} \mathbf{\hat{a}_y}\mathbf{\hat{a}_y} + - a_{23} \mathbf{\hat{a}_y}\mathbf{\hat{a}_z} + - a_{31} \mathbf{\hat{a}_z}\mathbf{\hat{a}_x} + - a_{32} \mathbf{\hat{a}_z}\mathbf{\hat{a}_y} + - a_{33} \mathbf{\hat{a}_z}\mathbf{\hat{a}_z}\\ - -Just as with vectors, the later representation makes it possible to keep track -of which frames the dyadic is defined with respect to. Also, the two -components of each term in the dyadic need not be in the same frame. The -following is valid: - -.. math:: - \mathbf{\hat{a}_x} \otimes \mathbf{\hat{b}_y} = \mathbf{\hat{a}_x} - \mathbf{\hat{b}_y} - -Dyadics can also be crossed and dotted with vectors; again, order matters: - -.. math:: - \mathbf{\hat{a}_x}\mathbf{\hat{a}_x} \cdot \mathbf{\hat{a}_x} &= - \mathbf{\hat{a}_x}\\ - \mathbf{\hat{a}_y}\mathbf{\hat{a}_x} \cdot \mathbf{\hat{a}_x} &= - \mathbf{\hat{a}_y}\\ - \mathbf{\hat{a}_x}\mathbf{\hat{a}_y} \cdot \mathbf{\hat{a}_x} &= 0\\ - \mathbf{\hat{a}_x} \cdot \mathbf{\hat{a}_x}\mathbf{\hat{a}_x} &= - \mathbf{\hat{a}_x}\\ - \mathbf{\hat{a}_x} \cdot \mathbf{\hat{a}_x}\mathbf{\hat{a}_y} &= - \mathbf{\hat{a}_y}\\ - \mathbf{\hat{a}_x} \cdot \mathbf{\hat{a}_y}\mathbf{\hat{a}_x} &= 0\\ - \mathbf{\hat{a}_x} \times \mathbf{\hat{a}_y}\mathbf{\hat{a}_x} &= - \mathbf{\hat{a}_z}\mathbf{\hat{a}_x}\\ - \mathbf{\hat{a}_x} \times \mathbf{\hat{a}_x}\mathbf{\hat{a}_x} &= 0\\ - \mathbf{\hat{a}_y}\mathbf{\hat{a}_x} \times \mathbf{\hat{a}_z} &= - - \mathbf{\hat{a}_y}\mathbf{\hat{a}_y}\\ - -One can also take the time derivative of dyadics or express them in different -frames, just like with vectors. - -Linear Momentum -=============== - -The linear momentum of a particle P is defined as: - -.. math:: - L_P = m\mathbf{v} - -where :math:`m` is the mass of the particle P and :math:`\mathbf{v}` is the -velocity of the particle in the inertial frame.[Likins1973]_. - -Similarly the linear momentum of a rigid body is defined as: - -.. math:: - L_B = m\mathbf{v^*} - -where :math:`m` is the mass of the rigid body, B, and :math:`\mathbf{v^*}` is -the velocity of the mass center of B in the inertial frame. - -Angular Momentum -================ - -The angular momentum of a particle P about an arbitrary point O in an inertial -frame N is defined as: - -.. math:: - ^N \mathbf{H} ^ {P/O} = \mathbf{r} \times m\mathbf{v} - -where :math:`\mathbf{r}` is a position vector from point O to the particle of -mass :math:`m` and :math:`\mathbf{v}` is the velocity of the particle in the -inertial frame. - -Similarly the angular momentum of a rigid body B about a point O in an inertial -frame N is defined as: - -.. math:: - ^N \mathbf{H} ^ {B/O} = ^N \mathbf{H} ^ {B/B^*} + ^N \mathbf{H} ^ {B^*/O} - -where the angular momentum of the body about it's mass center is: - -.. math:: - ^N \mathbf{H} ^ {B/B^*} = \mathbf{I^*} \cdot \omega - -and the angular momentum of the mass center about O is: - -.. math:: - ^N \mathbf{H} ^ {B^*/O} = \mathbf{r^*} \times m \mathbf{v^*} - -where :math:`\mathbf{I^*}` is the central inertia dyadic of rigid body B, -:math:`\omega` is the inertial angular velocity of B, :math:`\mathbf{r^*}` is a -position vector from point O to the mass center of B, :math:`m` is the mass of -B and :math:`\mathbf{v^*}` is the velocity of the mass center in the inertial -frame. - -Using momenta functions in Mechanics -==================================== - -The following example shows how to use the momenta functions in -:mod:`mechanics`. - -One begins by creating the requisite symbols to describe the system. Then -the reference frame is created and the kinematics are done. :: - - >> from sympy import symbols - >> from sympy.physics.mechanics import dynamicsymbols, ReferenceFrame - >> from sympy.physics.mechanics import RigidBody, Particle, Point, outer - >> from symp.physics.mechanics import linear_momentum, angular_momentum - >> m, M, l1 = symbols('m M l1') - >> q1d = dynamicsymbols('q1d') - >> N = ReferenceFrame('N') - >> O = Point('O') - >> O.set_vel(N, 0 * N.x) - >> Ac = O.locatenew('Ac', l1 * N.x) - >> P = Ac.locatenew('P', l1 * N.x) - >> a = ReferenceFrame('a') - >> a.set_ang_vel(N, q1d * N.z) - >> Ac.v2pt_theory(O, N, a) - >> P.v2pt_theory(O, N, a) - -Finally, the bodies that make up the system are created. In this case the -system consists of a particle Pa and a RigidBody A. :: - - >> Pa = Particle('Pa', P, m) - >> I = outer(N.z, N.z) - >> A = RigidBody('A', Ac, a, M, (I, Ac)) - -Then one can either choose to evaluate the the momenta of individual components -of the system or of the entire system itself. :: - - >> linear_momentum(N,A) - M*l1*q1d*N.y - >> angular_momentum(O, N, Pa) - 4*l1**2*m*q1d*N.z - >> linear_momentum(N, A, Pa) - (M*l1*q1d + 2*l1*m*q1d)*N.y - >> angular_momentum(O, N, A, Pa) - (4*l1**2*m*q1d + q1d)*N.z - -It should be noted that the user can determine either momenta in any frame -in :mod:`mechanics` as the user is allowed to specify the reference frame when -calling the function. In other words the user is not limited to determining -just inertial linear and angular momenta. Please refer to the docstrings on -each function to learn more about how each function works precisely. - -Kinetic Energy -============== - -The kinetic energy of a particle P is defined as - -.. math:: - T_P = \frac{1}{2} m \mathbf{v^2} - -where :math:`m` is the mass of the particle P and :math:`\mathbf{v}` -is the velocity of the particle in the inertial frame. - -Similarly the kinetic energy of a rigid body B is defined as - -.. math:: - T_B = T_t + T_r - -where the translational kinetic energy is given by: - -.. math:: - T_t = \frac{1}{2} m \mathbf{v^*} \cdot \mathbf{v^*} - -and the rotational kinetic energy is given by: - -.. math:: - T_r = \frac{1}{2} \omega \cdot \mathbf{I^*} \cdot \omega - -where :math:`m` is the mass of the rigid body, :math:`\mathbf{v^*}` is the -velocity of the mass center in the inertial frame, :math:`\omega` is the -inertial angular velocity of the body and :math:`\mathbf{I^*}` is the central -inertia dyadic. - -Potential Energy -================ - -Potential energy is defined as the energy possessed by a body or system by -virtue of its position or arrangement. - -Since there are a variety of definitions for potential energy, this is not -discussed further here. One can learn more about this in any elementary text -book on dynamics. - -Lagrangian -========== - -The Lagrangian of a body or a system of bodies is defined as: - -.. math:: - \mathcal{L} = T - V - -where :math:`T` and :math:`V` are the kinetic and potential energies -respectively. - -Using energy functions in Mechanics -=================================== - -The following example shows how to use the energy functions in -:mod:`mechanics`. - -As was discussed above in the momenta functions, one first creates the system -by going through an identical procedure. :: - - >> from sympy import symbols - >> from sympy.physics.mechanics import dynamicsymbols, ReferenceFrame, outer - >> from sympy.physics.mechanics import RigidBody, Particle, mechanics_printing - >> from symp.physics.mechanics import kinetic_energy, potential_energy, Point - >> mechanics_printing() - >> m, M, l1, g, h, H = symbols('m M l1 g h H') - >> omega = dynamicsymbols('omega') - >> N = ReferenceFrame('N') - >> O = Point('O') - >> O.set_vel(N, 0 * N.x) - >> Ac = O.locatenew('Ac', l1 * N.x) - >> P = Ac.locatenew('P', l1 * N.x) - >> a = ReferenceFrame('a') - >> a.set_ang_vel(N, omega * N.z) - >> Ac.v2pt_theory(O, N, a) - >> P.v2pt_theory(O, N, a) - >> Pa = Particle('Pa', P, m) - >> I = outer(N.z, N.z) - >> A = RigidBody('A', Ac, a, M, (I, Ac)) - -The user can then determine the kinetic energy of any number of entities of the -system: :: - - >> kinetic_energy(N, Pa) - 2*l1**2*m*q1d**2 - >> kinetic_energy(N, Pa, A) - M*l1**2*q1d**2/2 + 2*l1**2*m*q1d**2 + q1d**2/2 - -It should be noted that the user can determine either kinetic energy relative -to any frame in :mod:`mechanics` as the user is allowed to specify the -reference frame when calling the function. In other words the user is not -limited to determining just inertial kinetic energy. - -For potential energies, the user must first specify the potential energy of -every entity of the system using the :mod:`set_potential_energy` method. The -potential energy of any number of entities comprising the system can then be -determined: :: - - >> Pa.set_potential_energy(m * g * h) - >> A.set_potential_energy(M * g * H) - >> potential_energy(A, Pa) - H*M*g + g*h*m - -One can also determine the Lagrangian for this system: :: - - >> Lagrangian(Pa, A) - -H*M*g + M*l1**2*q1d**2/2 - g*h*m + 2*l1**2*m*q1d**2 + q1d**2/2 - -Please refer to the docstrings to learn more about each function. diff --git a/dev-py3k/_sources/modules/physics/mechanics/reference.txt b/dev-py3k/_sources/modules/physics/mechanics/reference.txt deleted file mode 100644 index f7fa36fd397..00000000000 --- a/dev-py3k/_sources/modules/physics/mechanics/reference.txt +++ /dev/null @@ -1,18 +0,0 @@ -================================ -References for Physics/Mechanics -================================ - -.. [Kane1983] Kane, Thomas R., Peter W. Likins, and David A. Levinson. - Spacecraft Dynamics. New York: McGraw-Hill Book, 1983. Print. -.. [Kane1985] Kane, Thomas R., and David A. Levinson. Dynamics, Theory and - Applications. New York: McGraw-Hill, 1985. Print. -.. [Meijaard2007] Meijaard, J.P., Jim M. Papadopoulos, Andy Ruina, and A.L. - Schwab. "Linearized Dynamics Equations for the Balance and Steer of a Bicycle: - a Benchmark and Review." Proceedings of the Royal Society A: Mathematical, - Physical and Engineering Sciences 463.2084 (2007): 1955-982. Print. -.. [WikiDyadics] "Dyadics." Wikipedia, the Free Encyclopedia. Web. 05 Aug. - 2011. . -.. [WikiDyadicProducts] "Dyadic Product." Wikipedia, the Free Encyclopedia. - Web. 05 Aug. 2011. . -.. [Likins1973] Likins, Peter W. Elements of Engineering Mechanics. - McGraw-Hill, Inc. 1973. Print. diff --git a/dev-py3k/_sources/modules/physics/mechanics/vectors.txt b/dev-py3k/_sources/modules/physics/mechanics/vectors.txt deleted file mode 100644 index e4d3b2d2154..00000000000 --- a/dev-py3k/_sources/modules/physics/mechanics/vectors.txt +++ /dev/null @@ -1,767 +0,0 @@ -================================== -Mechanics: Vector & ReferenceFrame -================================== - - -In :mod:`mechanics`, vectors and reference frames are the "building blocks" of -dynamic systems. This document will describe these mathematically and describe -how to use them with this module's code. - -Vector -====== - -A vector is a geometric object that has a magnitude (or length) and a -direction. Vectors in 3-space are often represented on paper as: - -.. image:: vec_rep.* - :height: 175 - :width: 350 - :align: center - -Vector Algebra -============== - -Vector algebra is the first topic to be discussed. - -Two vectors are said to be equal if and only if (iff) they have the same same -magnitude and orientation. - -Vector Operations ------------------ -Multiple algebraic operations can be done with vectors: addition between -vectors, scalar multiplication, and vector multiplication. - -Vector addition as based on the parallelogram law. - -.. image:: vec_add.* - :height: 200 - :width: 200 - :align: center - -Vector addition is also commutative: - -.. math:: - \mathbf{a} + \mathbf{b} &= \mathbf{b} + \mathbf{a} \\ - (\mathbf{a} + \mathbf{b}) + \mathbf{c} &= \mathbf{a} + (\mathbf{b} + - \mathbf{c}) - -Scalar multiplication is the product of a vector and a scalar; the result is a -vector with the same orientation but whose magnitude is scaled by the scalar. -Note that multiplication by -1 is equivalent to rotating the vector by 180 -degrees about an arbitrary axis in the plane perpendicular to the vector. - -.. image:: vec_mul.* - :height: 150 - :width: 200 - :align: center - -A unit vector is simply a vector whose magnitude is equal to 1. Given any -vector :math:`\mathbf{v}` we can define a unit vector as: - -.. math:: - \mathbf{\hat{n}_v} = \frac{\mathbf{v}}{\Vert \mathbf{v} \Vert} - -Note that every vector can be written as the product of a scalar and unit -vector. - -Three vector products are implemented in :mod:`mechanics`: the dot product, the -cross product, and the outer product. - -The dot product operation maps two vectors to a scalar. It is defined as: - -.. math:: - \mathbf{a} \cdot \mathbf{b} = \Vert \mathbf{a} \Vert \Vert \mathbf{b} - \Vert \cos(\theta)\\ - -where :math:`\theta` is the angle between :math:`\mathbf{a}` and -:math:`\mathbf{b}`. - -The dot product of two unit vectors represent the magnitude of the common -direction; for other vectors, it is the product of the magnitude of the common -direction and the two vectors' magnitudes. The dot product of two perpendicular -is zero. The figure below shows some examples: - -.. image:: vec_dot.* - :height: 250 - :width: 450 - :align: center - -The dot product is commutative: - -.. math:: - \mathbf{a} \cdot \mathbf{b} = \mathbf{b} \cdot \mathbf{a} - -The cross product vector multiplication operation of two vectors returns a -vector: - -.. math:: - \mathbf{a} \times \mathbf{b} = \mathbf{c} - -The vector :math:`\mathbf{c}` has the following properties: it's orientation is -perpendicular to both :math:`\mathbf{a}` and :math:`\mathbf{b}`, it's magnitude -is defined as :math:`\Vert \mathbf{c} \Vert = \Vert \mathbf{a} \Vert \Vert -\mathbf{b} \Vert \sin(\theta)` (where :math:`\theta` is the angle between -:math:`\mathbf{a}` and :math:`\mathbf{b}`), and has a sense defined by using -the right hand rule between :math:`\Vert \mathbf{a} \Vert \Vert \mathbf{b} -\Vert`. The figure below shows this: - -.. image:: vec_cross.* - :height: 350 - :width: 700 - :align: center - -The cross product has the following properties: - -It is not commutative: - -.. math:: - \mathbf{a} \times \mathbf{b} &\neq \mathbf{b} \times \mathbf{a} \\ - \mathbf{a} \times \mathbf{b} &= - \mathbf{b} \times \mathbf{a} - -and not associative: - -.. math:: - (\mathbf{a} \times \mathbf{b} ) \times \mathbf{c} \neq \mathbf{a} \times - (\mathbf{b} \times \mathbf{c}) - -Two parallel vectors will have a zero cross product. - -The outer product between two vectors will not be not be discussed here, but -instead in the inertia section (that is where it is used). Other useful vector -properties and relationships are: - -.. math:: - \alpha (\mathbf{a} + \mathbf{b}) &= \alpha \mathbf{a} + \alpha \mathbf{b}\\ - \mathbf{a} \cdot (\mathbf{b} + \mathbf{c}) &= \mathbf{a} \cdot \mathbf{b} + - \mathbf{a} \cdot \mathbf{c}\\ - \mathbf{a} \times (\mathbf{b} + \mathbf{c}) &= \mathbf{a} \times \mathbf{b} + - \mathbf{a} \times \mathbf{b}\\ - (\mathbf{a} \times \mathbf{b}) \cdot \mathbf{c} & \textrm{ gives the scalar - triple product.}\\ - \mathbf{a} \times (\mathbf{b} \cdot \mathbf{c}) & \textrm{ does not work, - as you cannot cross a vector and a scalar.}\\ - (\mathbf{a} \times \mathbf{b}) \cdot \mathbf{c} &= \mathbf{a} \cdot - (\mathbf{b} \times \mathbf{c})\\ - (\mathbf{a} \times \mathbf{b}) \cdot \mathbf{c} &= (\mathbf{b} \times - \mathbf{c}) \cdot \mathbf{a} = (\mathbf{c} \times \mathbf{a}) \cdot - \mathbf{b}\\ - (\mathbf{a} \times \mathbf{b}) \times \mathbf{c} &= \mathbf{b}(\mathbf{a} - \cdot \mathbf{c}) - \mathbf{a}(\mathbf{b} \cdot \mathbf{c})\\ - \mathbf{a} \times (\mathbf{b} \times \mathbf{c}) &= \mathbf{b}(\mathbf{a} - \cdot \mathbf{c}) - \mathbf{c}(\mathbf{a} \cdot \mathbf{b})\\ - -Alternative Representation --------------------------- -If we have three non-coplanar unit vectors -:math:`\mathbf{\hat{n}_x},\mathbf{\hat{n}_y},\mathbf{\hat{n}_z}`, -we can represent any vector -:math:`\mathbf{a}` as :math:`\mathbf{a} = a_x \mathbf{\hat{n}_x} + a_y -\mathbf{\hat{n}_y} + a_z \mathbf{\hat{n}_z}`. In this situation -:math:`\mathbf{\hat{n}_x},\mathbf{\hat{n}_y},\mathbf{\hat{n}_z}` -are referred to as a basis. :math:`a_x, a_y, a_z` -are called the measure numbers. -Usually the unit vectors are mutually perpendicular, in which case we can refer -to them as an orthonormal basis, and they are usually right-handed. - -To test equality between two vectors, now we can do the following. With -vectors: - -.. math:: - \mathbf{a} &= a_x \mathbf{\hat{n}_x} + a_y \mathbf{\hat{n}_y} + a_z - \mathbf{\hat{n}_z}\\ - \mathbf{b} &= b_x \mathbf{\hat{n}_x} + b_y \mathbf{\hat{n}_y} + b_z - \mathbf{\hat{n}_z}\\ - -We can claim equality if: :math:`a_x = b_x, a_y = b_y, a_z = b_z`. - -Vector addition is then represented, for the same two vectors, as: - -.. math:: - \mathbf{a} + \mathbf{b} = (a_x + b_x)\mathbf{\hat{n}_x} + (a_y + b_y) - \mathbf{\hat{n}_y} + (a_z + b_z) \mathbf{\hat{n}_z} - -Multiplication operations are now defined as: - -.. math:: - \alpha \mathbf{b} &= \alpha b_x \mathbf{\hat{n}_x} + \alpha b_y - \mathbf{\hat{n}_y} + \alpha b_z \mathbf{\hat{n}_z}\\ - \mathbf{a} \cdot \mathbf{b} &= a_x b_x + a_y b_y + a_z b_z\\ - \mathbf{a} \times \mathbf{b} &= - \textrm{det }\begin{bmatrix} \mathbf{\hat{n}_x} & \mathbf{\hat{n}_y} & - \mathbf{\hat{n}_z} \\ a_x & a_y & a_z \\ a_x & a_y & a_z \end{bmatrix}\\ - (\mathbf{a} \times \mathbf{b}) \cdot \mathbf{c} &= - \textrm{det }\begin{bmatrix} a_x & a_y & a_z \\ b_x & b_y & b_z \\ c_x & c_y - & c_z \end{bmatrix}\\ - -To write a vector in a given basis, we can do the follow: - -.. math:: - \mathbf{a} = (\mathbf{a}\cdot\mathbf{\hat{n}_x})\mathbf{\hat{n}_x} + - (\mathbf{a}\cdot\mathbf{\hat{n}_y})\mathbf{\hat{n}_y} + - (\mathbf{a}\cdot\mathbf{\hat{n}_z})\mathbf{\hat{n}_z}\\ - - -Examples --------- -Some numeric examples of these operations follow: - -.. math:: - \mathbf{a} &= \mathbf{\hat{n}_x} + 5 \mathbf{\hat{n}_y}\\ - \mathbf{b} &= \mathbf{\hat{n}_y} + \alpha \mathbf{\hat{n}_z}\\ - \mathbf{a} + \mathbf{b} &= \mathbf{\hat{n}_x} + 6 \mathbf{\hat{n}_y} + \alpha - \mathbf{\hat{n}_z}\\ - \mathbf{a} \cdot \mathbf{b} &= 5\\ - \mathbf{a} \cdot \mathbf{\hat{n}_y} &= 5\\ - \mathbf{a} \cdot \mathbf{\hat{n}_z} &= 0\\ - \mathbf{a} \times \mathbf{b} &= 5 \alpha \mathbf{\hat{n}_x} - \alpha - \mathbf{\hat{n}_y} + \mathbf{\hat{n}_z}\\ - \mathbf{b} \times \mathbf{a} &= -5 \alpha \mathbf{\hat{n}_x} + \alpha - \mathbf{\hat{n}_y} - \mathbf{\hat{n}_z}\\ - - -Vector Calculus -=============== -To deal with the calculus of vectors with moving object, we have to introduce -the concept of a reference frame. A classic example is a train moving along its -tracks, with you and a friend inside. If both you and your friend are sitting, -the relative velocity between the two of you is zero. From an observer outside -the train, you will both have velocity though. - -We will now apply more rigor to this definition. A reference frame is a virtual -"platform" which we choose to observe vector quantities from. If we have a -reference frame :math:`\mathbf{N}`, vector :math:`\mathbf{a}` is said to be -fixed in the frame :math:`\mathbf{N}` if none of its properties ever change -when observed from :math:`\mathbf{N}`. We will typically assign a fixed -orthonormal basis vector set with each reference frame; :math:`\mathbf{N}` will -have :math:`\mathbf{\hat{n}_x}, \mathbf{\hat{n}_y},\mathbf{\hat{n}_z}` as its -basis vectors. - -Derivatives of Vectors ----------------------- - -A vector which is not fixed in a reference frame therefore has changing -properties when observed from that frame. Calculus is the study of change, and -in order to deal with the peculiarities of vectors fixed and not fixed in -different reference frames, we need to be more explicit in our definitions. - -.. image:: vec_fix_notfix.* - :height: 300 - :width: 450 - :align: center - -In the above figure, we have vectors :math:`\mathbf{c,d,e,f}`. If one were to -take the derivative of :math:`\mathbf{e}` with respect to :math:`\theta`: - -.. math:: - \frac{d \mathbf{e}}{d \theta} - -it is not clear what the derivative is. If you are observing from frame -:math:`\mathbf{A}`, it is clearly non-zero. If you are observing from frame -:math:`\mathbf{B}`, the derivative is zero. We will therefore introduce the -frame as part of the derivative notation: - -.. math:: - \frac{^{\mathbf{A}} d \mathbf{e}}{d \theta} &\neq 0 \textrm{, - the derivative of } \mathbf{e} \textrm{ with respect to } \theta - \textrm{ in the reference frame } \mathbf{A}\\ - \frac{^{\mathbf{B}} d \mathbf{e}}{d \theta} &= 0 \textrm{, - the derivative of } \mathbf{e} \textrm{ with respect to } \theta - \textrm{ in the reference frame } \mathbf{B}\\ - \frac{^{\mathbf{A}} d \mathbf{c}}{d \theta} &= 0 \textrm{, - the derivative of } \mathbf{c} \textrm{ with respect to } \theta - \textrm{ in the reference frame } \mathbf{A}\\ - \frac{^{\mathbf{B}} d \mathbf{c}}{d \theta} &\neq 0 \textrm{, - the derivative of } \mathbf{c} \textrm{ with respect to } \theta - \textrm{ in the reference frame } \mathbf{B}\\ - -Here are some additional properties of derivatives of vectors in specific -frames: - -.. math:: - \frac{^{\mathbf{A}} d}{dt}(\mathbf{a} + \mathbf{b}) &= \frac{^{\mathbf{A}} - d\mathbf{a}}{dt} + \frac{^{\mathbf{A}} d\mathbf{b}}{dt}\\ - \frac{^{\mathbf{A}} d}{dt}\gamma \mathbf{a} &= \frac{ d \gamma}{dt}\mathbf{a} - + \gamma\frac{^{\mathbf{A}} d\mathbf{a}}{dt}\\ - \frac{^{\mathbf{A}} d}{dt}(\mathbf{a} \times \mathbf{b}) &= - \frac{^{\mathbf{A}} d\mathbf{a}}{dt} \times \mathbf{b} + - \mathbf{a} \times \frac{^{\mathbf{A}} d\mathbf{b}}{dt}\\ - -Relating Sets of Basis Vectors ------------------------------- - -We need to now define the relationship between two different reference frames; -or how to relate the basis vectors of one frame to another. We can do this -using a direction cosine matrix (DCM). The direction cosine matrix relates -the basis vectors of one frame to another, in the following fashion: - -.. math:: - \begin{bmatrix} - \mathbf{\hat{a}_x} \\ \mathbf{\hat{a}_y} \\ \mathbf{\hat{a}_z} \\ - \end{bmatrix} = - \begin{bmatrix} ^{\mathbf{A}} \mathbf{C}^{\mathbf{B}} \end{bmatrix} - \begin{bmatrix} - \mathbf{\hat{b}_x} \\ \mathbf{\hat{b}_y} \\ \mathbf{\hat{b}_z} \\ - \end{bmatrix} - -When two frames (say, :math:`\mathbf{A}` & :math:`\mathbf{B}`) are initially -aligned, then one frame has all of its basis vectors rotated around an axis -which is aligned with a basis vector, we say the frames are related by a simple -rotation. The figure below shows this: - -.. image:: simp_rot.* - :height: 250 - :width: 250 - :align: center - -The above rotation is a simple rotation about the Z axis by an angle -:math:`\theta`. Note that after the rotation, the basis vectors -:math:`\mathbf{\hat{a}_z}` and :math:`\mathbf{\hat{b}_z}` are still aligned. - -This rotation can be characterized by the following direction cosine matrix: - -.. math:: - - ^{\mathbf{A}}\mathbf{C}^{\mathbf{B}} = - \begin{bmatrix} - \cos(\theta) & - \sin(\theta) & 0\\ - \sin(\theta) & \cos(\theta) & 0\\ - 0 & 0 & 1\\ - \end{bmatrix} - -Simple rotations about the X and Y axes are defined by: - -.. math:: - - \textrm{DCM for x-axis rotation: } - \begin{bmatrix} - 1 & 0 & 0\\ - 0 & \cos(\theta) & -\sin(\theta)\\ - 0 & \sin(\theta) & \cos(\theta) - \end{bmatrix} - - \textrm{DCM for y-axis rotation: } - \begin{bmatrix} - \cos(\theta) & 0 & \sin(\theta)\\ - 0 & 1 & 0\\ - -\sin(\theta) & 0 & \cos(\theta)\\ - \end{bmatrix} - -Rotation in the positive direction here will be defined by using the right-hand -rule. - -The direction cosine matrix is also involved with the definition of the dot -product between sets of basis vectors. If we have two reference frames with -associated basis vectors, their direction cosine matrix can be defined as: - -.. math:: - - \begin{bmatrix} - C_{xx} & C_{xy} & C_{xz}\\ - C_{yx} & C_{yy} & C_{yz}\\ - C_{zx} & C_{zy} & C_{zz}\\ - \end{bmatrix} = - \begin{bmatrix} - \mathbf{\hat{a}_x}\cdot\mathbf{\hat{b}_x} & - \mathbf{\hat{a}_x}\cdot\mathbf{\hat{b}_y} & - \mathbf{\hat{a}_x}\cdot\mathbf{\hat{b}_z}\\ - \mathbf{\hat{a}_y}\cdot\mathbf{\hat{b}_x} & - \mathbf{\hat{a}_y}\cdot\mathbf{\hat{b}_y} & - \mathbf{\hat{a}_y}\cdot\mathbf{\hat{b}_z}\\ - \mathbf{\hat{a}_z}\cdot\mathbf{\hat{b}_x} & - \mathbf{\hat{a}_z}\cdot\mathbf{\hat{b}_y} & - \mathbf{\hat{a}_z}\cdot\mathbf{\hat{b}_z}\\ - \end{bmatrix} - -Additionally, the direction cosine matrix is orthogonal, in that: - -.. math:: - ^{\mathbf{A}}\mathbf{C}^{\mathbf{B}} = - (^{\mathbf{B}}\mathbf{C}^{\mathbf{A}})^{-1}\\ = - (^{\mathbf{B}}\mathbf{C}^{\mathbf{A}})^T\\ - -If we have reference frames :math:`\mathbf{A}` and :math:`\mathbf{B}`, which in -this example have undergone a simple z-axis rotation by an amount -:math:`\theta`, we will have two sets of basis vectors. We can then define two -vectors: :math:`\mathbf{a} = \mathbf{\hat{a}_x} + \mathbf{\hat{a}_y} + -\mathbf{\hat{a}_z}` and :math:`\mathbf{b} = \mathbf{\hat{b}_x} + -\mathbf{\hat{b}_y} + \mathbf{\hat{b}_z}`. If we wish to express -:math:`\mathbf{b}` in the :math:`\mathbf{A}` frame, we do the following: - -.. math:: - \mathbf{b} &= \mathbf{\hat{b}_x} + \mathbf{\hat{b}_y} + \mathbf{\hat{b}_z}\\ - \mathbf{b} &= \begin{bmatrix}\mathbf{\hat{a}_x}\cdot (\mathbf{\hat{b}_x} + - \mathbf{\hat{b}_y} + \mathbf{\hat{b}_z})\end{bmatrix} \mathbf{\hat{a}_x} + - \begin{bmatrix}\mathbf{\hat{a}_y}\cdot (\mathbf{\hat{b}_x} + \mathbf{\hat{b}_y} - + \mathbf{\hat{b}_z})\end{bmatrix} \mathbf{\hat{a}_y} + - \begin{bmatrix}\mathbf{\hat{a}_z}\cdot (\mathbf{\hat{b}_x} + \mathbf{\hat{b}_y} - + \mathbf{\hat{b}_z})\end{bmatrix} \mathbf{\hat{a}_z}\\ \mathbf{b} &= - (\cos(\theta) - \sin(\theta))\mathbf{\hat{a}_x} + - (\sin(\theta) + \cos(\theta))\mathbf{\hat{a}_y} + \mathbf{\hat{a}_z} - -And if we wish to express :math:`\mathbf{a}` in the :math:`\mathbf{B}`, we do: - -.. math:: - \mathbf{a} &= \mathbf{\hat{a}_x} + \mathbf{\hat{a}_y} + \mathbf{\hat{a}_z}\\ - \mathbf{a} &= \begin{bmatrix}\mathbf{\hat{b}_x}\cdot (\mathbf{\hat{a}_x} + - \mathbf{\hat{a}_y} + \mathbf{\hat{a}_z})\end{bmatrix} \mathbf{\hat{b}_x} + - \begin{bmatrix}\mathbf{\hat{b}_y}\cdot (\mathbf{\hat{a}_x} + - \mathbf{\hat{a}_y} + \mathbf{\hat{a}_z})\end{bmatrix} \mathbf{\hat{b}_y} + - \begin{bmatrix}\mathbf{\hat{b}_z}\cdot (\mathbf{\hat{a}_x} + - \mathbf{\hat{a}_y} + \mathbf{\hat{a}_z})\end{bmatrix} \mathbf{\hat{b}_z}\\ - \mathbf{a} &= (\cos(\theta) + \sin(\theta))\mathbf{\hat{b}_x} + - (-\sin(\theta)+\cos(\theta))\mathbf{\hat{b}_y} + \mathbf{\hat{b}_z} - - -Derivatives with Multiple Frames --------------------------------- - -If we have reference frames :math:`\mathbf{A}` and :math:`\mathbf{B}` -we will have two sets of basis vectors. We can then define two -vectors: :math:`\mathbf{a} = a_x\mathbf{\hat{a}_x} + a_y\mathbf{\hat{a}_y} + -a_z\mathbf{\hat{a}_z}` and :math:`\mathbf{b} = b_x\mathbf{\hat{b}_x} + -b_y\mathbf{\hat{b}_y} + b_z\mathbf{\hat{b}_z}`. If we want to take the -derivative of :math:`\mathbf{b}` in the reference frame :math:`\mathbf{A}`, we -must first express it in :math:`\mathbf{A}`, and the take the derivatives of -the measure numbers: - -.. math:: - \frac{^{\mathbf{A}} d\mathbf{b}}{dx} = \frac{d (\mathbf{b}\cdot - \mathbf{\hat{a}_x} )}{dx} \mathbf{\hat{a}_x} + \frac{d (\mathbf{b}\cdot - \mathbf{\hat{a}_y} )}{dx} \mathbf{\hat{a}_y} + \frac{d (\mathbf{b}\cdot - \mathbf{\hat{a}_z} )}{dx} \mathbf{\hat{a}_z} + - - -Examples --------- - -An example of vector calculus: - -.. image:: vec_simp_der.* - :height: 500 - :width: 350 - :align: center - -In this example we have two bodies, each with an attached reference frame. -We will say that :math:`\theta` and :math:`x` are functions of time. -We wish to know the time derivative of vector :math:`\mathbf{c}` in both the -:math:`\mathbf{A}` and :math:`\mathbf{B}` frames. - -First, we need to define :math:`\mathbf{c}`; -:math:`\mathbf{c}=x\mathbf{\hat{b}_x}+l\mathbf{\hat{b}_y}`. This provides a -definition in the :math:`\mathbf{B}` frame. We can now do the following: - -.. math:: - \frac{^{\mathbf{B}} d \mathbf{c}}{dt} &= \frac{dx}{dt} \mathbf{\hat{b}_x} + - \frac{dl}{dt} \mathbf{\hat{b}_y}\\ - &= \dot{x} \mathbf{\hat{b}_x} - -To take the derivative in the :math:`\mathbf{A}` frame, we have to first relate -the two frames: - -.. math:: - ^{\mathbf{A}} \mathbf{C} ^{\mathbf{B}} = - \begin{bmatrix} - \cos(\theta) & 0 & \sin(\theta)\\ - 0 & 1 & 0\\ - -\sin(\theta) & 0 & \cos(\theta)\\ - \end{bmatrix} - -Now we can do the following: - -.. math:: - \frac{^{\mathbf{A}} d \mathbf{c}}{dt} &= \frac{d (\mathbf{c} \cdot - \mathbf{\hat{a}_x})}{dt} \mathbf{\hat{a}_x} + \frac{d (\mathbf{c} \cdot - \mathbf{\hat{a}_y})}{dt} \mathbf{\hat{a}_y} + \frac{d (\mathbf{c} \cdot - \mathbf{\hat{a}_z})}{dt} \mathbf{\hat{a}_z}\\ - &= \frac{d (\cos(\theta) x)}{dt} \mathbf{\hat{a}_x} + - \frac{d (l)}{dt} \mathbf{\hat{a}_y} + - \frac{d (-\sin(\theta) x)}{dt} \mathbf{\hat{a}_z}\\ - &= (-\dot{\theta}\sin(\theta)x + \cos(\theta)\dot{x}) \mathbf{\hat{a}_x} + - (\dot{\theta}\cos(\theta)x + \sin(\theta)\dot{x}) \mathbf{\hat{a}_z} - -Note that this is the time derivative of :math:`\mathbf{c}` in -:math:`\mathbf{A}`, and is expressed in the :math:`\mathbf{A}` frame. We can -express it in the :math:`\mathbf{B}` frame however, and the expression will -still be valid: - -.. math:: - \frac{^{\mathbf{A}} d \mathbf{c}}{dt} &= (-\dot{\theta}\sin(\theta)x + - \cos(\theta)\dot{x}) \mathbf{\hat{a}_x} + (\dot{\theta}\cos(\theta)x + - \sin(\theta)\dot{x}) \mathbf{\hat{a}_z}\\ - &= \dot{x}\mathbf{\hat{b}_x} - \theta x \mathbf{\hat{b}_z}\\ - -Note the difference in expression complexity between the two forms. They are -equivalent, but one is much simpler. This is an extremely important concept, as -defining vectors in the more complex forms can vastly slow down formulation of -the equations of motion and increase their length, sometimes to a point where -they cannot be shown on screen. - -Using Vectors and Reference Frames in SymPy's Mechanics -======================================================= - -We have waited until after all of the relevant mathematical relationships have -been defined for vectors and reference frames to introduce code. This is due to -how vectors are formed. When starting any problem in :mod:`mechanics`, one of -the first steps is defining a reference frame (remember to import -sympy.physics.mechanics first):: - - >>> from sympy.physics.mechanics import * - >>> N = ReferenceFrame('N') - -Now we have created a reference frame, :math:`\mathbf{N}`. To have access to -any basis vectors, first a reference frame needs to be created. Now that we -have made and object representing :math:`\mathbf{N}`, we can access its basis -vectors:: - - >>> N.x - N.x - >>> N.y - N.y - >>> N.z - N.z - -Vector Algebra, in Mechanics ----------------------------- - -We can now do basic algebraic operations on these vectors.:: - - >>> N.x == N.x - True - >>> N.x == N.y - False - >>> N.x + N.y - N.x + N.y - >>> 2 * N.x + N.y - 2*N.x + N.y - -Remember, don't add a scalar quantity to a vector (``N.x + 5``); this will -raise an error. At this point, we'll use SymPy's Symbol in our vectors. -Remember to refer to SymPy's Gotchas and Pitfalls when dealing with symbols.:: - - >>> from sympy import Symbol, symbols - >>> x = Symbol('x') - >>> x * N.x - x*N.x - >>> x*(N.x + N.y) - x*N.x + x*N.y - -In :mod:`mechanics` multiple interfaces to vector multiplication have been -implemented, at the operator level, method level, and function level. The -vector dot product can work as follows: :: - - >>> N.x & N.x - 1 - >>> N.x & N.y - 0 - >>> N.x.dot(N.x) - 1 - >>> N.x.dot(N.y) - 0 - >>> dot(N.x, N.x) - 1 - >>> dot(N.x, N.y) - 0 - -The "official" interface is the function interface; this is what will be used -in all examples. This is to avoid confusion with the attribute and methods -being next to each other, and in the case of the operator operation priority. -The operators used in :mod:`mechanics` for vector multiplication do not posses -the correct order of operations; this can lead to errors. Care with parentheses -is needed when using operators to represent vector multiplication. - -The cross product is the other vector multiplication which will be discussed -here. It offers similar interfaces to the dot product, and comes with the same -warnings. :: - - >>> N.x ^ N.x - 0 - >>> N.x ^ N.y - N.z - >>> N.x.cross(N.x) - 0 - >>> N.x.cross(N.z) - - N.y - >>> cross(N.x, N.y) - N.z - >>> N.x ^ (N.y + N.z) - - N.y + N.z - -Two additional operations can be done with vectors: normalizing the vector to -length 1, and getting its magnitude. These are done as follows: - - >>> (N.x + N.y).normalize() - sqrt(2)/2*N.x + sqrt(2)/2*N.y - >>> (N.x + N.y).magnitude() - sqrt(2) - -Vector Calculus, in Mechanics ------------------------------ - -We have already introduced our first reference frame. We can take the -derivative in that frame right now, if we desire: :: - - >>> (x * N.x + N.y).diff(x, N) - N.x - -SymPy has a ``diff`` function, but it does not currently work with -:mod:`mechanics` Vectors, so please use ``Vector``'s ``diff`` method. The -reason for this is that when differentiating a ``Vector``, the frame of -reference must be specified in addition to what you are taking the derivative -with respect to; SymPy's ``diff`` function doesn't fit this mold. - -The more interesting case arise with multiple reference frames. If we introduce -a second reference frame, :math:`\mathbf{A}`, we now have two frames. Note that -at this point we can add components of :math:`\mathbf{N}` and -:math:`\mathbf{A}` together, but cannot perform vector multiplication, as no -relationship between the two frames has been defined. :: - - >>> A = ReferenceFrame('A') - >>> A.x + N.x - A.x + N.x - -If we want to do vector multiplication, first we have to define and -orientation. The ``orient`` method of ``ReferenceFrame`` provides that -functionality. :: - - >>> A.orient(N, 'Axis', [x, N.y]) - -If we desire, we can view the DCM between these two frames at any time. This -can be calculated with the ``dcm`` method. This code: ``N.dcm(A)`` gives the -dcm :math:`^{\mathbf{N}} \mathbf{C} ^{\mathbf{A}}`. - -This orients the :math:`\mathbf{A}` frame relative to the :math:`\mathbf{N}` -frame by a simple rotation around the Y axis, by an amount x. Other, more -complicated rotation types include Body rotations, Space rotations, -quaternions, and arbitrary axis rotations. Body and space rotations are -equivalent to doing 3 simple rotations in a row, each about a basis vector in -the new frame. An example follows: :: - - - >>> N = ReferenceFrame('N') - >>> Bp = ReferenceFrame('Bp') - >>> Bpp = ReferenceFrame('Bpp') - >>> B = ReferenceFrame('B') - >>> q1,q2,q3 = symbols('q1 q2 q3') - >>> Bpp.orient(N,'Axis', [q1, N.x]) - >>> Bp.orient(Bpp,'Axis', [q2, Bpp.y]) - >>> B.orient(Bp,'Axis', [q3, Bp.z]) - >>> N.dcm(B) - [ cos(q2)*cos(q3), -sin(q3)*cos(q2), sin(q2)] - [sin(q1)*sin(q2)*cos(q3) + sin(q3)*cos(q1), -sin(q1)*sin(q2)*sin(q3) + cos(q1)*cos(q3), -sin(q1)*cos(q2)] - [sin(q1)*sin(q3) - sin(q2)*cos(q1)*cos(q3), sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1), cos(q1)*cos(q2)] - >>> B.orient(N,'Body',[q1,q2,q3],'XYZ') - >>> N.dcm(B) - [ cos(q2)*cos(q3), -sin(q3)*cos(q2), sin(q2)] - [sin(q1)*sin(q2)*cos(q3) + sin(q3)*cos(q1), -sin(q1)*sin(q2)*sin(q3) + cos(q1)*cos(q3), -sin(q1)*cos(q2)] - [sin(q1)*sin(q3) - sin(q2)*cos(q1)*cos(q3), sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1), cos(q1)*cos(q2)] - -Space orientations are similar to body orientation, but applied from the frame -to body. Body and space rotations can involve either two or three axes: 'XYZ' -works, as does 'YZX', 'ZXZ', 'YXY', etc. What is key is that each simple -rotation is about a different axis than the previous one; 'ZZX' does not -completely orient a set of basis vectors in 3 space. - -Sometimes it will be more convenient to create a new reference frame and orient -relative to an existing one in one step. The ``orientnew`` method allows for -this functionality, and essentially wraps the ``orient`` method. All of the -things you can do in ``orient``, you can do in ``orientnew``. :: - - >>> C = N.orientnew('C', 'Axis', [q1, N.x]) - -Quaternions (or Euler Parameters) use 4 value to characterize the orientation -of the frame. This and arbitrary axis rotations are described in the ``orient`` -and ``orientnew`` method help, or in the references [Kane1983]_. - - -Finally, before starting multiframe calculus operations, we will introduce -another :mod:`mechanics` tool: ``dynamicsymbols``. ``dynamicsymbols`` is -a shortcut function to create undefined functions of time within SymPy. The -derivative of such a 'dynamicsymbol' is shown below. :: - - >>> from sympy import diff - >>> q1, q2, q3 = dynamicsymbols('q1 q2 q3') - >>> diff(q1, Symbol('t')) - Derivative(q1(t), t) - -The 'dynamicsymbol' printing is not very clear above; we will also introduce a -few other tools here. We can use ``mprint`` instead of print for -non-interactive sessions. :: - - >>> q1 - q1(t) - >>> q1d = diff(q1, Symbol('t')) - >>> mprint(q1) - q1 - >>> mprint(q1d) - q1' - -For interactive sessions use ``mechanics_printing``. There also exist analogs -for SymPy's ``pprint``, ``mpprint``, and ``latex``, ``mlatex``. :: - - >>> mechanics_printing() - >>> q1 - q1 - >>> q1d - q1' - -A 'dynamicsymbol' should be used to represent any time varying quantity in -:mod:`mechanics`, whether it is a coordinate, varying position, or force. The -primary use of a 'dynamicsymbol' is for speeds and coordinates (of which there -will be more discussion in the Kinematics Section of the documentation). - -Now we will define the orientation of our new frames with a 'dynamicsymbol', -and can take derivatives and time derivatives with ease. Some examples follow. -:: - - >>> N = ReferenceFrame('N') - >>> B = N.orientnew('B', 'Axis', [q1, N.x]) - >>> (B.y*q2 + B.z).diff(q2, N) - B.y - >>> (B.y*q2 + B.z).dt(N) - (-q1' + q2')*B.y + q2*q1'*B.z - -Note that the output vectors are kept in the same frames that they were -provided in. This remains true for vectors with components made of basis -vectors from multiple frames: :: - - >>> (B.y*q2 + B.z + q2*N.x).diff(q2, N) - B.y + N.x - - -How Vectors are Coded in Mechanics ----------------------------------- - -What follows is a short description of how vectors are defined by the code in -:mod:`mechanics`. It is provided for those who want to learn more about how -this part of :mod:`mechanics` works, and does not need to be read to use this -module; don't read it unless you want to learn how this module was implemented. - -Every ``Vector``'s main information is stored in the ``args`` attribute, which -stores the three measure numbers for each basis vector in a frame, for every -relevant frame. A vector does not exist in code until a ``ReferenceFrame`` -is created. At this point, the ``x``, ``y``, and ``z`` attributes of the -reference frame are immutable ``Vector``'s which have measure numbers of -[1,0,0], [0,1,0], and [0,0,1] associated with that ``ReferenceFrame``. Once -these vectors are accessible, new vectors can be created by doing algebraic -operations with the basis vectors. A vector can have components from multiple -frames though. That is why ``args`` is a list; it has as many elements in the -list as there are unique ``ReferenceFrames`` in its components, i.e. if there -are ``A`` and ``B`` frame basis vectors in our new vector, ``args`` is of -length 2; if it has ``A``, ``B``, and ``C`` frame basis vector, ``args`` is of -length three. - -Each element in the ``args`` list is a 2-tuple; the first element is a SymPy -``Matrix`` (this is where the measure numbers for each set of basis vectors are -stored) and the second element is a ``ReferenceFrame`` to associate those -measure numbers with. - -``ReferenceFrame`` stores a few things. First, it stores the name you supply it -on creation (``name`` attribute). It also stores the direction cosine matrices, -defined upon creation with the ``orientnew`` method, or calling the ``orient`` -method after creation. The direction cosine matrices are represented by SymPy's -``Matrix``, and are part of a dictionary where the keys are the -``ReferenceFrame`` and the value the ``Matrix``; these are set -bi-directionally; in that when you orient ``A`` to ``N`` you are setting ``A``'s -orientation dictionary to include ``N`` and its ``Matrix``, but also you also -are setting ``N``'s orientation dictionary to include ``A`` and its ``Matrix`` -(that DCM being the transpose of the other). diff --git a/dev-py3k/_sources/modules/physics/paulialgebra.txt b/dev-py3k/_sources/modules/physics/paulialgebra.txt deleted file mode 100644 index a6b916dd88e..00000000000 --- a/dev-py3k/_sources/modules/physics/paulialgebra.txt +++ /dev/null @@ -1,6 +0,0 @@ -============= -Pauli Algebra -============= - -.. automodule:: sympy.physics.paulialgebra - :members: diff --git a/dev-py3k/_sources/modules/physics/qho_1d.txt b/dev-py3k/_sources/modules/physics/qho_1d.txt deleted file mode 100644 index 8890e194a1a..00000000000 --- a/dev-py3k/_sources/modules/physics/qho_1d.txt +++ /dev/null @@ -1,6 +0,0 @@ -================================== -Quantum Harmonic Oscillator in 1-D -================================== - -.. automodule:: sympy.physics.qho_1d - :members: diff --git a/dev-py3k/_sources/modules/physics/quantum/anticommutator.txt b/dev-py3k/_sources/modules/physics/quantum/anticommutator.txt deleted file mode 100644 index a9d7c490d7d..00000000000 --- a/dev-py3k/_sources/modules/physics/quantum/anticommutator.txt +++ /dev/null @@ -1,6 +0,0 @@ -============== -Anticommutator -============== - -.. automodule:: sympy.physics.quantum.anticommutator - :members: diff --git a/dev-py3k/_sources/modules/physics/quantum/cartesian.txt b/dev-py3k/_sources/modules/physics/quantum/cartesian.txt deleted file mode 100644 index 9652226def7..00000000000 --- a/dev-py3k/_sources/modules/physics/quantum/cartesian.txt +++ /dev/null @@ -1,6 +0,0 @@ -============================== -Cartesian Operators and States -============================== - -.. automodule:: sympy.physics.quantum.cartesian - :members: diff --git a/dev-py3k/_sources/modules/physics/quantum/cg.txt b/dev-py3k/_sources/modules/physics/quantum/cg.txt deleted file mode 100644 index 375369902db..00000000000 --- a/dev-py3k/_sources/modules/physics/quantum/cg.txt +++ /dev/null @@ -1,6 +0,0 @@ -=========================== -Clebsch-Gordan Coefficients -=========================== - -.. automodule:: sympy.physics.quantum.cg - :members: diff --git a/dev-py3k/_sources/modules/physics/quantum/circuitplot.txt b/dev-py3k/_sources/modules/physics/quantum/circuitplot.txt deleted file mode 100644 index 6a821b1146c..00000000000 --- a/dev-py3k/_sources/modules/physics/quantum/circuitplot.txt +++ /dev/null @@ -1,6 +0,0 @@ -============ -Circuit Plot -============ - -.. automodule:: sympy.physics.quantum.circuitplot - :members: diff --git a/dev-py3k/_sources/modules/physics/quantum/commutator.txt b/dev-py3k/_sources/modules/physics/quantum/commutator.txt deleted file mode 100644 index 532195cc8cc..00000000000 --- a/dev-py3k/_sources/modules/physics/quantum/commutator.txt +++ /dev/null @@ -1,6 +0,0 @@ -========== -Commutator -========== - -.. automodule:: sympy.physics.quantum.commutator - :members: diff --git a/dev-py3k/_sources/modules/physics/quantum/constants.txt b/dev-py3k/_sources/modules/physics/quantum/constants.txt deleted file mode 100644 index 28258e8e2bd..00000000000 --- a/dev-py3k/_sources/modules/physics/quantum/constants.txt +++ /dev/null @@ -1,6 +0,0 @@ -========= -Constants -========= - -.. automodule:: sympy.physics.quantum.constants - :members: diff --git a/dev-py3k/_sources/modules/physics/quantum/dagger.txt b/dev-py3k/_sources/modules/physics/quantum/dagger.txt deleted file mode 100644 index 8aeac00dceb..00000000000 --- a/dev-py3k/_sources/modules/physics/quantum/dagger.txt +++ /dev/null @@ -1,6 +0,0 @@ -====== -Dagger -====== - -.. automodule:: sympy.physics.quantum.dagger - :members: diff --git a/dev-py3k/_sources/modules/physics/quantum/gate.txt b/dev-py3k/_sources/modules/physics/quantum/gate.txt deleted file mode 100644 index fcbb0a3d448..00000000000 --- a/dev-py3k/_sources/modules/physics/quantum/gate.txt +++ /dev/null @@ -1,6 +0,0 @@ -===== -Gates -===== - -.. automodule:: sympy.physics.quantum.gate - :members: diff --git a/dev-py3k/_sources/modules/physics/quantum/grover.txt b/dev-py3k/_sources/modules/physics/quantum/grover.txt deleted file mode 100644 index acea8fc038f..00000000000 --- a/dev-py3k/_sources/modules/physics/quantum/grover.txt +++ /dev/null @@ -1,6 +0,0 @@ -================== -Grover's Algorithm -================== - -.. automodule:: sympy.physics.quantum.grover - :members: diff --git a/dev-py3k/_sources/modules/physics/quantum/hilbert.txt b/dev-py3k/_sources/modules/physics/quantum/hilbert.txt deleted file mode 100644 index 00cc7408591..00000000000 --- a/dev-py3k/_sources/modules/physics/quantum/hilbert.txt +++ /dev/null @@ -1,6 +0,0 @@ -============= -Hilbert Space -============= - -.. automodule:: sympy.physics.quantum.hilbert - :members: diff --git a/dev-py3k/_sources/modules/physics/quantum/index.txt b/dev-py3k/_sources/modules/physics/quantum/index.txt deleted file mode 100644 index 3af790543ec..00000000000 --- a/dev-py3k/_sources/modules/physics/quantum/index.txt +++ /dev/null @@ -1,58 +0,0 @@ -================= -Quantum Mechanics -================= - -.. topic:: Abstract - - Contains Docstrings of Physics-Quantum module - -Quantum Functions -================= - -.. toctree:: - :maxdepth: 3 - - anticommutator.rst - cg.rst - commutator.rst - constants.rst - dagger.rst - innerproduct.rst - tensorproduct.rst - -States and Operators -==================== - -.. toctree:: - :maxdepth: 3 - - cartesian.rst - hilbert.rst - operator.rst - operatorset.rst - qapply.rst - represent.rst - spin.rst - state.rst - -Quantum Computation -=================== - -.. toctree:: - :maxdepth: 3 - - circuitplot.rst - gate.rst - grover.rst - qft.rst - qubit.rst - shor.rst - -Analytic Solutions -================== - - -.. toctree:: - :maxdepth: 3 - - piab.rst diff --git a/dev-py3k/_sources/modules/physics/quantum/innerproduct.txt b/dev-py3k/_sources/modules/physics/quantum/innerproduct.txt deleted file mode 100644 index 6fd1b23c2ec..00000000000 --- a/dev-py3k/_sources/modules/physics/quantum/innerproduct.txt +++ /dev/null @@ -1,6 +0,0 @@ -============= -Inner Product -============= - -.. automodule:: sympy.physics.quantum.innerproduct - :members: diff --git a/dev-py3k/_sources/modules/physics/quantum/operator.txt b/dev-py3k/_sources/modules/physics/quantum/operator.txt deleted file mode 100644 index 813a21cf7c2..00000000000 --- a/dev-py3k/_sources/modules/physics/quantum/operator.txt +++ /dev/null @@ -1,6 +0,0 @@ -======== -Operator -======== - -.. automodule:: sympy.physics.quantum.operator - :members: diff --git a/dev-py3k/_sources/modules/physics/quantum/operatorset.txt b/dev-py3k/_sources/modules/physics/quantum/operatorset.txt deleted file mode 100644 index e6c1457d630..00000000000 --- a/dev-py3k/_sources/modules/physics/quantum/operatorset.txt +++ /dev/null @@ -1,6 +0,0 @@ -=============================== -Operator/State Helper Functions -=============================== - -.. automodule:: sympy.physics.quantum.operatorset - :members: diff --git a/dev-py3k/_sources/modules/physics/quantum/piab.txt b/dev-py3k/_sources/modules/physics/quantum/piab.txt deleted file mode 100644 index 16d1c5d8251..00000000000 --- a/dev-py3k/_sources/modules/physics/quantum/piab.txt +++ /dev/null @@ -1,6 +0,0 @@ -================= -Particle in a Box -================= - -.. automodule:: sympy.physics.quantum.piab - :members: diff --git a/dev-py3k/_sources/modules/physics/quantum/qapply.txt b/dev-py3k/_sources/modules/physics/quantum/qapply.txt deleted file mode 100644 index 12df45aba7a..00000000000 --- a/dev-py3k/_sources/modules/physics/quantum/qapply.txt +++ /dev/null @@ -1,6 +0,0 @@ -====== -Qapply -====== - -.. automodule:: sympy.physics.quantum.qapply - :members: diff --git a/dev-py3k/_sources/modules/physics/quantum/qft.txt b/dev-py3k/_sources/modules/physics/quantum/qft.txt deleted file mode 100644 index 99d1a857535..00000000000 --- a/dev-py3k/_sources/modules/physics/quantum/qft.txt +++ /dev/null @@ -1,6 +0,0 @@ -=== -QFT -=== - -.. automodule:: sympy.physics.quantum.qft - :members: diff --git a/dev-py3k/_sources/modules/physics/quantum/qubit.txt b/dev-py3k/_sources/modules/physics/quantum/qubit.txt deleted file mode 100644 index 65c02630d18..00000000000 --- a/dev-py3k/_sources/modules/physics/quantum/qubit.txt +++ /dev/null @@ -1,6 +0,0 @@ -===== -Qubit -===== - -.. automodule:: sympy.physics.quantum.qubit - :members: diff --git a/dev-py3k/_sources/modules/physics/quantum/represent.txt b/dev-py3k/_sources/modules/physics/quantum/represent.txt deleted file mode 100644 index 1b4e4869c0e..00000000000 --- a/dev-py3k/_sources/modules/physics/quantum/represent.txt +++ /dev/null @@ -1,6 +0,0 @@ -========= -Represent -========= - -.. automodule:: sympy.physics.quantum.represent - :members: diff --git a/dev-py3k/_sources/modules/physics/quantum/shor.txt b/dev-py3k/_sources/modules/physics/quantum/shor.txt deleted file mode 100644 index 29210295a3f..00000000000 --- a/dev-py3k/_sources/modules/physics/quantum/shor.txt +++ /dev/null @@ -1,6 +0,0 @@ -================ -Shor's Algorithm -================ - -.. automodule:: sympy.physics.quantum.shor - :members: diff --git a/dev-py3k/_sources/modules/physics/quantum/spin.txt b/dev-py3k/_sources/modules/physics/quantum/spin.txt deleted file mode 100644 index 80bedaae041..00000000000 --- a/dev-py3k/_sources/modules/physics/quantum/spin.txt +++ /dev/null @@ -1,6 +0,0 @@ -==== -Spin -==== - -.. automodule:: sympy.physics.quantum.spin - :members: diff --git a/dev-py3k/_sources/modules/physics/quantum/state.txt b/dev-py3k/_sources/modules/physics/quantum/state.txt deleted file mode 100644 index 4d72c5443d7..00000000000 --- a/dev-py3k/_sources/modules/physics/quantum/state.txt +++ /dev/null @@ -1,6 +0,0 @@ -===== -State -===== - -.. automodule:: sympy.physics.quantum.state - :members: diff --git a/dev-py3k/_sources/modules/physics/quantum/tensorproduct.txt b/dev-py3k/_sources/modules/physics/quantum/tensorproduct.txt deleted file mode 100644 index 4bb2d99ce43..00000000000 --- a/dev-py3k/_sources/modules/physics/quantum/tensorproduct.txt +++ /dev/null @@ -1,6 +0,0 @@ -============== -Tensor Product -============== - -.. automodule:: sympy.physics.quantum.tensorproduct - :members: diff --git a/dev-py3k/_sources/modules/physics/secondquant.txt b/dev-py3k/_sources/modules/physics/secondquant.txt deleted file mode 100644 index d007b77482e..00000000000 --- a/dev-py3k/_sources/modules/physics/secondquant.txt +++ /dev/null @@ -1,6 +0,0 @@ -=================== -Second Quantization -=================== - -.. automodule:: sympy.physics.secondquant - :members: diff --git a/dev-py3k/_sources/modules/physics/sho.txt b/dev-py3k/_sources/modules/physics/sho.txt deleted file mode 100644 index 80564c250bf..00000000000 --- a/dev-py3k/_sources/modules/physics/sho.txt +++ /dev/null @@ -1,6 +0,0 @@ -================================== -Quantum Harmonic Oscillator in 3-D -================================== - -.. automodule:: sympy.physics.sho - :members: diff --git a/dev-py3k/_sources/modules/physics/units.txt b/dev-py3k/_sources/modules/physics/units.txt deleted file mode 100644 index 529068bb798..00000000000 --- a/dev-py3k/_sources/modules/physics/units.txt +++ /dev/null @@ -1,69 +0,0 @@ -.. _physics-units: - -===== -Units -===== - -Introduction -============ - -This module provides around 200 predefined units that are commonly used in the -sciences. Additionally, it provides the :class:`Unit` class which allows you -to define your own units. - -Examples -======== - -All examples in this tutorial are computable, so one can just copy and -paste them into a Python shell and do something useful with them. All -computations were done using the following setup:: - - >>> from sympy.physics.units import * - -Dimensionless quantities ------------------------- - -Provides variables for commonly written dimensionless (unit-less) quantities. - - >>> 2*ten - 20 - >>> 20*percent - 1/5 - >>> 300*kilo*20*percent - 60000 - >>> nano*deg - pi/180000000000 - -Base units ----------- - -The SI base units are defined variable name that are commonly used in written -and verbal communication. The singular abbreviated versions are what is used -for display purposes, but the plural non-abbreviated versions are defined in -case it helps readability. - - >>> 5*meters - 5*m - >>> milli*kilogram - kg/1000 - >>> gram - kg/1000 - -Note that British Imperial and U.S. customary units are not included. -We strongly urge the use of SI units; only Myanmar (Burma), Liberia, and the -United States have not officially accepted the SI system. - - -Derived units -------------- - -Common SI derived units. - - >>> joule - kg*m**2/s**2 - -Docstring -========= - -.. automodule:: sympy.physics.units - :members: diff --git a/dev-py3k/_sources/modules/physics/wigner.txt b/dev-py3k/_sources/modules/physics/wigner.txt deleted file mode 100644 index 8b6d98ef4d4..00000000000 --- a/dev-py3k/_sources/modules/physics/wigner.txt +++ /dev/null @@ -1,6 +0,0 @@ -============== -Wigner Symbols -============== - -.. automodule:: sympy.physics.wigner - :members: diff --git a/dev-py3k/_sources/modules/plotting.txt b/dev-py3k/_sources/modules/plotting.txt deleted file mode 100644 index 402a8fbe1df..00000000000 --- a/dev-py3k/_sources/modules/plotting.txt +++ /dev/null @@ -1,287 +0,0 @@ -Plotting Module -=============== - -.. module:: sympy.plotting.plot - -Introduction ------------- - -The plotting module allows you to plot 2-dimensional and 3-dimensional plots. -Presently the plots are rendered using ``Matplotlib`` as its backend. It is -also possible to plot 2-dimensional plots using a ``TextBackend`` if you don't -have ``Matplotlib``. - -The plotting module has the following functions: - -* plot: Plots 2D line plots. -* plot_parametric: Plots 2D parametric plots. -* plot_implicit: Plots 2D implicit and region plots. -* plot3d: Plots 3D plots of functions in two variables. -* plot3d_parametric_line: Plots 3D line plots, defined by a parameter. -* plot3d_parametric_surface: Plots 3D parametric surface plots. - -The above functions are only for convenience and ease of use. It is possible to -plot any plot by passing the corresponding ``Series`` class to ``Plot`` as -argument. - -Plot Class ----------- - -.. autoclass:: sympy.plotting.plot.Plot - :members: - -Plotting Function Reference ---------------------------- - -.. autofunction:: plot - -.. autofunction:: plot_parametric - -.. autofunction:: plot3d - -.. autofunction:: plot3d_parametric_line - -.. autofunction:: plot3d_parametric_surface - -.. autofunction:: sympy.plotting.plot_implicit.plot_implicit - -Series Classes --------------- - -.. autoclass:: sympy.plotting.plot.BaseSeries - :members: - -.. autoclass:: sympy.plotting.plot.Line2DBaseSeries - :members: - -.. autoclass:: sympy.plotting.plot.LineOver1DRangeSeries - :members: - -.. autoclass:: sympy.plotting.plot.Parametric2DLineSeries - :members: - -.. autoclass:: sympy.plotting.plot.Line3DBaseSeries - :members: - -.. autoclass:: sympy.plotting.plot.Parametric3DLineSeries - :members: - -.. autoclass:: sympy.plotting.plot.SurfaceBaseSeries - :members: - -.. autoclass:: sympy.plotting.plot.SurfaceOver2DRangeSeries - :members: - -.. autoclass:: sympy.plotting.plot.ParametricSurfaceSeries - :members: - -.. autoclass:: sympy.plotting.plot_implicit.ImplicitSeries - :members: - - - - -Pyglet Plotting Module -====================== - -.. module:: sympy.plotting.pygletplot - -This is the documentation for the old plotting module that uses pyglet. -This module has some limitations and is not actively developed anymore. -For an alternative you can look at the new plotting module. - -The pyglet plotting module can do nice 2D and 3D plots that can be -controlled by console commands as well as keyboard and mouse, with -the only dependency being ``pyglet``. - -Here is the simplest usage: - - >>> from sympy import var, Plot - >>> var('x y z') - >>> Plot(x*y**3-y*x**3) - -To see lots of plotting examples, see ``examples/pyglet_plotting.py`` and try running -it in interactive mode (python -i plotting.py):: - - $ python -i examples/pyglet_plotting.py - -And type for instance ``example(7)`` or ``example(11)``. - -See also the `Plotting Module `_ -wiki page for screenshots. - - -Plot Window Controls --------------------- - -====================== ======== -Camera Keys -====================== ======== -Sensitivity Modifier SHIFT -Zoom R and F, Page Up and Down, Numpad + and - -Rotate View X,Y axis Arrow Keys, A,S,D,W, Numpad 4,6,8,2 -Rotate View Z axis Q and E, Numpad 7 and 9 -Rotate Ordinate Z axis Z and C, Numpad 1 and 3 -View XY F1 -View XZ F2 -View YZ F3 -View Perspective F4 -Reset X, Numpad 5 -====================== ======== - -====================== ======== -Axes Keys -====================== ======== -Toggle Visible F5 -Toggle Colors F6 -====================== ======== - -====================== ======== -Window Keys -====================== ======== -Close ESCAPE -Screenshot F8 -====================== ======== - -The mouse can be used to rotate, zoom, and translate by dragging the left, middle, and right mouse buttons respectively. - -Coordinate Modes ----------------- - -Plot supports several curvilinear coordinate modes, and they are independent -for each plotted function. You can specify a coordinate mode explicitly with -the 'mode' named argument, but it can be automatically determined for cartesian -or parametric plots, and therefore must only be specified for polar, -cylindrical, and spherical modes. - -Specifically, Plot(function arguments) and Plot.__setitem__(i, function -arguments) (accessed using array-index syntax on the Plot instance) will -interpret your arguments as a cartesian plot if you provide one function and a -parametric plot if you provide two or three functions. Similarly, the arguments -will be interpreted as a curve is one variable is used, and a surface if two -are used. - -Supported mode names by number of variables: - -* 1 (curves): parametric, cartesian, polar -* 2 (surfaces): parametric, cartesian, cylindrical, spherical - -:: - - >>> Plot(1, 'mode=spherical; color=zfade4') - -Note that function parameters are given as option strings of the form -"key1=value1; key2 = value2" (spaces are truncated). Keyword arguments given -directly to plot apply to the plot itself. - -Specifying Intervals for Variables ----------------------------------- - -The basic format for variable intervals is [var, min, max, steps]. However, the -syntax is quite flexible, and arguments not specified are taken from the -defaults for the current coordinate mode: - - >>> Plot(x**2) # implies [x,-5,5,100] - >>> Plot(x**2, [], []) # [x,-1,1,40], [y,-1,1,40] - >>> Plot(x**2-y**2, [100], [100]) # [x,-1,1,100], [y,-1,1,100] - >>> Plot(x**2, [x,-13,13,100]) - >>> Plot(x**2, [-13,13]) # [x,-13,13,100] - >>> Plot(x**2, [x,-13,13]) # [x,-13,13,100] - >>> Plot(1*x, [], [x], 'mode=cylindrical') # [unbound_theta,0,2*Pi,40], [x,-1,1,20] - -Using the Interactive Interface -------------------------------- -:: - - >>> p = Plot(visible=False) - >>> f = x**2 - >>> p[1] = f - >>> p[2] = f.diff(x) - >>> p[3] = f.diff(x).diff(x) - >>> p - [1]: x**2, 'mode=cartesian' - [2]: 2*x, 'mode=cartesian' - [3]: 2, 'mode=cartesian' - >>> p.show() - >>> p.clear() - >>> p - - >>> p[1] = x**2+y**2 - >>> p[1].style = 'solid' - >>> p[2] = -x**2-y**2 - >>> p[2].style = 'wireframe' - >>> p[1].color = z, (0.4,0.4,0.9), (0.9,0.4,0.4) - >>> p[1].style = 'both' - >>> p[2].style = 'both' - >>> p.close() - -Using Custom Color Functions ----------------------------- - -The following code plots a saddle and color it by the magnitude of its gradient: - - >>> fz = x**2-y**2 - >>> Fx, Fy, Fz = fz.diff(x), fz.diff(y), 0 - >>> p[1] = fz, 'style=solid' - >>> p[1].color = (Fx**2 + Fy**2 + Fz**2)**(0.5) - -The coloring algorithm works like this: - -#. Evaluate the color function(s) across the curve or surface. -#. Find the minimum and maximum value of each component. -#. Scale each component to the color gradient. - -When not specified explicitly, the default color gradient is -f(0.0)=(0.4,0.4,0.4) -> f(1.0)=(0.9,0.9,0.9). In our case, everything is -gray-scale because we have applied the default color gradient uniformly for -each color component. When defining a color scheme in this way, you might want -to supply a color gradient as well: - - >>> p[1].color = (Fx**2 + Fy**2 + Fz**2)**(0.5), (0.1,0.1,0.9), (0.9,0.1,0.1) - -Here's a color gradient with four steps: - - >>> gradient = [ 0.0, (0.1,0.1,0.9), 0.3, (0.1,0.9,0.1), - ... 0.7, (0.9,0.9,0.1), 1.0, (1.0,0.0,0.0) ] - >>> p[1].color = (Fx**2 + Fy**2 + Fz**2)**(0.5), gradient - -The other way to specify a color scheme is to give a separate function for each -component r, g, b. With this syntax, the default color scheme is defined: - - >>> p[1].color = z,y,x, (0.4,0.4,0.4), (0.9,0.9,0.9) - -This maps z->red, y->green, and x->blue. In some cases, you might prefer to use -the following alternative syntax: - - >>> p[1].color = z,(0.4,0.9), y,(0.4,0.9), x,(0.4,0.9) - -You can still use multi-step gradients with three-function color schemes. - -Plotting Geometric Entities ---------------------------- - -The plotting module is capable of plotting some 2D geometric entities like -line, circle and ellipse. The following example plots a circle and a tangent -line at a random point on the ellipse. -:: - - In [1]: p = Plot(axes='label_axes=True') - - In [2]: c = Circle(Point(0,0), 1) - - In [3]: t = c.tangent_line(c.random_point()) - - In [4]: p[0] = c - - In [5]: p[1] = t - -Plotting polygons (Polygon, RegularPolygon, Triangle) are not supported -directly. However a polygon can be plotted through a loop as follows. -:: - - In [6]: p = Plot(axes='label_axes=True') - - In [7]: t = RegularPolygon(Point(0,0), 1, 5) - - In [8]: for i in range(len(t.sides)): - ....: p[i] = t.sides[i] diff --git a/dev-py3k/_sources/modules/polys/agca.txt b/dev-py3k/_sources/modules/polys/agca.txt deleted file mode 100644 index 6af960c3e6b..00000000000 --- a/dev-py3k/_sources/modules/polys/agca.txt +++ /dev/null @@ -1,308 +0,0 @@ -.. _polys-agca: - -======================================================== -AGCA - Algebraic Geometry and Commutative Algebra Module -======================================================== - -Introduction -============ - - Algebraic geometry is a mixture of the ideas of two Mediterranean - cultures. It is the superposition of the Arab science of the lightening - calculation of the solutions of equations over the Greek art of position - and shape. - This tapestry was originally woven on European soil and is still being refined - under the influence of international fashion. Algebraic geometry studies the - delicate balance between the geometrically plausible and the algebraically - possible. Whenever one side of this mathematical teeter-totter outweighs the - other, one immediately loses interest and runs off in search of a more exciting - amusement. - - George R. Kempf - 1944 -- 2002 - - -Algebraic Geometry refers to the study of geometric problems via algebraic -methods (and sometimes vice versa). While this is a rather old topic, -algebraic geometry as understood today is very much a 20th century -development. Building on ideas of e.g. Riemann and Dedekind, it was realized -that there is an intimate connection between properties of the set of -solutions of a system of polynomial equations (called an algebraic variety) -and the behavior of the set of polynomial functions on that variety -(called the coordinate ring). - -As in many geometric disciplines, we can distinguish between local and global -questions (and methods). Local investigations in algebraic geometry are -essentially equivalent to the study of certain rings, their ideals and modules. -This latter topic is also called commutative algebra. It is the basic local -toolset of algebraic geometers, in much the same way that differential analysis -is the local toolset of differential geometers. - -A good conceptual introduction to commutative algebra is [Atiyah69]_. An -introduction more geared towards computations, and the work most of the -algorithms in this module are based on, is [Greuel2008]_. - -This module aims to eventually allow expression and solution of both -local and global geometric problems, both in the classical case over a field -and in the more modern arithmetic cases. So far, however, there is no geometric -functionality at all. Currently the module only provides tools for computational -commutative algebra over fields. - -All code examples assume:: - - >>> from sympy import * - >>> x, y, z = symbols('x,y,z') - >>> init_printing(use_unicode=True, wrap_line=False, no_global=True) - -Reference -========= - -In this section we document the usage of the AGCA module. For convenience of -the reader, some definitions and examples/explanations are interspersed. - -Base Rings ----------- - -Almost all computations in commutative algebra are relative to a "base ring". -(For example, when asking questions about an ideal, the base ring is the ring -the ideal is a subset of.) In principle all polys "domains" can be used as base -rings. However, useful functionality is only implemented for polynomial rings -over fields, and various localizations and quotients thereof. - -As demonstrated in -the examples below, the most convenient method to create objects you are -interested in is to build them up from the ground field, and then use the -various methods to create new objects from old. For example, in order to -create the local ring of the nodal cubic `y^2 = x^3` at the origin, over -`\mathbb{Q}`, you do:: - - >>> lr = QQ.poly_ring(x, y, order="ilex") / [y**2 - x**3] - >>> lr - ℚ[x, y, order=ilex] - ─────────────────── - ╱ 3 2╲ - ╲- x + y ╱ - -Note how the python list notation can be used as a short cut to express ideals. -You can use the ``convert`` method to return ordinary sympy objects into -objects understood by the AGCA module (although in many cases this will be done -automatically -- for example the list was automatically turned into an ideal, -and in the process the symbols `x` and `y` were automatically converted into -other representations). For example:: - - >>> X, Y = lr.convert(x), lr.convert(y) ; X - ╱ 3 2╲ - x + ╲- x + y ╱ - - >>> x**3 == y**2 - False - - >>> X**3 == Y**2 - True - -When no localisation is needed, a more mathematical notation can be -used. For example, let us create the coordinate ring of three-dimensional -affine space `\mathbb{A}^3`:: - - >>> ar = QQ[x, y, z] ; ar - ℚ[x, y, z] - -For more details, refer to the following class documentation. Note that -the base rings, being domains, are the main point of overlap between the -AGCA module and the rest of the polys module. All domains are documented -in detail in the polys reference, so we show here only an abridged version, -with the methods most pertinent to the AGCA module. - -.. currentmodule:: sympy.polys.domains -.. autoclass:: Ring - :members: free_module, ideal, quotient_ring - :noindex: - -.. autofunction:: PolynomialRing - :noindex: - -.. autoclass:: QuotientRing - :noindex: - -Modules, Ideals and their Elementary Properties ------------------------------------------------ - -Let `A` be a ring. An `A`-module is a set `M`, together with two binary -operations `+: M \times M \to M` and `\times: R \times M \to M` called -addition and scalar multiplication. These are required to satisfy certain -axioms, which can be found in e.g. [Atiyah69]_. In this way modules are -a direct generalisation of both vector spaces (`A` being a field) and abelian -groups (`A = \mathbb{Z}`). A *submodule* of the `A`-module `M` is a subset -`N \subset M`, such that the binary operations restrict to `N`, and `N` becomes -an `A`-module with these operations. - -The ring `A` itself has a natural `A`-module structure where addition and -multiplication in the module coincide with addition and multiplication in -the ring. This `A`-module is also written as `A`. An `A`-submodule of `A` -is called an *ideal* of `A`. Ideals come up very naturally in algebraic -geometry. More general modules can be seen as a technically convenient "elbow -room" beyond talking only about ideals. - -If `M`, `N` are `A`-modules, -then there is a natural (componentwise) `A`-module structure on `M \times N`. -Similarly there are `A`-module structures on cartesian products of more -components. (For the categorically inclined: -the cartesian product of finitely many `A`-modules, with this -`A`-module structure, is the finite biproduct in the category of all -`A`-modules. With infinitely many components, it is the direct product -(but the infinite direct sum has to be constructed differently).) As usual, -repeated product of the `A`-module `M` is denoted `M, M^2, M^3 \dots`, or -`M^I` for arbitrary index sets `I`. - -An `A`-module `M` is called *free* if it is isomorphic to the `A`-module -`A^I` for some (not necessarily finite) index set `I` (refer to the next -section for a definition of isomorphism). The cardinality of `I` is called -the *rank* of `M`; one may prove this is well-defined. -In general, the AGCA module only works with free modules of finite rank, and -other closely related modules. The easiest way to create modules is to use -member methods of the objects they are made up from. For example, let us create -a free module of rank 4 over the coordinate ring of `\mathbb{A}^2` -we created above, together with a submodule:: - - >>> F = ar.free_module(4) ; F - 4 - ℚ[x, y, z] - - >>> S = F.submodule([1, x, x**2, x**3], [0, 1, 0, y]) ; S - ╱⎡ 2 3⎤ ╲ - ╲⎣1, x, x , x ⎦, [0, 1, 0, y]╱ - -Note how python lists can be used as a short-cut notation for module -elements (vectors). As usual, the ``convert`` method can be used to convert -sympy/python objects into the internal AGCA representation (see detailed -reference below). - -Here is the detailed documentation of the classes for modules, free modules, -and submodules: - -.. currentmodule:: sympy.polys.agca.modules - -.. autoclass:: Module - :members: - -.. autoclass:: FreeModule - :members: - -.. autoclass:: SubModule - :members: - - -Ideals are created very similarly to modules. For example, let's verify -that the nodal cubic is indeed singular at the origin:: - - >>> I = lr.ideal(x, y) - >>> I == lr.ideal(x) - False - - >>> I == lr.ideal(y) - False - -We are using here the fact that a curve is non-singular at a point if and only -if the maximal ideal of the local ring is principal, and that in this case at -least one of `x` and `y` must be generators. - -This is the detailed documentation of the class ideal. Please note that most -of the methods regarding properties of ideals (primality etc.) are not yet -implemented. - -.. currentmodule:: sympy.polys.agca.ideals - -.. autoclass:: Ideal - :members: - - -If `M` is an `A`-module and `N` is an `A`-submodule, we can define two elements -`x` and `y` of `M` to be equivalent if `x - y \in N`. The set of equivalence -classes is written `M/N`, and has a natural `A`-module structure. This is -called the quotient module of `M` by `N`. If `K` is a submodule of `M` -containing `N`, then `K/N` is in a natural way a submodule of `M/N`. Such a -module is called a subquotient. Here is the documentation of quotient and -subquotient modules: - -.. currentmodule:: sympy.polys.agca.modules - -.. autoclass:: QuotientModule - :members: - -.. autoclass:: SubQuotientModule - :members: - -Module Homomorphisms and Syzygies ---------------------------------- - -Let `M` and `N` be `A`-modules. A mapping `f: M \to N` satisfying various -obvious properties (see [Atiyah69]_) is called an `A`-module homomorphism. -In this case `M` is called the *domain* and *N* the *codomain*. The -set `\{x \in M | f(x) = 0\}` is called the *kernel* `ker(f)`, whereas the -set `\{f(x) | x \in M\}` is called the *image* `im(f)`. -The kernel is a submodule of `M`, the image is a submodule of `N`. -The homomorphism `f` is injective if and only if `ker(f) = 0` and surjective -if and only if `im(f) = N`. -A bijective homomorphism is called an *isomorphism*. Equivalently, `ker(f) = 0` -and `im(f) = N`. (A related notion, which currently has no special name in -the AGCA module, is that of the *cokernel*, `coker(f) = N/im(f)`.) - -Suppose now `M` is an `A`-module. `M` is called *finitely generated* if there -exists a surjective homomorphism `A^n \to M` for some `n`. If such a morphism -`f` is chosen, the images of the standard basis of `A^n` are called the -*generators* of `M`. The module `ker(f)` is called *syzygy module* with respect -to the generators. A module is called *finitely presented* if it is finitely -generated with a finitely generated syzygy module. The class of finitely -presented modules is essentially the largest class we can hope to be able to -meaningfully compute in. - -It is an important theorem that, for all the rings we are considering, -all submodules of finitely generated modules are finitely generated, and hence -finitely generated and finitely presented modules are the same. - -The notion of syzygies, while it may first seem rather abstract, is actually -very computational. This is because there exist (fairly easy) algorithms for -computing them, and more general questions (kernels, intersections, ...) are -often reduced to syzygy computation. - -Let us say a few words about the definition of homomorphisms in the AGCA -module. Suppose first that `f : M \to N` is an arbitrary morphism of -`A`-modules. Then if `K` is a submodule of `M`, `f` naturally defines a new -homomorphism `g: K \to N` (via `g(x) = f(x)`), called the *restriction* of -`f` to `K`. If now `K` contained in the kernel of -`f`, then moreover `f` defines in a natural homomorphism `g: M/K \to N` -(same formula as above!), and we say that `f` *descends* to `M/K`. -Similarly, if `L` is a submodule of `N`, there is a natural homomorphism -`g: M \to N/L`, we say that `g` *factors* through `f`. Finally, if now `L` -contains the image of `f`, then there is a natural homomorphism `g: M \to L` -(defined, again, by the same formula), and we say `g` is obtained from `f` -by restriction of codomain. Observe also that each of these four operations -is reversible, in the sense that given `g`, one can always (non-uniquely) -find `f` such that `g` is obtained from `f` in the above way. - -Note that all modules implemented in AGCA are obtained from free modules by -taking a succession of submodules and quotients. Hence, in order to explain -how to define a homomorphism between arbitrary modules, in light of the above, -we need only explain how to define homomorphisms of free modules. But, -essentially by the definition of free module, a homomorphism from a free module -`A^n` to any module `M` is precisely the same as giving `n` elements of `M` -(the images of the standard basis), and giving an element of a free module -`A^m` is precisely the same as giving `m` elements of `A`. Hence a -homomorphism of free modules `A^n \to A^m` can be specified via a matrix, -entirely analogously to the case of vector spaces. - -The functions ``restrict_domain`` etc. of the class ``Homomorphism`` can be -used -to carry out the operations described above, and homomorphisms of free modules -can in principle be instantiated by hand. Since these operations are so common, -there is a convenience function ``homomorphism`` to define a homomorphism -between arbitrary modules via the method outlined above. It is essentially -the only way homomorphisms need ever be created by the user. - -.. currentmodule:: sympy.polys.agca.homomorphisms -.. autofunction:: homomorphism - -Finally, here is the detailed reference of the actual homomorphism class: - -.. autoclass:: ModuleHomomorphism - :members: diff --git a/dev-py3k/_sources/modules/polys/basics.txt b/dev-py3k/_sources/modules/polys/basics.txt deleted file mode 100644 index 5c87e9a615b..00000000000 --- a/dev-py3k/_sources/modules/polys/basics.txt +++ /dev/null @@ -1,226 +0,0 @@ -.. _polys-basics: - -================================= -Basic functionality of the module -================================= - -Introduction -============ - -This tutorial tries to give an overview of the functionality concerning -polynomials within SymPy. All code examples assume:: - - >>> from sympy import * - >>> x, y, z = symbols('x,y,z') - >>> init_printing(use_unicode=False, wrap_line=False, no_global=True) - -Basic functionality -=================== - -These functions provide different algorithms dealing with polynomials in the -form of SymPy expression, like symbols, sums etc. - -Division --------- - -The function :func:`div` provides division of polynomials with remainder. -That is, for polynomials ``f`` and ``g``, it computes ``q`` and ``r``, such -that `f = g \cdot q + r` and `\deg(r) < q`. For polynomials in one variables -with coefficients in a field, say, the rational numbers, ``q`` and ``r`` are -uniquely defined this way:: - - >>> f = 5*x**2 + 10*x + 3 - >>> g = 2*x + 2 - - >>> q, r = div(f, g, domain='QQ') - >>> q - 5*x 5 - --- + - - 2 2 - >>> r - -2 - >>> (q*g + r).expand() - 2 - 5*x + 10*x + 3 - -As you can see, ``q`` has a non-integer coefficient. If you want to do division -only in the ring of polynomials with integer coefficients, you can specify an -additional parameter:: - - >>> q, r = div(f, g, domain='ZZ') - >>> q - 0 - >>> r - 2 - 5*x + 10*x + 3 - -But be warned, that this ring is no longer Euclidean and that the degree of the -remainder doesn't need to be smaller than that of ``f``. Since 2 doesn't divide 5, -`2 x` doesn't divide `5 x^2`, even if the degree is smaller. But:: - - >>> g = 5*x + 1 - - >>> q, r = div(f, g, domain='ZZ') - >>> q - x - >>> r - 9*x + 3 - >>> (q*g + r).expand() - 2 - 5*x + 10*x + 3 - -This also works for polynomials with multiple variables:: - - >>> f = x*y + y*z - >>> g = 3*x + 3*z - - >>> q, r = div(f, g, domain='QQ') - >>> q - y - - - 3 - >>> r - 0 - -In the last examples, all of the three variables ``x``, ``y`` and ``z`` are -assumed to be variables of the polynomials. But if you have some unrelated -constant as coefficient, you can specify the variables explicitly:: - - >>> a, b, c = symbols('a,b,c') - >>> f = a*x**2 + b*x + c - >>> g = 3*x + 2 - >>> q, r = div(f, g, domain='QQ') - >>> q - a*x 2*a b - --- - --- + - - 3 9 3 - - >>> r - 4*a 2*b - --- - --- + c - 9 3 - -GCD and LCM ------------ - -With division, there is also the computation of the greatest common divisor and -the least common multiple. - -When the polynomials have integer coefficients, the contents' gcd is also -considered:: - - >>> f = (12*x + 12)*x - >>> g = 16*x**2 - >>> gcd(f, g) - 4*x - -But if the polynomials have rational coefficients, then the returned polynomial is -monic:: - - >>> f = 3*x**2/2 - >>> g = 9*x/4 - >>> gcd(f, g) - x - -It also works with multiple variables. In this case, the variables are ordered -alphabetically, be default, which has influence on the leading coefficient:: - - >>> f = x*y/2 + y**2 - >>> g = 3*x + 6*y - - >>> gcd(f, g) - x + 2*y - -The lcm is connected with the gcd and one can be computed using the other:: - - >>> f = x*y**2 + x**2*y - >>> g = x**2*y**2 - >>> gcd(f, g) - x*y - >>> lcm(f, g) - 3 2 2 3 - x *y + x *y - >>> (f*g).expand() - 4 3 3 4 - x *y + x *y - >>> (gcd(f, g, x, y)*lcm(f, g, x, y)).expand() - 4 3 3 4 - x *y + x *y - -Square-free factorization -------------------------- - -The square-free factorization of a univariate polynomial is the product of all -factors (not necessarily irreducible) of degree 1, 2 etc.:: - - >>> f = 2*x**2 + 5*x**3 + 4*x**4 + x**5 - - >>> sqf_list(f) - (1, [(x + 2, 1), (x, 2), (x + 1, 2)]) - - >>> sqf(f) - 2 2 - x *(x + 1) *(x + 2) - -Factorization -------------- - -This function provides factorization of univariate and multivariate polynomials -with rational coefficients:: - - >>> factor(x**4/2 + 5*x**3/12 - x**2/3) - 2 - x *(2*x - 1)*(3*x + 4) - ---------------------- - 12 - - >>> factor(x**2 + 4*x*y + 4*y**2) - 2 - (x + 2*y) - -Groebner bases --------------- - -Buchberger's algorithm is implemented, supporting various monomial orders:: - - >>> groebner([x**2 + 1, y**4*x + x**3], x, y, order='lex') - /[ 2 4 ] \ - GroebnerBasis\[x + 1, y - 1], x, y, domain=ZZ, order=lex/ - - - >>> groebner([x**2 + 1, y**4*x + x**3, x*y*z**3], x, y, z, order='grevlex') - /[ 4 3 2 ] \ - GroebnerBasis\[y - 1, z , x + 1], x, y, z, domain=ZZ, order=grevlex/ - -Solving Equations ------------------ - -We have (incomplete) methods to find the complex or even symbolic roots of -polynomials and to solve some systems of polynomial equations:: - - >>> from sympy import roots, solve_poly_system - - >>> solve(x**3 + 2*x + 3, x) - ____ ____ - 1 \/ 11 *I 1 \/ 11 *I - [-1, - - --------, - + --------] - 2 2 2 2 - - >>> p = Symbol('p') - >>> q = Symbol('q') - - >>> sorted(solve(x**2 + p*x + q, x)) - __________ __________ - / 2 / 2 - p \/ p - 4*q p \/ p - 4*q - [- - + -------------, - - - -------------] - 2 2 2 2 - - >>> solve_poly_system([y - x, x - 5], x, y) - [(5, 5)] - - >>> solve_poly_system([y**2 - x**3 + 1, y*x], x, y) - ___ ___ - 1 \/ 3 *I 1 \/ 3 *I - [(0, I), (0, -I), (1, 0), (- - + -------, 0), (- - - -------, 0)] - 2 2 2 2 diff --git a/dev-py3k/_sources/modules/polys/index.txt b/dev-py3k/_sources/modules/polys/index.txt deleted file mode 100644 index be212828af3..00000000000 --- a/dev-py3k/_sources/modules/polys/index.txt +++ /dev/null @@ -1,28 +0,0 @@ -.. _polys-docs: - -=============================== -Polynomials Manipulation Module -=============================== - -Computations with polynomials are at the core of computer algebra and -having a fast and robust polynomials manipulation module is a key for -building a powerful symbolic manipulation system. SymPy has a dedicated -module :mod:`sympy.polys` for computing in polynomial algebras over -various coefficient domains. - -There is a vast number of methods implemented, ranging from simple tools -like polynomial division, to advanced concepts including Gröbner bases -and multivariate factorization over algebraic number domains. - -Contents -======== - -.. toctree:: - :maxdepth: 3 - - basics.rst - wester.rst - reference.rst - agca.rst - internals.rst - literature.rst diff --git a/dev-py3k/_sources/modules/polys/internals.txt b/dev-py3k/_sources/modules/polys/internals.txt deleted file mode 100644 index c18fe7c2caf..00000000000 --- a/dev-py3k/_sources/modules/polys/internals.txt +++ /dev/null @@ -1,551 +0,0 @@ -.. _polys-internals: - -=============================================== -Internals of the Polynomial Manipulation Module -=============================================== - -The implementation of the polynomials module is structured internally in -"levels". There are four levels, called L0, L1, L2 and L3. The levels three -and four contain the user-facing functionality and were described in the -previous section. This section focuses on levels zero and one. - -Level zero provides core polynomial manipulation functionality with C-like, -low-level interfaces. Level one wraps this low-level functionality into object -oriented structures. These are *not* the classes seen by the user, but rather -classes used internally throughout the polys module. - -There is one additional complication in the implementation. This comes from the -fact that all polynomial manipulations are relative to a *ground domain*. For -example, when factoring a polynomial like `x^{10} - 1`, one has to decide what -ring the coefficients are supposed to belong to, or less trivially, what -coefficients are allowed to appear in the factorization. This choice of -coefficients is called a ground domain. Typical choices include the integers -`\mathbb{Z}`, the rational numbers `\mathbb{Q}` or various related rings and -fields. But it is perfectly legitimate (although in this case uninteresting) -to factorize over polynomial rings such as `k[Y]`, where `k` is some fixed -field. - -Thus the polynomial manipulation algorithms (both -complicated ones like factoring, and simpler ones like addition or -multiplication) have to rely on other code to manipulate the coefficients. -In the polynomial manipulation module, such code is encapsulated in so-called -"domains". A domain is basically a factory object: it takes various -representations of data, and converts them into objects with unified interface. -Every object created by a domain has to implement the arithmetic operations -`+`, `-` and `\times`. Other operations are accessed through the domain, e.g. -as in ``ZZ.quo(ZZ(4), ZZ(2))``. - -Note that there is some amount of *circularity*: the polynomial ring domains -use the level one classes, the level one classes use the level zero functions, -and level zero functions use domains. It is possible, in principle, but not in -the current implementation, to work in rings like `k[X][Y]`. This would create -even more layers. For this reason, working in the isomorphic ring `k[X, Y]` -is preferred. - -Domains -======= - -.. currentmodule:: sympy.polys.domains - -Here we document the various implemented ground domains. There are three -types: abstract domains, concrete domains, and "implementation domains". -Abstract domains cannot be (usefully) instantiated at all, and just collect -together functionality shared by many other domains. Concrete domains are -those meant to be instantiated and used in the polynomial manipulation -algorithms. In some cases, there are various possible ways to implement the -data type the domain provides. For example, depending on what libraries are -available on the system, the integers are implemented either using the python -built-in integers, or using gmpy. Note that various aliases are created -automatically depending on the libraries available. As such e.g. ``ZZ`` always -refers to the most efficient implementation of the integer ring available. - -Abstract Domains -**************** - -.. autoclass:: Domain - :members: - -.. autoclass:: Field - :members: - -.. autoclass:: Ring - :members: - -.. autoclass:: SimpleDomain - :members: - -.. autoclass:: CompositeDomain - :members: - -Concrete Domains -**************** - -.. autoclass:: FiniteField - :members: - -.. autoclass:: IntegerRing - :members: - -.. autoclass:: PolynomialRing - :members: - -.. autoclass:: RationalField - :members: - -.. autoclass:: AlgebraicField - :members: - -.. autoclass:: FractionField - :members: - -.. autoclass:: RealDomain - :members: - -.. autoclass:: ExpressionDomain - :members: - -Implementation Domains -********************** - -.. autoclass:: PythonFiniteField -.. autoclass:: SymPyFiniteField -.. autoclass:: GMPYFiniteField - -.. autoclass:: PythonIntegerRing -.. autoclass:: SymPyIntegerRing -.. autoclass:: GMPYIntegerRing - -.. autoclass:: PythonRationalField -.. autoclass:: SymPyRationalField -.. autoclass:: GMPYRationalField - -.. autoclass:: MPmathRealDomain - - -Level One -========= - -.. currentmodule:: sympy.polys.polyclasses - -.. autoclass:: DMP - :members: - -.. autoclass:: DMF - :members: - -.. autoclass:: ANP - :members: - -Level Zero -========== - -Level zero contains the bulk code of the polynomial manipulation module. - -Manipulation of dense, multivariate polynomials -*********************************************** - -These functions can be used to manipulate polynomials in `K[X_0, \dots, X_u]`. -Functions for manipulating multivariate polynomials in the dense representation -have the prefix ``dmp_``. Functions which only apply to univariate polynomials -(i.e. `u = 0`) -have the prefix ``dup__``. The ground domain `K` has to be passed explicitly. -For many multivariate polynomial manipulation functions also the level `u`, -i.e. the number of generators minus one, has to be passed. -(Note that, in many cases, ``dup_`` versions of functions are available, which -may be slightly more efficient.) - -**Basic manipulation:** - -.. currentmodule:: sympy.polys.densebasic - -.. autofunction:: dmp_LC -.. autofunction:: dmp_TC -.. autofunction:: dmp_ground_LC -.. autofunction:: dmp_ground_TC -.. autofunction:: dmp_true_LT -.. autofunction:: dmp_degree -.. autofunction:: dmp_degree_in -.. autofunction:: dmp_degree_list -.. autofunction:: dmp_strip -.. autofunction:: dmp_validate -.. autofunction:: dup_reverse -.. autofunction:: dmp_copy -.. autofunction:: dmp_to_tuple -.. autofunction:: dmp_normal -.. autofunction:: dmp_convert -.. autofunction:: dmp_from_sympy -.. autofunction:: dmp_nth -.. autofunction:: dmp_ground_nth -.. autofunction:: dmp_zero_p -.. autofunction:: dmp_zero -.. autofunction:: dmp_one_p -.. autofunction:: dmp_one -.. autofunction:: dmp_ground_p -.. autofunction:: dmp_ground -.. autofunction:: dmp_zeros -.. autofunction:: dmp_grounds -.. autofunction:: dmp_negative_p -.. autofunction:: dmp_positive_p -.. autofunction:: dmp_from_dict -.. autofunction:: dmp_to_dict -.. autofunction:: dmp_swap -.. autofunction:: dmp_permute -.. autofunction:: dmp_nest -.. autofunction:: dmp_raise -.. autofunction:: dmp_deflate -.. autofunction:: dmp_multi_deflate -.. autofunction:: dmp_inflate -.. autofunction:: dmp_exclude -.. autofunction:: dmp_include -.. autofunction:: dmp_inject -.. autofunction:: dmp_eject -.. autofunction:: dmp_terms_gcd -.. autofunction:: dmp_list_terms -.. autofunction:: dmp_apply_pairs -.. autofunction:: dmp_slice -.. autofunction:: dup_random - -**Arithmetic operations:** - -.. currentmodule:: sympy.polys.densearith - -.. autofunction:: dmp_add_term -.. autofunction:: dmp_sub_term -.. autofunction:: dmp_mul_term -.. autofunction:: dmp_add_ground -.. autofunction:: dmp_sub_ground -.. autofunction:: dmp_mul_ground -.. autofunction:: dmp_quo_ground -.. autofunction:: dmp_exquo_ground -.. autofunction:: dup_lshift -.. autofunction:: dup_rshift -.. autofunction:: dmp_abs -.. autofunction:: dmp_neg -.. autofunction:: dmp_add -.. autofunction:: dmp_sub -.. autofunction:: dmp_add_mul -.. autofunction:: dmp_sub_mul -.. autofunction:: dmp_mul -.. autofunction:: dmp_sqr -.. autofunction:: dmp_pow -.. autofunction:: dmp_pdiv -.. autofunction:: dmp_prem -.. autofunction:: dmp_pquo -.. autofunction:: dmp_pexquo -.. autofunction:: dmp_rr_div -.. autofunction:: dmp_ff_div -.. autofunction:: dmp_div -.. autofunction:: dmp_rem -.. autofunction:: dmp_quo -.. autofunction:: dmp_exquo -.. autofunction:: dmp_max_norm -.. autofunction:: dmp_l1_norm -.. autofunction:: dmp_expand - -**Further tools:** - -.. currentmodule:: sympy.polys.densetools - -.. autofunction:: dmp_integrate -.. autofunction:: dmp_integrate_in -.. autofunction:: dmp_diff -.. autofunction:: dmp_diff_in -.. autofunction:: dmp_eval -.. autofunction:: dmp_eval_in -.. autofunction:: dmp_eval_tail -.. autofunction:: dmp_diff_eval_in -.. autofunction:: dmp_trunc -.. autofunction:: dmp_ground_trunc -.. autofunction:: dup_monic -.. autofunction:: dmp_ground_monic -.. autofunction:: dup_content -.. autofunction:: dmp_ground_content -.. autofunction:: dup_primitive -.. autofunction:: dmp_ground_primitive -.. autofunction:: dup_extract -.. autofunction:: dmp_ground_extract -.. autofunction:: dup_real_imag -.. autofunction:: dup_mirror -.. autofunction:: dup_scale -.. autofunction:: dup_shift -.. autofunction:: dup_transform -.. autofunction:: dmp_compose -.. autofunction:: dup_decompose -.. autofunction:: dmp_lift -.. autofunction:: dup_sign_variations -.. autofunction:: dmp_clear_denoms -.. autofunction:: dmp_revert - -Manipulation of dense, univariate polynomials with finite field coefficients -**************************************************************************** -.. currentmodule:: sympy.polys.galoistools - -Functions in this module carry the prefix ``gf_``, referring to the classical -name "Galois Fields" for finite fields. Note that many polynomial -factorization algorithms work by reduction to the finite field case, so having -special implementations for this case is justified both by performance, and by -the necessity of certain methods which do not even make sense over general -fields. - -.. autofunction:: gf_crt -.. autofunction:: gf_crt1 -.. autofunction:: gf_crt2 -.. autofunction:: gf_int -.. autofunction:: gf_degree -.. autofunction:: gf_LC -.. autofunction:: gf_TC -.. autofunction:: gf_strip -.. autofunction:: gf_trunc -.. autofunction:: gf_normal -.. autofunction:: gf_convert -.. autofunction:: gf_from_dict -.. autofunction:: gf_to_dict -.. autofunction:: gf_from_int_poly -.. autofunction:: gf_to_int_poly -.. autofunction:: gf_neg -.. autofunction:: gf_add_ground -.. autofunction:: gf_sub_ground -.. autofunction:: gf_mul_ground -.. autofunction:: gf_quo_ground -.. autofunction:: gf_add -.. autofunction:: gf_sub -.. autofunction:: gf_mul -.. autofunction:: gf_sqr -.. autofunction:: gf_add_mul -.. autofunction:: gf_sub_mul -.. autofunction:: gf_expand -.. autofunction:: gf_div -.. autofunction:: gf_rem -.. autofunction:: gf_quo -.. autofunction:: gf_exquo -.. autofunction:: gf_lshift -.. autofunction:: gf_rshift -.. autofunction:: gf_pow -.. autofunction:: gf_pow_mod -.. autofunction:: gf_gcd -.. autofunction:: gf_lcm -.. autofunction:: gf_cofactors -.. autofunction:: gf_gcdex -.. autofunction:: gf_monic -.. autofunction:: gf_diff -.. autofunction:: gf_eval -.. autofunction:: gf_multi_eval -.. autofunction:: gf_compose -.. autofunction:: gf_compose_mod -.. autofunction:: gf_trace_map -.. autofunction:: gf_random -.. autofunction:: gf_irreducible -.. autofunction:: gf_irreducible_p -.. autofunction:: gf_sqf_p -.. autofunction:: gf_sqf_part -.. autofunction:: gf_sqf_list -.. autofunction:: gf_Qmatrix -.. autofunction:: gf_Qbasis -.. autofunction:: gf_berlekamp -.. autofunction:: gf_zassenhaus -.. autofunction:: gf_shoup -.. autofunction:: gf_factor_sqf -.. autofunction:: gf_factor -.. autofunction:: gf_value -.. autofunction:: gf_csolve - -Manipulation of sparse, distributed polynomials and vectors -*********************************************************** - -Dense representations quickly require infeasible amounts of storage and -computation time if the number of variables increases. For this reason, -there is code to manipulate polynomials in a *sparse* representation. These -functions carry the ``sdp_`` prefix. As in the ``sdm_`` case, the ground domain -`K` and level `u` often have to be passed explicitly. Furthermore, sparse -representations are relative to a monomial order `O`, which also has to be -passed. - -.. currentmodule:: sympy.polys.distributedpolys -.. autofunction:: sdp_LC -.. autofunction:: sdp_LM -.. autofunction:: sdp_LT -.. autofunction:: sdp_del_LT -.. autofunction:: sdp_monoms -.. autofunction:: sdp_sort -.. autofunction:: sdp_strip -.. autofunction:: sdp_normal -.. autofunction:: sdp_from_dict -.. autofunction:: sdp_to_dict -.. autofunction:: sdp_indep_p -.. autofunction:: sdp_one_p -.. autofunction:: sdp_one -.. autofunction:: sdp_term_p -.. autofunction:: sdp_abs -.. autofunction:: sdp_neg -.. autofunction:: sdp_add_term -.. autofunction:: sdp_sub_term -.. autofunction:: sdp_mul_term -.. autofunction:: sdp_add -.. autofunction:: sdp_sub -.. autofunction:: sdp_mul -.. autofunction:: sdp_sqr -.. autofunction:: sdp_pow -.. autofunction:: sdp_monic -.. autofunction:: sdp_content -.. autofunction:: sdp_primitive -.. autofunction:: sdp_div -.. autofunction:: sdp_rem -.. autofunction:: sdp_quo -.. autofunction:: sdp_exquo -.. autofunction:: sdp_lcm -.. autofunction:: sdp_gcd - -In commutative algebra, one often studies not only polynomials, but also -*modules* over polynomial rings. The polynomial manipulation module provides -rudimentary low-level support for finitely generated free modules. This is -mainly used for Groebner basis computations (see there), so manipulation -functions are only provided to the extend needed. They carry the prefix -``sdm_``. Note that in examples, the generators of the free module are called -`f_1, f_2, \dots`. - -.. currentmodule:: sympy.polys.distributedmodules - -.. autofunction:: sdm_monomial_mul -.. autofunction:: sdm_monomial_deg -.. autofunction:: sdm_monomial_divides -.. autofunction:: sdm_LC -.. autofunction:: sdm_to_dict -.. autofunction:: sdm_from_dict -.. autofunction:: sdm_add -.. autofunction:: sdm_LM -.. autofunction:: sdm_LT -.. autofunction:: sdm_mul_term -.. autofunction:: sdm_zero -.. autofunction:: sdm_deg -.. autofunction:: sdm_from_vector -.. autofunction:: sdm_to_vector - -Polynomial factorization algorithms -*********************************** - -Many variants of Euclid's algorithm: - -.. currentmodule:: sympy.polys.euclidtools - -.. autofunction:: dmp_half_gcdex -.. autofunction:: dmp_gcdex -.. autofunction:: dmp_invert -.. autofunction:: dmp_euclidean_prs -.. autofunction:: dmp_primitive_prs -.. autofunction:: dmp_inner_subresultants -.. autofunction:: dmp_subresultants -.. autofunction:: dmp_prs_resultant -.. autofunction:: dmp_zz_modular_resultant -.. autofunction:: dmp_zz_collins_resultant -.. autofunction:: dmp_qq_collins_resultant -.. autofunction:: dmp_resultant -.. autofunction:: dmp_discriminant -.. autofunction:: dmp_rr_prs_gcd -.. autofunction:: dmp_ff_prs_gcd -.. autofunction:: dmp_zz_heu_gcd -.. autofunction:: dmp_qq_heu_gcd -.. autofunction:: dmp_inner_gcd -.. autofunction:: dmp_gcd -.. autofunction:: dmp_lcm -.. autofunction:: dmp_content -.. autofunction:: dmp_primitive -.. autofunction:: dmp_cancel - -Polynomial factorization in characteristic zero: - -.. currentmodule:: sympy.polys.factortools - -.. autofunction:: dmp_trial_division -.. autofunction:: dmp_zz_mignotte_bound -.. autofunction:: dup_zz_hensel_step -.. autofunction:: dup_zz_hensel_lift -.. autofunction:: dup_zz_zassenhaus -.. autofunction:: dup_zz_irreducible_p -.. autofunction:: dup_cyclotomic_p -.. autofunction:: dup_zz_cyclotomic_poly -.. autofunction:: dup_zz_cyclotomic_factor -.. autofunction:: dup_zz_factor_sqf -.. autofunction:: dup_zz_factor -.. autofunction:: dmp_zz_wang_non_divisors -.. autofunction:: dmp_zz_wang_test_points -.. autofunction:: dmp_zz_wang_lead_coeffs -.. autofunction:: dmp_zz_diophantine -.. autofunction:: dmp_zz_wang_hensel_lifting -.. autofunction:: dmp_zz_wang -.. autofunction:: dmp_zz_factor -.. autofunction:: dmp_ext_factor -.. autofunction:: dup_gf_factor -.. autofunction:: dmp_factor_list -.. autofunction:: dmp_factor_list_include -.. autofunction:: dmp_irreducible_p - -Groebner basis algorithms -************************* - -Groebner bases can be used to answer many problems in computational -commutative algebra. Their computation in rather complicated, and very -performance-sensitive. We present here various low-level implementations of -Groebner basis computation algorithms; please see the previous section of the -manual for usage. - -.. currentmodule:: sympy.polys.groebnertools - -.. autofunction:: sdp_groebner -.. autofunction:: buchberger -.. autofunction:: sdp_spoly -.. autofunction:: f5b -.. autofunction:: red_groebner -.. autofunction:: is_groebner -.. autofunction:: is_minimal -.. autofunction:: is_reduced -.. autofunction:: matrix_fglm - -Groebner basis algorithms for modules are also provided: - -.. currentmodule:: sympy.polys.distributedmodules - -.. autofunction:: sdm_spoly -.. autofunction:: sdm_ecart -.. autofunction:: sdm_nf_mora -.. autofunction:: sdm_groebner - -Exceptions -========== - -These are exceptions defined by the polynomials module. - -TODO sort and explain - -.. currentmodule:: sympy.polys.polyerrors - -.. autoclass:: BasePolynomialError - -.. autoclass:: ExactQuotientFailed -.. autoclass:: OperationNotSupported -.. autoclass:: HeuristicGCDFailed -.. autoclass:: HomomorphismFailed -.. autoclass:: IsomorphismFailed -.. autoclass:: ExtraneousFactors -.. autoclass:: EvaluationFailed -.. autoclass:: RefinementFailed -.. autoclass:: CoercionFailed -.. autoclass:: NotInvertible -.. autoclass:: NotReversible -.. autoclass:: NotAlgebraic -.. autoclass:: DomainError -.. autoclass:: PolynomialError -.. autoclass:: UnificationFailed -.. autoclass:: GeneratorsNeeded -.. autoclass:: ComputationFailed -.. autoclass:: GeneratorsError -.. autoclass:: UnivariatePolynomialError -.. autoclass:: MultivariatePolynomialError -.. autoclass:: PolificationFailed -.. autoclass:: OptionError -.. autoclass:: FlagError - -Undocumented -============ - -Many parts of the polys module are still undocumented, and even where there is -documentation it is scarce. Please contribute! diff --git a/dev-py3k/_sources/modules/polys/literature.txt b/dev-py3k/_sources/modules/polys/literature.txt deleted file mode 100644 index 3774fdca1b1..00000000000 --- a/dev-py3k/_sources/modules/polys/literature.txt +++ /dev/null @@ -1,79 +0,0 @@ -.. _polys-literature: - -========== -Literature -========== - -The following is a non-comprehensive list of publications that were used as -a theoretical foundation for implementing polynomials manipulation module. - -.. [Kozen89] D. Kozen, S. Landau, Polynomial decomposition algorithms, - Journal of Symbolic Computation 7 (1989), pp. 445-456 - -.. [Liao95] Hsin-Chao Liao, R. Fateman, Evaluation of the heuristic - polynomial GCD, International Symposium on Symbolic and Algebraic - Computation (ISSAC), ACM Press, Montreal, Quebec, Canada, 1995, - pp. 240--247 - -.. [Gathen99] J. von zur Gathen, J. Gerhard, Modern Computer Algebra, - First Edition, Cambridge University Press, 1999 - -.. [Weisstein09] Eric W. Weisstein, Cyclotomic Polynomial, From MathWorld - A - Wolfram Web Resource, http://mathworld.wolfram.com/CyclotomicPolynomial.html - -.. [Wang78] P. S. Wang, An Improved Multivariate Polynomial Factoring - Algorithm, Math. of Computation 32, 1978, pp. 1215--1231 - -.. [Geddes92] K. Geddes, S. R. Czapor, G. Labahn, Algorithms for - Computer Algebra, Springer, 1992 - -.. [Monagan93] Michael Monagan, In-place Arithmetic for Polynomials - over Z_n, Proceedings of DISCO '92, Springer-Verlag LNCS, 721, - 1993, pp. 22--34 - -.. [Kaltofen98] E. Kaltofen, V. Shoup, Subquadratic-time Factoring of - Polynomials over Finite Fields, Mathematics of Computation, Volume - 67, Issue 223, 1998, pp. 1179--1197 - -.. [Shoup95] V. Shoup, A New Polynomial Factorization Algorithm and - its Implementation, Journal of Symbolic Computation, Volume 20, - Issue 4, 1995, pp. 363--397 - -.. [Gathen92] J. von zur Gathen, V. Shoup, Computing Frobenius Maps - and Factoring Polynomials, ACM Symposium on Theory of Computing, - 1992, pp. 187--224 - -.. [Shoup91] V. Shoup, A Fast Deterministic Algorithm for Factoring - Polynomials over Finite Fields of Small Characteristic, In Proceedings - of International Symposium on Symbolic and Algebraic Computation, 1991, - pp. 14--21 - -.. [Cox97] D. Cox, J. Little, D. O'Shea, Ideals, Varieties and - Algorithms, Springer, Second Edition, 1997 - -.. [Ajwa95] I.A. Ajwa, Z. Liu, P.S. Wang, Groebner Bases Algorithm, - http://citeseer.ist.psu.edu/ajwa95grbner.html, 1995 - -.. [Bose03] N.K. Bose, B. Buchberger, J.P. Guiver, Multidimensional - Systems Theory and Applications, Springer, 2003 - -.. [Giovini91] A. Giovini, T. Mora, "One sugar cube, please" or - Selection strategies in Buchberger algorithm, ISSAC '91, ACM - -.. [Bronstein93] M. Bronstein, B. Salvy, Full partial fraction - decomposition of rational functions, Proceedings ISSAC '93, - ACM Press, Kiev, Ukraine, 1993, pp. 157--160 - -.. [Buchberger01] B. Buchberger, Groebner Bases: A Short Introduction for - Systems Theorists, In: R. Moreno-Diaz, B. Buchberger, - J. L. Freire, Proceedings of EUROCAST'01, February, 2001 - -.. [Davenport88] J.H. Davenport, Y. Siret, E. Tournier, Computer Algebra - Systems and Algorithms for Algebraic Computation, Academic Press, London, - 1988, pp. 124--128 - -.. [Greuel2008] G.-M. Greuel, Gerhard Pfister, A Singular Introduction to - Commutative Algebra, Springer, 2008 - -.. [Atiyah69] M.F. Atiyah, I.G. MacDonald, Introduction to Commutative Algebra, - Addison-Wesley, 1969 diff --git a/dev-py3k/_sources/modules/polys/reference.txt b/dev-py3k/_sources/modules/polys/reference.txt deleted file mode 100644 index b3e5546b127..00000000000 --- a/dev-py3k/_sources/modules/polys/reference.txt +++ /dev/null @@ -1,171 +0,0 @@ -.. _polys-reference: - -========================================= -Polynomials Manipulation Module Reference -========================================= - -Basic polynomial manipulation functions -======================================= - -.. currentmodule:: sympy.polys.polytools - -.. autofunction:: poly -.. autofunction:: poly_from_expr -.. autofunction:: parallel_poly_from_expr -.. autofunction:: degree -.. autofunction:: degree_list -.. autofunction:: LC -.. autofunction:: LM -.. autofunction:: LT -.. autofunction:: pdiv -.. autofunction:: prem -.. autofunction:: pquo -.. autofunction:: pexquo -.. autofunction:: div -.. autofunction:: rem -.. autofunction:: quo -.. autofunction:: exquo -.. autofunction:: half_gcdex -.. autofunction:: gcdex -.. autofunction:: invert -.. autofunction:: subresultants -.. autofunction:: resultant -.. autofunction:: discriminant -.. autofunction:: terms_gcd -.. autofunction:: cofactors -.. autofunction:: gcd -.. autofunction:: gcd_list -.. autofunction:: lcm -.. autofunction:: lcm_list -.. autofunction:: trunc -.. autofunction:: monic -.. autofunction:: content -.. autofunction:: primitive -.. autofunction:: compose -.. autofunction:: decompose -.. autofunction:: sturm -.. autofunction:: gff_list -.. autofunction:: gff -.. autofunction:: sqf_norm -.. autofunction:: sqf_part -.. autofunction:: sqf_list -.. autofunction:: sqf -.. autofunction:: factor_list -.. autofunction:: factor -.. autofunction:: intervals -.. autofunction:: refine_root -.. autofunction:: count_roots -.. autofunction:: real_roots -.. autofunction:: nroots -.. autofunction:: ground_roots -.. autofunction:: nth_power_roots_poly -.. autofunction:: cancel -.. autofunction:: reduced -.. autofunction:: groebner -.. autofunction:: is_zero_dimensional - -.. autoclass:: Poly - :members: - -.. autoclass:: PurePoly - :members: - -.. autoclass:: GroebnerBasis - :members: - -Extra polynomial manipulation functions -======================================= - -.. currentmodule:: sympy.polys.polyfuncs - -.. autofunction:: symmetrize -.. autofunction:: horner -.. autofunction:: interpolate -.. autofunction:: viete - -Domain constructors -=================== - -.. currentmodule:: sympy.polys.constructor - -.. autofunction:: construct_domain - -Algebraic number fields -======================= - -.. currentmodule:: sympy.polys.numberfields - -.. autofunction:: minimal_polynomial -.. autofunction:: minpoly -.. autofunction:: primitive_element -.. autofunction:: field_isomorphism -.. autofunction:: to_number_field -.. autofunction:: isolate - -.. autoclass:: AlgebraicNumber - :members: - -Monomials encoded as tuples -=========================== - -.. currentmodule:: sympy.polys.monomialtools - -.. autoclass:: Monomial -.. autofunction:: monomials -.. autofunction:: monomial_count -.. autoclass:: LexOrder -.. autoclass:: GradedLexOrder -.. autoclass:: ReversedGradedLexOrder - -Formal manipulation of roots of polynomials -=========================================== - -.. currentmodule:: sympy.polys.rootoftools - -.. autoclass:: RootOf -.. autoclass:: RootSum - -Symbolic root-finding algorithms -================================ - -.. currentmodule:: sympy.polys.polyroots - -.. autofunction:: roots - -Special polynomials -=================== - -.. currentmodule:: sympy.polys.specialpolys - -.. autofunction:: swinnerton_dyer_poly -.. autofunction:: interpolating_poly -.. autofunction:: cyclotomic_poly -.. autofunction:: symmetric_poly -.. autofunction:: random_poly - -Orthogonal polynomials -====================== - -.. currentmodule:: sympy.polys.orthopolys - -.. autofunction:: chebyshevt_poly -.. autofunction:: chebyshevu_poly -.. autofunction:: gegenbauer_poly -.. autofunction:: hermite_poly -.. autofunction:: jacobi_poly -.. autofunction:: legendre_poly -.. autofunction:: laguerre_poly - -Manipulation of rational functions -================================== - -.. currentmodule:: sympy.polys.rationaltools - -.. autofunction:: together - -Partial fraction decomposition -============================== - -.. currentmodule:: sympy.polys.partfrac - -.. autofunction:: apart diff --git a/dev-py3k/_sources/modules/polys/wester.txt b/dev-py3k/_sources/modules/polys/wester.txt deleted file mode 100644 index 39a3b2d15b3..00000000000 --- a/dev-py3k/_sources/modules/polys/wester.txt +++ /dev/null @@ -1,451 +0,0 @@ -.. _polys-wester: - -============================== -Examples from Wester's Article -============================== - -Introduction -============ - -In this tutorial we present examples from Wester's article concerning -comparison and critique of mathematical abilities of several computer -algebra systems (see [Wester1999]_). All the examples are related to -polynomial and algebraic computations and SymPy specific remarks were -added to all of them. - -Examples -======== - -All examples in this tutorial are computable, so one can just copy and -paste them into a Python shell and do something useful with them. All -computations were done using the following setup:: - - >>> from sympy import * - - >>> init_printing(use_unicode=True, wrap_line=False, no_global=True) - - >>> var('x,y,z,s,c,n') - (x, y, z, s, c, n) - -Simple univariate polynomial factorization ------------------------------------------- - -To obtain a factorization of a polynomial use :func:`factor` function. -By default :func:`factor` returns the result in unevaluated form, so the -content of the input polynomial is left unexpanded, as in the following -example:: - - >>> factor(6*x - 10) - 2⋅(3⋅x - 5) - -To achieve the same effect in a more systematic way use :func:`primitive` -function, which returns the content and the primitive part of the input -polynomial:: - - >>> primitive(6*x - 10) - (2, 3⋅x - 5) - -.. note:: - - The content and the primitive part can be computed only over a ring. To - simplify coefficients of a polynomial over a field use :func:`monic`. - -Univariate GCD, resultant and factorization -------------------------------------------- - -Consider univariate polynomials ``f``, ``g`` and ``h`` over integers:: - - >>> f = 64*x**34 - 21*x**47 - 126*x**8 - 46*x**5 - 16*x**60 - 81 - >>> g = 72*x**60 - 25*x**25 - 19*x**23 - 22*x**39 - 83*x**52 + 54*x**10 + 81 - >>> h = 34*x**19 - 25*x**16 + 70*x**7 + 20*x**3 - 91*x - 86 - -We can compute the greatest common divisor (GCD) of two polynomials using -:func:`gcd` function:: - - >>> gcd(f, g) - 1 - -We see that ``f`` and ``g`` have no common factors. However, ``f*h`` and ``g*h`` -have an obvious factor ``h``:: - - >>> gcd(expand(f*h), expand(g*h)) - h - 0 - -The same can be verified using the resultant of univariate polynomials:: - - >>> resultant(expand(f*h), expand(g*h)) - 0 - -Factorization of large univariate polynomials (of degree 120 in this case) over -integers is also possible:: - - >>> factor(expand(f*g)) - ⎛ 60 47 34 8 5 ⎞ ⎛ 60 52 39 25 23 10 ⎞ - -⎝16⋅x + 21⋅x - 64⋅x + 126⋅x + 46⋅x + 81⎠⋅⎝72⋅x - 83⋅x - 22⋅x - 25⋅x - 19⋅x + 54⋅x + 81⎠ - -Multivariate GCD and factorization ----------------------------------- - -What can be done in univariate case, can be also done for multivariate -polynomials. Consider the following polynomials ``f``, ``g`` and ``h`` -in `\mathbb{Z}[x,y,z]`:: - - >>> f = 24*x*y**19*z**8 - 47*x**17*y**5*z**8 + 6*x**15*y**9*z**2 - 3*x**22 + 5 - >>> g = 34*x**5*y**8*z**13 + 20*x**7*y**7*z**7 + 12*x**9*y**16*z**4 + 80*y**14*z - >>> h = 11*x**12*y**7*z**13 - 23*x**2*y**8*z**10 + 47*x**17*y**5*z**8 - -As previously, we can verify that ``f`` and ``g`` have no common factors:: - - >>> gcd(f, g) - 1 - -However, ``f*h`` and ``g*h`` have an obvious factor ``h``:: - - >>> gcd(expand(f*h), expand(g*h)) - h - 0 - -Multivariate factorization of large polynomials is also possible:: - - >>> factor(expand(f*g)) - 7 ⎛ 9 9 3 7 6 5 12 7⎞ ⎛ 22 17 5 8 15 9 2 19 8 ⎞ - -2⋅y ⋅z⋅⎝6⋅x ⋅y ⋅z + 10⋅x ⋅z + 17⋅x ⋅y⋅z + 40⋅y ⎠⋅⎝3⋅x + 47⋅x ⋅y ⋅z - 6⋅x ⋅y ⋅z - 24⋅x⋅y ⋅z - 5⎠ - -Support for symbols in exponents --------------------------------- - -Polynomial manipulation functions provided by :mod:`sympy.polys` are mostly -used with integer exponents. However, it's perfectly valid to compute with -symbolic exponents, e.g.:: - - >>> gcd(2*x**(n + 4) - x**(n + 2), 4*x**(n + 1) + 3*x**n) - n - x - -Testing if polynomials have common zeros ----------------------------------------- - -To test if two polynomials have a root in common we can use :func:`resultant` -function. The theory says that the resultant of two polynomials vanishes if -there is a common zero of those polynomials. For example:: - - >>> resultant(3*x**4 + 3*x**3 + x**2 - x - 2, x**3 - 3*x**2 + x + 5) - 0 - -We can visualize this fact by factoring the polynomials:: - - >>> factor(3*x**4 + 3*x**3 + x**2 - x - 2) - ⎛ 3 ⎞ - (x + 1)⋅⎝3⋅x + x - 2⎠ - - >>> factor(x**3 - 3*x**2 + x + 5) - ⎛ 2 ⎞ - (x + 1)⋅⎝x - 4⋅x + 5⎠ - -In both cases we obtained the factor `x + 1` which tells us that the common -root is `x = -1`. - -Normalizing simple rational functions -------------------------------------- - -To remove common factors from the numerator and the denominator of a rational -function the elegant way, use :func:`cancel` function. For example:: - - >>> cancel((x**2 - 4)/(x**2 + 4*x + 4)) - x - 2 - ───── - x + 2 - -Expanding expressions and factoring back ----------------------------------------- - -One can work easily we expressions in both expanded and factored forms. -Consider a polynomial ``f`` in expanded form. We differentiate it and -factor the result back:: - - >>> f = expand((x + 1)**20) - - >>> g = diff(f, x) - - >>> factor(g) - 19 - 20⋅(x + 1) - -The same can be achieved in factored form:: - - >>> diff((x + 1)**20, x) - 19 - 20⋅(x + 1) - -Factoring in terms of cyclotomic polynomials --------------------------------------------- - -SymPy can very efficiently decompose polynomials of the form `x^n \pm 1` in -terms of cyclotomic polynomials:: - - >>> factor(x**15 - 1) - ⎛ 2 ⎞ ⎛ 4 3 2 ⎞ ⎛ 8 7 5 4 3 ⎞ - (x - 1)⋅⎝x + x + 1⎠⋅⎝x + x + x + x + 1⎠⋅⎝x - x + x - x + x - x + 1⎠ - -The original Wester`s example was `x^{100} - 1`, but was truncated for -readability purpose. Note that this is not a big struggle for :func:`factor` -to decompose polynomials of degree 1000 or greater. - -Univariate factoring over Gaussian numbers ------------------------------------------- - -Consider a univariate polynomial ``f`` with integer coefficients:: - - >>> f = 4*x**4 + 8*x**3 + 77*x**2 + 18*x + 153 - -We want to obtain a factorization of ``f`` over Gaussian numbers. To do this -we use :func:`factor` as previously, but this time we set ``gaussian`` keyword -to ``True``:: - - >>> factor(f, gaussian=True) - ⎛ 3⋅ⅈ⎞ ⎛ 3⋅ⅈ⎞ - 4⋅⎜x - ───⎟⋅⎜x + ───⎟⋅(x + 1 - 4⋅ⅈ)⋅(x + 1 + 4⋅ⅈ) - ⎝ 2 ⎠ ⎝ 2 ⎠ - -As the result we got a splitting factorization of ``f`` with monic factors -(this is a general rule when computing in a field with SymPy). The ``gaussian`` -keyword is useful for improving code readability, however the same result can -be computed using more general syntax:: - - >>> factor(f, extension=I) - ⎛ 3⋅ⅈ⎞ ⎛ 3⋅ⅈ⎞ - 4⋅⎜x - ───⎟⋅⎜x + ───⎟⋅(x + 1 - 4⋅ⅈ)⋅(x + 1 + 4⋅ⅈ) - ⎝ 2 ⎠ ⎝ 2 ⎠ - -Computing with automatic field extensions ------------------------------------------ - -Consider two univariate polynomials ``f`` and ``g``:: - - >>> f = x**3 + (sqrt(2) - 2)*x**2 - (2*sqrt(2) + 3)*x - 3*sqrt(2) - >>> g = x**2 - 2 - -We would like to reduce degrees of the numerator and the denominator of a -rational function ``f/g``. Do do this we employ :func:`cancel` function:: - - >>> cancel(f/g) - 3 2 ___ 2 ___ ___ - x - 2⋅x + ╲╱ 2 ⋅x - 3⋅x - 2⋅╲╱ 2 ⋅x - 3⋅╲╱ 2 - ──────────────────────────────────────────────── - 2 - x - 2 - -Unfortunately nothing interesting happened. This is because by default SymPy -treats `\sqrt{2}` as a generator, obtaining a bivariate polynomial for the -numerator. To make :func:`cancel` recognize algebraic properties of `\sqrt{2}`, -one needs to use ``extension`` keyword:: - - >>> cancel(f/g, extension=True) - 2 - x - 2⋅x - 3 - ──────────── - ___ - x - ╲╱ 2 - -Setting ``extension=True`` tells :func:`cancel` to find minimal algebraic -number domain for the coefficients of ``f/g``. The automatically inferred -domain is `\mathbb{Q}(\sqrt{2})`. If one doesn't want to rely on automatic -inference, the same result can be obtained by setting the ``extension`` -keyword with an explicit algebraic number:: - - >>> cancel(f/g, extension=sqrt(2)) - 2 - x - 2⋅x - 3 - ──────────── - ___ - x - ╲╱ 2 - -Univariate factoring over various domains ------------------------------------------ - -Consider a univariate polynomial ``f`` with integer coefficients:: - - >>> f = x**4 - 3*x**2 + 1 - -With :mod:`sympy.polys` we can obtain factorizations of ``f`` over different -domains, which includes: - -* rationals:: - - >>> factor(f) - ⎛ 2 ⎞ ⎛ 2 ⎞ - ⎝x - x - 1⎠⋅⎝x + x - 1⎠ - -* finite fields:: - - >>> factor(f, modulus=5) - 2 2 - (x - 2) ⋅(x + 2) - -* algebraic numbers:: - - >>> alg = AlgebraicNumber((sqrt(5) - 1)/2, alias='alpha') - - >>> factor(f, extension=alg) - (x - 1 - α)⋅(x - α)⋅(x + α)⋅(x + 1 + α) - -Factoring polynomials into linear factors ------------------------------------------ - -Currently SymPy can factor polynomials into irreducibles over various domains, -which can result in a splitting factorization (into linear factors). However, -there is currently no systematic way to infer a splitting field (algebraic -number field) automatically. In future the following syntax will be -implemented:: - - >>> factor(x**3 + x**2 - 7, split=True) - Traceback (most recent call last): - ... - NotImplementedError: 'split' option is not implemented yet - -Note this is different from ``extension=True``, because the later only tells how -expression parsing should be done, not what should be the domain of computation. -One can simulate the ``split`` keyword for several classes of polynomials using -:func:`solve` function. - -Advanced factoring over finite fields -------------------------------------- - -Consider a univariate polynomial ``f`` with integer coefficients:: - - >>> f = x**11 + x + 1 - -We can factor ``f`` over a large finite field `F_{65537}`:: - - >>> factor(f, modulus=65537) - ⎛ 2 ⎞ ⎛ 9 8 6 5 3 2 ⎞ - ⎝x + x + 1⎠⋅⎝x - x + x - x + x - x + 1⎠ - -and expand the resulting factorization back:: - - >>> expand(_) - 11 - x + x + 1 - -obtaining polynomial ``f``. This was done using symmetric polynomial -representation over finite fields The same thing can be done using -non-symmetric representation:: - - >>> factor(f, modulus=65537, symmetric=False) - ⎛ 2 ⎞ ⎛ 9 8 6 5 3 2 ⎞ - ⎝x + x + 1⎠⋅⎝x + 65536⋅x + x + 65536⋅x + x + 65536⋅x + 1⎠ - -As with symmetric representation we can expand the factorization -to get the input polynomial back. This time, however, we need to -truncate coefficients of the expanded polynomial modulo 65537:: - - >>> trunc(expand(_), 65537) - 11 - x + x + 1 - -Working with expressions as polynomials ---------------------------------------- - -Consider a multivariate polynomial ``f`` in `\mathbb{Z}[x,y,z]`:: - - >>> f = expand((x - 2*y**2 + 3*z**3)**20) - -We want to compute factorization of ``f``. To do this we use ``factor`` as -usually, however we note that the polynomial in consideration is already -in expanded form, so we can tell the factorization routine to skip -expanding ``f``:: - - >>> factor(f, expand=False) - 20 - ⎛ 2 3⎞ - ⎝x - 2⋅y + 3⋅z ⎠ - -The default in :mod:`sympy.polys` is to expand all expressions given as -arguments to polynomial manipulation functions and :class:`Poly` class. -If we know that expanding is unnecessary, then by setting ``expand=False`` -we can save quite a lot of time for complicated inputs. This can be really -important when computing with expressions like:: - - >>> g = expand((sin(x) - 2*cos(y)**2 + 3*tan(z)**3)**20) - - >>> factor(g, expand=False) - 20 - ⎛ 2 3 ⎞ - ⎝-sin(x) + 2⋅cos (y) - 3⋅tan (z)⎠ - -Computing reduced Gröbner bases -------------------------------- - -To compute a reduced Gröbner basis for a set of polynomials use -:func:`groebner` function. The function accepts various monomial -orderings, e.g.: ``lex``, ``grlex`` and ``grevlex``, or a user -defined one, via ``order`` keyword. The ``lex`` ordering is the -most interesting because it has elimination property, which means -that if the system of polynomial equations to :func:`groebner` is -zero-dimensional (has finite number of solutions) the last element -of the basis is a univariate polynomial. Consider the following example:: - - >>> f = expand((1 - c**2)**5 * (1 - s**2)**5 * (c**2 + s**2)**10) - - >>> groebner([f, c**2 + s**2 - 1]) - ⎛⎡ 2 2 20 18 16 14 12 10⎤ ⎞ - GroebnerBasis⎝⎣c + s - 1, c - 5⋅c + 10⋅c - 10⋅c + 5⋅c - c ⎦, s, c, domain=ℤ, order=lex⎠ - -The result is an ordinary Python list, so we can easily apply a function to -all its elements, for example we can factor those elements:: - - >>> list(map(factor, _)) - ⎡ 2 2 10 5 5⎤ - ⎣c + s - 1, c ⋅(c - 1) ⋅(c + 1) ⎦ - -From the above we can easily find all solutions of the system of polynomial -equations. Or we can use :func:`solve` to achieve this in a more systematic -way:: - - >>> solve([f, s**2 + c**2 - 1], c, s) - [(-1, 0), (0, -1), (0, 1), (1, 0)] - -Multivariate factoring over algebraic numbers ---------------------------------------------- - -Computing with multivariate polynomials over various domains is as simple as -in univariate case. For example consider the following factorization over -`\mathbb{Q}(\sqrt{-3})`:: - - >>> factor(x**3 + y**3, extension=sqrt(-3)) - ⎛ ⎛ ___ ⎞⎞ ⎛ ⎛ ___ ⎞⎞ - ⎜ ⎜ 1 ╲╱ 3 ⋅ⅈ⎟⎟ ⎜ ⎜ 1 ╲╱ 3 ⋅ⅈ⎟⎟ - (x + y)⋅⎜x + y⋅⎜- ─ - ───────⎟⎟⋅⎜x + y⋅⎜- ─ + ───────⎟⎟ - ⎝ ⎝ 2 2 ⎠⎠ ⎝ ⎝ 2 2 ⎠⎠ - -.. note:: Currently multivariate polynomials over finite fields aren't supported. - -Partial fraction decomposition ------------------------------- - -Consider a univariate rational function ``f`` with integer coefficients:: - - >>> f = (x**2 + 2*x + 3)/(x**3 + 4*x**2 + 5*x + 2) - -To decompose ``f`` into partial fractions use :func:`apart` function:: - - >>> apart(f) - 3 2 2 - ───── - ───── + ──────── - x + 2 x + 1 2 - (x + 1) - -To return from partial fractions to the rational function use -a composition of :func:`together` and :func:`cancel`:: - - >>> cancel(together(_)) - 2 - x + 2⋅x + 3 - ─────────────────── - 3 2 - x + 4⋅x + 5⋅x + 2 - -Literature -========== - -.. [Wester1999] Michael J. Wester, A Critique of the Mathematical Abilities of - CA Systems, 1999, ``_ diff --git a/dev-py3k/_sources/modules/printing.txt b/dev-py3k/_sources/modules/printing.txt deleted file mode 100644 index 6317d575635..00000000000 --- a/dev-py3k/_sources/modules/printing.txt +++ /dev/null @@ -1,444 +0,0 @@ -Printing System -=============== - -See the :ref:`printing-tutorial` section in Tutorial for introduction into -printing. - -This guide documents the printing system in SymPy and how it works -internally. - -Printer Class -------------- - -.. automodule:: sympy.printing.printer - -The main class responsible for printing is ``Printer`` (see also its -`source code -`_): - -.. autoclass:: Printer - :members: doprint, _print, set_global_settings, order - - .. autoattribute:: Printer.printmethod - - -PrettyPrinter Class -------------------- - -Pretty printing subsystem is implemented in ``sympy.printing.pretty.pretty`` -by the ``PrettyPrinter`` class deriving from ``Printer``. It relies on -modules ``sympy.printing.pretty.stringPict``, and -``sympy.printing.pretty.pretty_symbology`` for rendering nice-looking -formulas. - -The module ``stringPict`` provides a base class ``stringPict`` and a derived -class ``prettyForm`` that ease the creation and manipulation of formulas -that span across multiple lines. - -The module ``pretty_symbology`` provides primitives to construct 2D shapes -(hline, vline, etc) together with a technique to use unicode automatically -when possible. - -.. module:: sympy.printing.pretty.pretty - -.. autoclass:: PrettyPrinter - :members: _use_unicode, doprint - - .. autoattribute:: PrettyPrinter.printmethod - -.. autofunction:: pretty -.. autofunction:: pretty_print - -CCodePrinter ------------- - -.. module:: sympy.printing.ccode - -This class implements C code printing (i.e. it converts Python expressions -to strings of C code). - -Usage:: - - >>> from sympy.printing import print_ccode - >>> from sympy.functions import sin, cos, Abs - >>> from sympy.abc import x - >>> print_ccode(sin(x)**2 + cos(x)**2) - pow(sin(x), 2) + pow(cos(x), 2) - >>> print_ccode(2*x + cos(x), assign_to="result") - result = 2*x + cos(x); - >>> print_ccode(Abs(x**2)) - fabs(pow(x, 2)) - -.. autodata:: sympy.printing.ccode.known_functions - -.. autoclass:: sympy.printing.ccode.CCodePrinter - :members: - - .. autoattribute:: CCodePrinter.printmethod - - -.. autofunction:: sympy.printing.ccode.ccode - -.. autofunction:: sympy.printing.ccode.print_ccode - -Fortran Printing ----------------- - -The fcode function translates a sympy expression into Fortran code. The main -purpose is to take away the burden of manually translating long mathematical -expressions. Therefore the resulting expression should also require no (or -very little) manual tweaking to make it compilable. The optional arguments -of fcode can be used to fine-tune the behavior of fcode in such a way that -manual changes in the result are no longer needed. - -.. module:: sympy.printing.fcode -.. autofunction:: fcode -.. autofunction:: print_fcode -.. autoclass:: FCodePrinter - :members: - - .. autoattribute:: FCodePrinter.printmethod - - -Two basic examples: - - >>> from sympy import * - >>> x = symbols("x") - >>> fcode(sqrt(1-x**2)) - ' sqrt(-x**2 + 1)' - >>> fcode((3 + 4*I)/(1 - conjugate(x))) - ' (cmplx(3,4))/(-conjg(x) + 1)' - -An example where line wrapping is required: - - >>> expr = sqrt(1-x**2).series(x,n=20).removeO() - >>> print(fcode(expr)) - -715*x**18/65536 - 429*x**16/32768 - 33*x**14/2048 - 21*x**12/1024 - @ - 7*x**10/256 - 5*x**8/128 - x**6/16 - x**4/8 - x**2/2 + 1 - -In case of line wrapping, it is handy to include the assignment so that lines -are wrapped properly when the assignment part is added. - - >>> print(fcode(expr, assign_to="var")) - var = -715*x**18/65536 - 429*x**16/32768 - 33*x**14/2048 - 21*x** - @ 12/1024 - 7*x**10/256 - 5*x**8/128 - x**6/16 - x**4/8 - x**2/2 + - @ 1 - -For piecewise functions, the assign_to option is mandatory: - - >>> print(fcode(Piecewise((x,x<1),(x**2,True)), assign_to="var")) - if (x < 1) then - var = x - else - var = x**2 - end if - -Note that only top-level piecewise functions are supported due to the lack of -a conditional operator in Fortran. Nested piecewise functions would require the -introduction of temporary variables, which is a type of expression manipulation -that goes beyond the scope of fcode. - -Loops are generated if there are Indexed objects in the expression. This -also requires use of the assign_to option. - - >>> A, B = list(map(IndexedBase, ['A', 'B'])) - >>> m = Symbol('m', integer=True) - >>> i = Idx('i', m) - >>> print(fcode(2*B[i], assign_to=A[i])) - do i = 1, m - A(i) = 2*B(i) - end do - -Repeated indices in an expression with Indexed objects are interpreted as -summation. For instance, code for the trace of a matrix can be generated -with - - >>> print(fcode(A[i, i], assign_to=x)) - x = 0 - do i = 1, m - x = x + A(i, i) - end do - -By default, number symbols such as ``pi`` and ``E`` are detected and defined as -Fortran parameters. The precision of the constants can be tuned with the -precision argument. Parameter definitions are easily avoided using the ``N`` -function. - - >>> print(fcode(x - pi**2 - E)) - parameter (E = 2.71828182845905d0) - parameter (pi = 3.14159265358979d0) - x - pi**2 - E - >>> print(fcode(x - pi**2 - E, precision=25)) - parameter (E = 2.718281828459045235360287d0) - parameter (pi = 3.141592653589793238462643d0) - x - pi**2 - E - >>> print(fcode(N(x - pi**2, 25))) - x - 9.869604401089358618834491d0 - -When some functions are not part of the Fortran standard, it might be desirable -to introduce the names of user-defined functions in the Fortran expression. - - >>> print(fcode(1 - gamma(x)**2, user_functions={gamma: 'mygamma'})) - -mygamma(x)**2 + 1 - -However, when the user_functions argument is not provided, fcode attempts to -use a reasonable default and adds a comment to inform the user of the issue. - - >>> print(fcode(1 - gamma(x)**2)) - C Not Fortran: - C gamma(x) - -gamma(x)**2 + 1 - -By default the output is human readable code, ready for copy and paste. With the -option ``human=False``, the return value is suitable for post-processing with -source code generators that write routines with multiple instructions. The -return value is a three-tuple containing: (i) a set of number symbols that must -be defined as 'Fortran parameters', (ii) a list functions that can not be -translated in pure Fortran and (iii) a string of Fortran code. A few examples: - - >>> fcode(1 - gamma(x)**2, human=False) - (set(), set([gamma(x)]), ' -gamma(x)**2 + 1') - >>> fcode(1 - sin(x)**2, human=False) - (set(), set(), ' -sin(x)**2 + 1') - >>> fcode(x - pi**2, human=False) - (set([(pi, '3.14159265358979d0')]), set(), ' x - pi**2') - -Gtk ---- - -.. module:: sympy.printing.gtk - -You can print to a grkmathview widget using the function ``print_gtk`` -located in ``sympy.printing.gtk`` (it requires to have installed -gtkmatmatview and libgtkmathview-bin in some systems). - -GtkMathView accepts MathML, so this rendering depends on the MathML -representation of the expression. - -Usage:: - - from sympy import * - print_gtk(x**2 + 2*exp(x**3)) - -.. autofunction:: print_gtk - -LambdaPrinter -------------- - -.. module:: sympy.printing.lambdarepr - -This classes implements printing to strings that can be used by the -:py:func:`sympy.utilities.lambdify.lambdify` function. - -.. autoclass:: LambdaPrinter - - .. autoattribute:: LambdaPrinter.printmethod - - -.. autofunction:: lambdarepr - -LatexPrinter ------------- - -.. module:: sympy.printing.latex - -This class implements LaTeX printing. See ``sympy.printing.latex``. - -See also the extended LatexPrinter: :ref:`Extended LaTeXModule for SymPy ` - -.. autodata:: accepted_latex_functions - -.. autoclass:: LatexPrinter - :members: - - .. autoattribute:: LatexPrinter.printmethod - -.. autofunction:: latex - -.. autofunction:: print_latex - -MathMLPrinter -------------- - -.. module:: sympy.printing.mathml - -This class is responsible for MathML printing. See ``sympy.printing.mathml``. - -More info on mathml content: http://www.w3.org/TR/MathML2/chapter4.html - -.. autoclass:: MathMLPrinter - :members: - - .. autoattribute:: MathMLPrinter.printmethod - -.. autofunction:: mathml - -.. autofunction:: print_mathml - -PythonPrinter -------------- - -.. module:: sympy.printing.python - -This class implements Python printing. Usage:: - - >>> from sympy import print_python, sin - >>> from sympy.abc import x - - >>> print_python(5*x**3 + sin(x)) - x = Symbol('x') - e = 5*x**3 + sin(x) - -ReprPrinter ------------ - -.. module:: sympy.printing.repr - -This printer generates executable code. This code satisfies the identity -``eval(srepr(expr))=expr``. - -.. autoclass:: ReprPrinter - :members: - - .. autoattribute:: ReprPrinter.printmethod - -.. autofunction:: srepr - -StrPrinter ----------- - -.. module:: sympy.printing.str - -This module generates readable representations of SymPy expressions. - -.. autoclass:: StrPrinter - :members: parenthesize, stringify, emptyPrinter - - .. autoattribute:: StrPrinter.printmethod - -.. autofunction:: sstrrepr - -Tree Printing -------------- - -.. module:: sympy.printing.tree - -The functions in this module create a representation of an expression as a -tree. - -.. autofunction:: pprint_nodes - -.. autofunction:: print_node - -.. autofunction:: tree - -.. autofunction:: print_tree - -Preview -------- - -A useful function is ``preview``: - -.. module:: sympy.printing.preview - -.. autofunction:: preview - -Implementation - Helper Classes/Functions ------------------------------------------ - -.. module:: sympy.printing.conventions - -.. autofunction:: split_super_sub - -CodePrinter -+++++++++++ - -.. module:: sympy.printing.codeprinter - -This class is a base class for other classes that implement code-printing -functionality, and additionally lists a number of functions that cannot be -easily translated to C or Fortran. - -.. autoclass:: sympy.printing.codeprinter.CodePrinter - - .. autoattribute:: CodePrinter.printmethod - -.. autoexception:: sympy.printing.codeprinter.AssignmentError - -Precedence -++++++++++ - -.. module:: sympy.printing.precedence - -.. autodata:: PRECEDENCE - - Default precedence values for some basic types. - -.. autodata:: PRECEDENCE_VALUES - - A dictionary assigning precedence values to certain classes. These values - are treated like they were inherited, so not every single class has to be - named here. - -.. autodata:: PRECEDENCE_FUNCTIONS - - Sometimes it's not enough to assign a fixed precedence value to a - class. Then a function can be inserted in this dictionary that takes an - instance of this class as argument and returns the appropriate precedence - value. - -.. autofunction:: precedence - -Pretty-Printing Implementation Helpers --------------------------------------- - -.. module:: sympy.printing.pretty.pretty_symbology - -.. autofunction:: U -.. autofunction:: pretty_use_unicode -.. autofunction:: pretty_try_use_unicode -.. autofunction:: xstr - -The following two functions return the Unicode version of the inputted Greek -letter. - -.. autofunction:: g -.. autofunction:: G -.. autodata:: greek_letters -.. autodata:: greek -.. autodata:: digit_2txt -.. autodata:: symb_2txt - -The following functions return the Unicode subscript/superscript version of -the character. - -.. autodata:: sub -.. autodata:: sup - -The following functions return Unicode vertical objects. - -.. autofunction:: xobj -.. autofunction:: vobj -.. autofunction:: hobj - -The following constants are for rendering roots and fractions. - -.. autodata:: root -.. autofunction:: VF -.. autodata:: frac - -The following constants/functions are for rendering atoms and symbols. - -.. autofunction:: xsym -.. autodata:: atoms_table -.. autofunction:: pretty_atom -.. autofunction:: pretty_symbol -.. autofunction:: annotated - -.. automodule:: sympy.printing.pretty.stringpict - -.. autoclass:: stringPict - :members: - -.. autoclass:: prettyForm - :members: diff --git a/dev-py3k/_sources/modules/rewriting.txt b/dev-py3k/_sources/modules/rewriting.txt deleted file mode 100644 index 266c4eff96e..00000000000 --- a/dev-py3k/_sources/modules/rewriting.txt +++ /dev/null @@ -1,92 +0,0 @@ -Term rewriting -============== - -Term rewriting is a very general class of functionalities which are used to -convert expressions of one type in terms of expressions of different kind. For -example expanding, combining and converting expressions apply to term -rewriting, and also simplification routines can be included here. Currently -!SymPy has several functions and Basic built-in methods for performing various -types of rewriting. - -Expanding ---------- - -The simplest rewrite rule is expanding expressions into a _sparse_ form. -Expanding has several flavors and include expanding complex valued expressions, -arithmetic expand of products and powers but also expanding functions in terms -of more general functions is possible. Below are listed all currently available -expand rules. - -Expanding of arithmetic expressions involving products and powers: - >>> from sympy import * - >>> x, y, z = symbols('x,y,z') - >>> ((x + y)*(x - y)).expand(basic=True) - x**2 - y**2 - >>> ((x + y + z)**2).expand(basic=True) - x**2 + 2*x*y + 2*x*z + y**2 + 2*y*z + z**2 - -Arithmetic expand is done by default in ``expand()`` so the keyword ``basic`` can -be omitted. However you can set ``basic=False`` to avoid this type of expand if -you use rules described below. This give complete control on what is done with -the expression. - -Another type of expand rule is expanding complex valued expressions and putting -them into a normal form. For this ``complex`` keyword is used. Note that it will -always perform arithmetic expand to obtain the desired normal form: - - >>> (x + I*y).expand(complex=True) - re(x) + I*re(y) + I*im(x) - im(y) - - >>> sin(x + I*y).expand(complex=True) - sin(re(x) - im(y))*cosh(re(y) + im(x)) + I*cos(re(x) - im(y))*sinh(re(y) + im(x)) - -Note also that the same behavior can be obtained by using ``as_real_imag()`` -method. However it will return a tuple containing the real part in the first -place and the imaginary part in the other. This can be also done in a two step -process by using ``collect`` function: - - >>> (x + I*y).as_real_imag() - (re(x) - im(y), re(y) + im(x)) - - >>> collect((x + I*y).expand(complex=True), I, evaluate=False) - {1: re(x) - im(y), I: re(y) + im(x)} - -There is also possibility for expanding expressions in terms of expressions of -different kind. This is very general type of expanding and usually you would -use ``rewrite()`` to do specific type of rewrite:: - - >>> GoldenRatio.expand(func=True) - 1/2 + sqrt(5)/2 - -Common Subexpression Detection and Collection ---------------------------------------------- - -.. module:: sympy.simplify.cse_main - -Before evaluating a large expression, it is often useful to identify common -subexpressions, collect them and evaluate them at once. This is implemented -in the ``cse`` function. Examples:: - - >>> from sympy import cse, sqrt, sin, pprint - >>> from sympy.abc import x - - >>> pprint(cse(sqrt(sin(x))), use_unicode=True) - ⎛ ⎡ ________⎤⎞ - ⎝[], ⎣╲╱ sin(x) ⎦⎠ - - >>> pprint(cse(sqrt(sin(x)+5)*sqrt(sin(x)+4)), use_unicode=True) - ⎛ ⎡ ________ ________⎤⎞ - ⎝[(x₀, sin(x))], ⎣╲╱ x₀ + 4 ⋅╲╱ x₀ + 5 ⎦⎠ - - >>> pprint(cse(sqrt(sin(x+1) + 5 + cos(y))*sqrt(sin(x+1) + 4 + cos(y))), - ... use_unicode=True) - ⎛ ⎡ ________ ________⎤⎞ - ⎝[(x₀, sin(x + 1) + cos(y))], ⎣╲╱ x₀ + 4 ⋅╲╱ x₀ + 5 ⎦⎠ - - >>> pprint(cse((x-y)*(z-y) + sqrt((x-y)*(z-y))), use_unicode=True) - ⎛ ⎡ ____ ⎤⎞ - ⎝[(x₀, -(x - y)⋅(y - z))], ⎣╲╱ x₀ + x₀⎦⎠ - -More information: - -.. autofunction:noindex: cse diff --git a/dev-py3k/_sources/modules/series.txt b/dev-py3k/_sources/modules/series.txt deleted file mode 100644 index 441172c8bd5..00000000000 --- a/dev-py3k/_sources/modules/series.txt +++ /dev/null @@ -1,188 +0,0 @@ -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 \ No newline at end of file diff --git a/dev-py3k/_sources/modules/sets.txt b/dev-py3k/_sources/modules/sets.txt deleted file mode 100644 index 7e3571db0e1..00000000000 --- a/dev-py3k/_sources/modules/sets.txt +++ /dev/null @@ -1,71 +0,0 @@ -Sets -=========== - -.. automodule:: sympy.core.sets - -Set -^^^ -.. autoclass:: Set - :members: - -Elementary Sets ---------------- - -Interval -^^^^^^^^ -.. autoclass:: Interval - :members: - -FiniteSet -^^^^^^^^^ -.. autoclass:: FiniteSet - :members: - -Compound Sets -------------- -Union -^^^^^ -.. autoclass:: Union - :members: - -Intersection -^^^^^^^^^^^^ -.. autoclass:: Intersection - :members: - -ProductSet -^^^^^^^^^^ -.. autoclass:: ProductSet - :members: - -Singleton Sets --------------- - -EmptySet -^^^^^^^^ -.. autoclass:: EmptySet - :members: - -UniversalSet -^^^^^^^^^^^^ -.. autoclass:: UniversalSet - :members: - -Special Sets ------------- -.. automodule:: sympy.sets.fancysets - -Naturals -^^^^^^^^ -.. autoclass:: Naturals - :members: - -Integers -^^^^^^^^ -.. autoclass:: Integers - :members: - -TransformationSet -^^^^^^^^^^^^^^^^^ -.. autoclass:: TransformationSet - :members: diff --git a/dev-py3k/_sources/modules/simplify/hyperexpand.txt b/dev-py3k/_sources/modules/simplify/hyperexpand.txt deleted file mode 100644 index 0a251df8541..00000000000 --- a/dev-py3k/_sources/modules/simplify/hyperexpand.txt +++ /dev/null @@ -1,634 +0,0 @@ -Details on the Hypergeometric Function Expansion Module -####################################################### - -This page describes how the function :func:`hyperexpand` and related code -work. For usage, see the documentation of the symplify module. - -Hypergeometric Function Expansion Algorithm -******************************************* - -This section describes the algorithm used to expand hypergeometric functions. -Most of it is based on the papers [Roach1996]_ and [Roach1997]_. - -Recall that the hypergeometric function is (initially) defined as - -.. math :: - {}_pF_q\left(\begin{matrix} a_1, \dots, a_p \\ b_1, \dots, b_q \end{matrix} - \middle| z \right) - = \sum_{n=0}^\infty \frac{(a_1)_n \dots (a_p)_n}{(b_1)_n \dots (b_q)_n} - \frac{z^n}{n!}. - -It turns out that there are certain differential operators that can change the -`a_p` and `p_q` parameters by integers. If a sequence of such -operators is known that converts the set of indices `a_r^0` and -`b_s^0` into `a_p` and `b_q`, then we shall say the pair `a_p, -b_q` is reachable from `a_r^0, b_s^0`. Our general strategy is thus as -follows: given a set `a_p, b_q` of parameters, try to look up an origin -`a_r^0, b_s^0` for which we know an expression, and then apply the -sequence of differential operators to the known expression to find an -expression for the Hypergeometric function we are interested in. - -Notation -======== - -In the following, the symbol `a` will always denote a numerator parameter -and the symbol `b` will always denote a denominator parameter. The -subscripts `p, q, r, s` denote vectors of that length, so e.g. -`a_p` denotes a vector of `p` numerator parameters. The subscripts -`i` and `j` denote "running indices", so they should usually be used in -conjuction with a "for all `i`". E.g. `a_i < 4` for all `i`. -Uppercase subscripts `I` and `J` denote a chosen, fixed index. So -for example `a_I > 0` is true if the inequality holds for the one index -`I` we are currently interested in. - -Incrementing and decrementing indices -===================================== - -Suppose `a_i \ne 0`. Set `A(a_i) = -\frac{z}{a_i}\frac{\mathrm{d}}{dz}+1`. It is then easy to show that -`A(a_i) {}_p F_q\left({a_p \atop b_q} \middle| z \right) = {}_p F_q\left({a_p + -e_i \atop b_q} \middle| z \right)`, where `e_i` is the i-th unit vector. -Similarly for `b_j \ne 1` we set `B(b_j) = \frac{z}{b_j-1} -\frac{\mathrm{d}}{dz}+1` and find `B(b_j) {}_p F_q\left({a_p \atop b_q} -\middle| z \right) = {}_p F_q\left({a_p \atop b_q - e_i} \middle| z \right)`. -Thus we can increment upper and decrement lower indices at will, as long as we -don't go through zero. The `A(a_i)` and `B(b_j)` are called shift -operators. - -It is also easy to show that `\frac{\mathrm{d}}{dz} {}_p F_q\left({a_p -\atop b_q} \middle| z \right) = \frac{a_1 \dots a_p}{b_1 \dots b_q} {}_p -F_q\left({a_p + 1 \atop b_q + 1} \middle| z \right)`, where `a_p + 1` is -the vector `a_1 + 1, a_2 + 1, \dots` and similarly for `b_q + 1`. -Combining this with the shift operators, we arrive at one form of the -Hypergeometric differential equation: `\left[ \frac{\mathrm{d}}{dz} -\prod_{j=1}^q B(b_j) - \frac{a_1 \dots a_p}{(b_1-1) \dots (b_q-1)} -\prod_{i=1}^p A(a_i) \right] {}_p F_q\left({a_p \atop b_q} \middle| z \right) = -0`. This holds if all shift operators are defined, i.e. if no `a_i = 0` -and no `b_j = 1`. Clearing denominators and multiplying through by z we -arrive at the following equation: `\left[ z\frac{\mathrm{d}}{dz} -\prod_{j=1}^q \left(z\frac{\mathrm{d}}{dz} + b_j-1 \right) - z \prod_{i=1}^p -\left( z\frac{\mathrm{d}}{\mathrm{d}z} + a_i \right) \right] {}_p F_q\left({a_p -\atop b_q} \middle| z\right) = 0`. Even though our derivation does not show it, -it can be checked that this equation holds whenever the `{}_p F_q` is -defined. - -Notice that, under suitable conditions on `a_I, b_J`, each of the -operators `A(a_i)`, `B(b_j)` and -`z\frac{\mathrm{d}}{\mathrm{d}z}` can be expressed in terms of `A(a_I)` or -`B(b_J)`. Our next aim is to write the Hypergeometric differential -equation as follows: `[X A(a_I) - r] {}_p F_q\left({a_p \atop b_q} -\middle| z\right) = 0`, for some operator `X` and some constant `r` -to be determined. If -`r \ne 0`, then we can write this as `\frac{-1}{r} X {}_p F_q\left({a_p + -e_I \atop b_q} \middle| z\right) = {}_p F_q\left({a_p \atop b_q} \middle| -z\right)`, and so `\frac{-1}{r}X` undoes the shifting of `A(a_I)`, -whence it will be called an inverse-shift operator. - -Now `A(a_I)` exists if `a_I \ne 0`, and then -`z\frac{\mathrm{d}}{\mathrm{d}z} = a_I A(a_I) - a_I`. Observe also that all the -operators `A(a_i)`, `B(b_j)` and -`z\frac{\mathrm{d}}{\mathrm{d}z}` commute. We have `\prod_{i=1}^p \left( -z\frac{\mathrm{d}}{\mathrm{d}z} + a_i \right) = \left(\prod_{i=1, i \ne I}^p -\left( z\frac{\mathrm{d}}{\mathrm{d}z} + a_i \right)\right) a_I A(a_I)`, so -this gives us the first half of `X`. The other half does not have such a -nice expression. We find `z\frac{\mathrm{d}}{dz} \prod_{j=1}^q -\left(z\frac{\mathrm{d}}{dz} + b_j-1 \right) = \left(a_I A(a_I) - a_I\right) -\prod_{j=1}^q \left(a_I A(a_I) - a_I + b_j - 1\right)`. Since the first -half had no constant term, we infer `r = -a_I\prod_{j=1}^q(b_j - 1 --a_I)`. - -This tells us under which conditions we can "un-shift" `A(a_I)`, namely -when `a_I \ne 0` and `r \ne 0`. Substituting `a_I - 1` for -`a_I` then tells us under what conditions we can decrement the index -`a_I`. Doing a similar analysis for `B(a_J)`, we arrive at the -following rules: - -* An index `a_I` can be decremented if `a_I \ne 1` and - `a_I \ne b_j` for all `b_j`. -* An index `b_J` can be - incremented if `b_J \ne -1` and `b_J \ne a_i` for all - `a_i`. - -Combined with the conditions (stated above) for the existence of shift -operators, we have thus established the rules of the game! - - -Reduction of Order -================== - -Notice that, quite trivially, if `a_I = b_J`, we have `{}_p -F_q\left({a_p \atop b_q} \middle| z \right) = {}_{p-1} F_{q-1}\left({a_p^* -\atop b_q^*} \middle| z \right)`, where `a_p^*` means `a_p` with -`a_I` omitted, and similarly for `b_q^*`. We call this reduction of -order. - -In fact, we can do even better. If `a_I - b_J \in \mathbb{Z}_{>0}`, then -it is easy to see that `\frac{(a_I)_n}{(b_J)_n}` is actually a polynomial -in `n`. It is also easy to see that -`(z\frac{\mathrm{d}}{\mathrm{d}z})^k z^n = n^k z^n`. Combining these two remarks -we find: - - If `a_I - b_J \in \mathbb{Z}_{>0}`, then there exists a polynomial - `p(n) = p_0 + p_1 n + \dots` (of degree `a_I - b_J`) such - that `\frac{(a_I)_n}{(b_J)_n} = p(n)` and `{}_p F_q\left({a_p - \atop b_q} \middle| z \right) = \left(p_0 + p_1 - z\frac{\mathrm{d}}{\mathrm{d}z} + p_2 - \left(z\frac{\mathrm{d}}{\mathrm{d}z}\right)^2 + \dots \right) {}_{p-1} - F_{q-1}\left({a_p^* \atop b_q^*} \middle| z \right)`. - -Thus any set of parameters `a_p, b_q` is reachable from a set of -parameters `c_r, d_s` where `c_i - d_j \in \mathbb{Z}` implies -`c_i < d_j`. Such a set of parameters `c_r, d_s` is called -suitable. Our database of known formulae should only contain suitable origins. -The reasons are twofold: firstly, working from suitable origins is easier, and -secondly, a formula for a non-suitable origin can be deduced from a lower order -formula, and we should put this one into the database instead. - -Moving Around in the Parameter Space -==================================== - -It remains to investigate the following question: suppose `a_p, b_q` and -`a_p^0, b_q^0` are both suitable, and also `a_i - a_i^0 \in -\mathbb{Z}`, `b_j - b_j^0 \in \mathbb{Z}`. When is `a_p, b_q` -reachable from `a_p^0, b_q^0`? It is clear that we can treat all -parameters independently that are incongruent mod 1. So assume that `a_i` -and `b_j` are congruent to `r` mod 1, for all `i` and -`j`. The same then follows for `a_i^0` and `b_j^0`. - -If `r \ne 0`, then any such `a_p, b_q` is reachable from any -`a_p^0, b_q^0`. To see this notice that there exist constants `c, c^0`, -congruent mod 1, such that `a_i < c < b_j` for all `i` and -`j`, and similarly `a_i^0 < c^0 < b_j^0`. If `n = c - c^0 > 0` then -we first inverse-shift all the `b_j^0` `n` times up, and then -similarly shift shift up all the `a_i^0` `n` times. If `n < -0` then we first inverse-shift down the `a_i^0` and then shift down the -`b_j^0`. This reduces to the case `c = c^0`. But evidently we can -now shift or inverse-shift around the `a_i^0` arbitrarily so long as we -keep them less than `c`, and similarly for the `b_j^0` so long as -we keep them bigger than `c`. Thus `a_p, b_q` is reachable from -`a_p^0, b_q^0`. - -If `r = 0` then the problem is slightly more involved. WLOG no parameter -is zero. We now have one additional complication: no parameter can ever move -through zero. Hence `a_p, b_q` is reachable from `a_p^0, b_q^0` if -and only if the number of `a_i < 0` equals the number of `a_i^0 < -0`, and similarly for the `b_i` and `b_i^0`. But in a suitable set -of parameters, all `b_j > 0`! This is because the Hypergeometric function -is undefined if one of the `b_j` is a non-positive integer and all -`a_i` are smaller than the `b_j`. Hence the number of `b_j \le 0` is -always zero. - -We can thus associate to every suitable set of parameters `a_p, b_q`, -where no `a_i = 0`, the following invariants: - - * For every `r \in [0, 1)` the number `\alpha_r` of parameters - `a_i \equiv r \pmod{1}`, and similarly the number `\beta_r` - of parameters `b_i \equiv r \pmod{1}`. - * The number `\gamma` - of integers `a_i` with `a_i < 0`. - -The above reasoning shows that `a_p, b_q` is reachable from `a_p^0, -b_q^0` if and only if the invariants `\alpha_r, \beta_r, \gamma` all -agree. Thus in particular "being reachable from" is a symmetric relation on -suitable parameters without zeros. - - -Applying the Operators -====================== - -If all goes well then for a given set of parameters we find an origin in our -database for which we have a nice formula. We now have to apply (potentially) -many differential operators to it. If we do this blindly then the result will -be very messy. This is because with Hypergeometric type functions, the -derivative is usually expressed as a sum of two contiguous functions. Hence if -we compute `N` derivatives, then the answer will involve `2N` -contiguous functions! This is clearly undesirable. In fact we know from the -Hypergeometric differential equation that we need at most `\max(p, q+1)` -contiguous functions to express all derivatives. - -Hence instead of differentiating blindly, we will work with a -`\mathbb{C}(z)`-module basis: for an origin `a_r^0, b_s^0` we either store -(for particularly pretty answers) or compute a set of `N` functions -(typically `N = \max(r, s+1)`) with the property that the derivative of -any of them is a `\mathbb{C}(z)`-linear combination of them. In formulae, -we store a vector `B` of `N` functions, a matrix `M` and a -vector `C` (the latter two with entries in `\mathbb{C}(z)`), with -the following properties: - -* `{}_r F_s\left({a_r^0 \atop b_s^0} \middle| z \right) = C B` -* `z\frac{\mathrm{d}}{\mathrm{d}z} B = M B`. - -Then we can compute as many derivatives as we want and we will always end up -with `\mathbb{C}(z)`-linear combination of at most `N` special -functions. - -As hinted above, `B`, `M` and `C` can either all be stored -(for particularly pretty answers) or computed from a single `{}_p F_q` -formula. - - -Loose Ends -========== - -This describes the bulk of the hypergeometric function algorithm. There a few -further tricks, described in the hyperexpand.py source file. The extension to -Meijer G-functions is also described there. - -Meijer G-Functions of Finite Confluence -*************************************** - -Slater's theorem essentially evaluates a `G`-function as a sum of residues. -If all poles are simple, the resulting series can be recognised as -hypergeometric series. Thus a `G`-function can be evaluated as a sum of -Hypergeometric functions. - -If the poles are not simple, the resulting series are not hypergeometric. This -is known as the "confluent" or "logarithmic" case (the latter because the -resulting series tend to contain logarithms). The answer depends in a -complicated way on the multiplicities of various poles, and there is no -accepted notation for representing it (as far as I know). -However if there are only finitely many -multiple poles, we can evaluate the `G` function as a sum of hypergeometric -functions, plus finitely many extra terms. I could not find any good reference -for this, which is why I work it out here. - -Recall the general setup. We define - -.. math:: - G(z) = \frac{1}{2\pi i} \int_L \frac{\prod_{j=1}^m \Gamma(b_j - s) - \prod_{j=1}^n \Gamma(1 - a_j + s)}{\prod_{j=m+1}^q \Gamma(1 - b_j + s) - \prod_{j=n+1}^p \Gamma(a_j - s)} z^s \mathrm{d}s, - -where `L` is a contour starting and ending at `+\infty`, enclosing all of the -poles of `\Gamma(b_j - s)` for `j = 1, \dots, n` once in the negative -direction, and no other poles. Also the integral is assumed absolutely -convergent. - -In what follows, for any complex numbers `a, b`, we write `a \equiv b \pmod{1}` if -and only if there exists an integer `k` such that `a - b = k`. Thus there are -double poles iff `a_i \equiv a_j \pmod{1}` for some `i \ne j \le n`. - -We now assume that whenever `b_j \equiv a_i \pmod{1}` for `i \le m`, `j > n` -then `b_j < a_i`. This means that no quotient of the relevant gamma functions -is a polynomial, and can always be achieved by "reduction of order". Fix a -complex number `c` such that `\{b_i | b_i \equiv c \pmod{1}, i \le m\}` is -not empty. Enumerate this set as `b, b+k_1, \dots, b+k_u`, with `k_i` -non-negative integers. Enumerate similarly -`\{a_j | a_j \equiv c \pmod{1}, j > n\}` as `b + l_1, \dots, b + l_v`. -Then `l_i > k_j` for all `i, j`. For finite confluence, we need to assume -`v \ge u` for all such `c`. - -Let `c_1, \dots, c_w` be distinct `\pmod{1}` and exhaust the congruence classes -of the `b_i`. I claim - -.. math :: G(z) = -\sum_{j=1}^w (F_j(z) + R_j(z)), - -where `F_j(z)` is a hypergeometric function and `R_j(z)` is a finite sum, both -to be specified later. Indeed corresponding to every `c_j` there is -a sequence of poles, at mostly finitely many of them multiple poles. This is where -the `j`-th term comes from. - -Hence fix again `c`, enumerate the relevant `b_i` as -`b, b + k_1, \dots, b + k_u`. We will look at the `a_j` corresponding to -`a + l_1, \dots, a + l_u`. The other `a_i` are not treated specially. The -corresponding gamma functions have poles at (potentially) `s = b + r` for -`r = 0, 1, \dots`. For `r \ge l_u`, pole of the integrand is simple. We thus set - -.. math :: R(z) = \sum_{r=0}^{l_u - 1} res_{s = r + b}. - -We finally need to investigate the other poles. Set `r = l_u + t`, `t \ge 0`. -A computation shows - -.. math :: - \frac{\Gamma(k_i - l_u - t)}{\Gamma(l_i - l_u - t)} - = \frac{1}{(k_i - l_u - t)_{l_i - k_i}} - = \frac{(-1)^{\delta_i}}{(l_u - l_i + 1)_{\delta_i}} - \frac{(l_u - l_i + 1)_t}{(l_u - k_i + 1)_t}, - -where `\delta_i = l_i - k_i`. - -Also - -.. math :: - \Gamma(b_j - l_u - b - t) = - \frac{\Gamma(b_j - l_u - b)}{(-1)^t(l_u + b + 1 - b_j)_t}, \\ - - \Gamma(1 - a_j + l_u + b + t) = - \Gamma(1 - a_j + l_u + b) (1 - a_j + l_u + b)_t - -and - -.. math :: - res_{s = b + l_u + t} \Gamma(b - s) = -\frac{(-1)^{l_u + t}}{(l_u + t)!} - = -\frac{(-1)^{l_u}}{l_u!} \frac{(-1)^t}{(l_u+1)_t}. - -Hence - -.. math :: - res_{s = b + l_u + t} =& -z^{b + l_u} - \frac{(-1)^{l_u}}{l_u!} - \prod_{i=1}^{u} \frac{(-1)^{\delta_i}}{(l_u - k_i + 1)_{\delta_i}} - \frac{\prod_{j=1}^n \Gamma(1 - a_j + l_u + b) - \prod_{j=1}^m \Gamma(b_j - l_u - b)^*} - {\prod_{j=n+1}^p \Gamma(a_j - l_u - b)^* \prod_{j=m+1}^q - \Gamma(1 - b_j + l_u + b)} - \\ &\times - z^t - \frac{(-1)^t}{(l_u+1)_t} - \prod_{i=1}^{u} \frac{(l_u - l_i + 1)_t}{(l_u - k_i + 1)_t} - \frac{\prod_{j=1}^n (1 - a_j + l_u + b)_t - \prod_{j=n+1}^p (-1)^t (l_u + b + 1 - a_j)_t^*} - {\prod_{j=1}^m (-1)^t (l_u + b + 1 - b_j)_t^* - \prod_{j=m+1}^q (1 - b_j + l_u + b)_t}, - -where the `*` means to omit the terms we treated specially. - -We thus arrive at - -.. math :: - F(z) = C \times {}_{p+1}F_{q}\left( - \begin{matrix} 1, (1 + l_u - l_i), (1 + l_u + b - a_i)^* \\ - 1 + l_u, (1 + l_u - k_i), (1 + l_u + b - b_i)^* - \end{matrix} \middle| (-1)^{p-m-n} z\right), - -where `C` designates the factor in the residue independent of `t`. -(This result can also be written in slightly simpler form by converting -all the `l_u` etc back to `a_* - b_*`, but doing so is going to require more -notation still and is not helpful for computation.) - -Extending The Hypergeometric Tables -*********************************** - -Adding new formulae to the tables is straightforward. At the top of the file -``sympy/simplify/hyperexpand.py``, there is a function called -:func:`add_formulae`. Nested in it are defined two helpers, -``add(ap, bq, res)`` and ``addb(ap, bq, B, C, M)``, as well as dummys -``a``, ``b``, ``c``, and ``z``. - -The first step in adding a new formula is by using ``add(ap, bq, res)``. This -declares ``hyper(ap, bq, z) == res``. Here ``ap`` and ``bq`` may use the -dummys ``a``, ``b``, and ``c`` as free symbols. For example the well-known formula -`\sum_0^\infty \frac{(-a)_n z^n}{n!} = (1-z)^a` is declared by the following -line: ``add((-a, ), (), (1-z)**a)``. - -From the information provided, the matrices `B`, `C` and `M` will be computed, -and the formula is now available when expanding hypergeometric functions. -Next the test file ``sympy/simplify/tests/test_hyperexpand.py`` should be run, -in particular the test :func:`test_formulae`. This will test the newly added -formula numerically. If it fails, there is (presumably) a typo in what was -entered. - -Since all newly-added formulae are probably relatively complicated, chances -are that the automatically computed basis is rather suboptimal (there is no -good way of testing this, other than observing very messy output). In this -case the matrices `B`, `C` and `M` should be computed by hand. Then the helper -``addb`` can be used to declare a hypergeometric formula with hand-computed -basis. - -An example -========== - -Because this explanation so far might be very theoretical and difficult to -understand, we walk through an explicit example now. We take the Fresnel -function `C(z)` which obeys the following hypergeometric representation: - -.. math :: - C(z) = z \cdot {}_{1}F_{2}\left.\left( - \begin{matrix} \frac{1}{4} \\ - \frac{1}{2}, \frac{5}{4} - \end{matrix} \right| -\frac{\pi^2 z^4}{16}\right) \,. - -First we try to add this formula to the lookup table by using the -(simpler) function ``add(ap, bq, res)``. The first two arguments -are simply the lists containing the parameter sets of `{}_{1}F_{2}`. -The ``res`` argument is a little bit more complicated. We only know -`C(z)` in terms of `{}_{1}F_{2}(\ldots | f(z))` with `f` -a function of `z`, in our case - -.. math :: - f(z) = -\frac{\pi^2 z^4}{16} \,. - -What we need is a formula where the hypergeometric function has -only `z` as argument `{}_{1}F_{2}(\ldots | z)`. We -introduce the new complex symbol `w` and search for a function -`g(w)` such that - -.. math :: - f(g(w)) = w - -holds. Then we can replace every `z` in `C(z)` by `g(w)`. -In the case of our example the function `g` could look like - -.. math :: - g(w) = \frac{2}{\sqrt{\pi}} \exp\left(\frac{i \pi}{4}\right) w^{\frac{1}{4}} \,. - -We get these functions mainly by guessing and testing the result. Hence -we proceed by computing `f(g(w))` (and simplifying naively) - -.. math :: - f(g(w)) &= -\frac{\pi^2 g(w)^4}{16} \\ - &= -\frac{\pi^2 g\left(\frac{2}{\sqrt{\pi}} \exp\left(\frac{i \pi}{4}\right) w^{\frac{1}{4}}\right)^4}{16} \\ - &= -\frac{\pi^2 \frac{2^4}{\sqrt{\pi}^4} \exp\left(\frac{i \pi}{4}\right)^4 {w^{\frac{1}{4}}}^4}{16} \\ - &= -\exp\left(i \pi\right) w \\ - &= w - -and indeed get back `w`. (In case of branched functions we have to be -aware of branch cuts. In that case we take `w` to be a positive real -number and check the formula. If what we have found works for positive -`w`, then just replace :func:`exp` inside any branched function by -:func:`exp\_polar` and what we get is right for `all` `w`.) Hence -we can write the formula as - -.. math :: - C(g(w)) = g(w) \cdot {}_{1}F_{2}\left.\left( - \begin{matrix} \frac{1}{4} \\ - \frac{1}{2}, \frac{5}{4} - \end{matrix} \right| w\right) \,. - -and trivially - -.. math :: - {}_{1}F_{2}\left.\left( - \begin{matrix} \frac{1}{4} \\ - \frac{1}{2}, \frac{5}{4} - \end{matrix} \right| w\right) - = \frac{C(g(w))}{g(w)} - = \frac{C\left(\frac{2}{\sqrt{\pi}} \exp\left(\frac{i \pi}{4}\right) w^{\frac{1}{4}}\right)} - {\frac{2}{\sqrt{\pi}} \exp\left(\frac{i \pi}{4}\right) w^{\frac{1}{4}}} - -which is exactly what is needed for the third paramenter, -``res``, in ``add``. Finally, the whole function call to add -this rule to the table looks like:: - - add([S(1)/4], - [S(1)/2, S(5)/4], - fresnelc(exp(pi*I/4)*root(z,4)*2/sqrt(pi)) / (exp(pi*I/4)*root(z,4)*2/sqrt(pi)) - ) - -Using this rule we will find that it works but the results are not really nice -in terms of simplicity and number of special function instances included. -We can obtain much better results by adding the formula to the lookup table -in another way. For this we use the (more complicated) function ``addb(ap, bq, B, C, M)``. -The first two arguments are again the lists containing the parameter sets of -`{}_{1}F_{2}`. The remaining three are the matrices mentioned earlier -on this page. - -We know that the `n = \max{\left(p, q+1\right)}`-th derivative can be -expressed as a linear combination of lower order derivatives. The matrix -`B` contains the basis `\{B_0, B_1, \ldots\}` and is of shape -`n \times 1`. The best way to get `B_i` is to take the first -`n = \max(p, q+1)` derivatives of the expression for `{}_p F_q` -and take out usefull pieces. In our case we find that -`n = \max{\left(1, 2+1\right)} = 3`. For computing the derivatives, -we have to use the operator `z\frac{\mathrm{d}}{\mathrm{d}z}`. The -first basis element `B_0` is set to the expression for `{}_1 F_2` -from above: - -.. math :: - B_0 = \frac{ \sqrt{\pi} \exp\left(-\frac{\mathbf{\imath}\pi}{4}\right) - C\left( \frac{2}{\sqrt{\pi}} \exp\left(\frac{\mathbf{\imath}\pi}{4}\right) z^{\frac{1}{4}}\right)} - {2 z^{\frac{1}{4}}} - -Next we compute `z\frac{\mathrm{d}}{\mathrm{d}z} B_0`. For this we can -directly use SymPy! - - >>> from sympy import Symbol, sqrt, exp, I, pi, fresnelc, root, diff, expand - >>> z = Symbol("z") - >>> B0 = sqrt(pi)*exp(-I*pi/4)*fresnelc(2*root(z,4)*exp(I*pi/4)/sqrt(pi))/\ - ... (2*root(z,4)) - >>> z * diff(B0, z) - z*(cosh(2*sqrt(z))/(4*z) - - sqrt(pi)*exp(-I*pi/4)*fresnelc(2*z**(1/4)*exp(I*pi/4)/sqrt(pi))/(8*z**(5/4))) - >>> expand(_) - cosh(2*sqrt(z))/4 - sqrt(pi)*exp(-I*pi/4)*fresnelc(2*z**(1/4)*exp(I*pi/4)/sqrt(pi))/(8*z**(1/4)) - -Formatting this result nicely we obtain - -.. math :: - B_1^\prime = - - \frac{1}{4} \frac{ - \sqrt{\pi} - \exp\left(-\frac{\mathbf{\imath}\pi}{4}\right) - C\left( \frac{2}{\sqrt{\pi}} \exp\left(\frac{\mathbf{\imath}\pi}{4}\right) z^{\frac{1}{4}}\right) - } - {2 z^{\frac{1}{4}}} - + \frac{1}{4} \cosh{\left( 2 \sqrt{z} \right )} - -Computing the second derivative we find - - >>> from sympy import (Symbol, cosh, sqrt, pi, exp, I, fresnelc, root, - ... diff, expand) - >>> z = Symbol("z") - >>> B1prime = cosh(2*sqrt(z))/4 - sqrt(pi)*exp(-I*pi/4)*\ - ... fresnelc(2*root(z,4)*exp(I*pi/4)/sqrt(pi))/(8*root(z,4)) - >>> z * diff(B1prime, z) - z*(-cosh(2*sqrt(z))/(16*z) + sinh(2*sqrt(z))/(4*sqrt(z)) + sqrt(pi)*exp(-I*pi/4)*fresnelc(2*z**(1/4)*exp(I*pi/4)/sqrt(pi))/(32*z**(5/4))) - >>> expand(_) - sqrt(z)*sinh(2*sqrt(z))/4 - cosh(2*sqrt(z))/16 + sqrt(pi)*exp(-I*pi/4)*fresnelc(2*z**(1/4)*exp(I*pi/4)/sqrt(pi))/(32*z**(1/4)) - -which can be printed as - -.. math :: - B_2^\prime = - \frac{1}{16} \frac{ - \sqrt{\pi} - \exp\left(-\frac{\mathbf{\imath}\pi}{4}\right) - C\left( \frac{2}{\sqrt{\pi}} \exp\left(\frac{\mathbf{\imath}\pi}{4}\right) z^{\frac{1}{4}}\right) - } - {2 z^{\frac{1}{4}}} - - \frac{1}{16} \cosh{\left(2\sqrt{z}\right)} - + \frac{1}{4} \sinh{\left(2\sqrt{z}\right)} \sqrt{z} - -We see the common pattern and can collect the pieces. Hence it makes sense to -choose `B_1` and `B_2` as follows - -.. math :: - B = - \left( \begin{matrix} - B_0 \\ B_1 \\ B_2 - \end{matrix} \right) - = - \left( \begin{matrix} - \frac{ - \sqrt{\pi} - \exp\left(-\frac{\mathbf{\imath}\pi}{4}\right) - C\left( \frac{2}{\sqrt{\pi}} \exp\left(\frac{\mathbf{\imath}\pi}{4}\right) z^{\frac{1}{4}}\right) - }{2 z^{\frac{1}{4}}} \\ - \cosh\left(2\sqrt{z}\right) \\ - \sinh\left(2\sqrt{z}\right) \sqrt{z} - \end{matrix} \right) - -(This is in contrast to the basis `B = \left(B_0, B_1^\prime, B_2^\prime\right)` that would -have been computed automatically if we used just ``add(ap, bq, res)``.) - -Because it must hold that `{}_p F_q\left(\cdots \middle| z \right) = C B` -the entries of `C` are obviously - -.. math :: - C = - \left( \begin{matrix} - 1 \\ 0 \\ 0 - \end{matrix} \right) - -Finally we have to compute the entries of the `3 \times 3` matrix `M` -such that `z\frac{\mathrm{d}}{\mathrm{d}z} B = M B` holds. This is easy. -We already computed the first part `z\frac{\mathrm{d}}{\mathrm{d}z} B_0` -above. This gives us the first row of `M`. For the second row we have: - - >>> from sympy import Symbol, cosh, sqrt, diff - >>> z = Symbol("z") - >>> B1 = cosh(2*sqrt(z)) - >>> z * diff(B1, z) - sqrt(z)*sinh(2*sqrt(z)) - -and for the third one - - >>> from sympy import Symbol, sinh, sqrt, expand, diff - >>> z = Symbol("z") - >>> B2 = sinh(2*sqrt(z))*sqrt(z) - >>> expand(z * diff(B2, z)) - sqrt(z)*sinh(2*sqrt(z))/2 + z*cosh(2*sqrt(z)) - -Now we have computed the entries of this matrix to be - -.. math :: - M = - \left( \begin{matrix} - -\frac{1}{4} & \frac{1}{4} & 0 \\ - 0 & 0 & 1 \\ - 0 & z & \frac{1}{2} \\ - \end{matrix} \right) - -Note that the entries of `C` and `M` should typically be -rational functions in `z`, with rational coefficients. This is all -we need to do in order to add a new formula to the lookup table for -``hyperexpand``. - -Implemented Hypergeometric Formulae -*********************************** - -A vital part of the algorithm is a relatively large table of hypergeometric -function representations. The following automatically generated list contains -all the representations implemented in SymPy (of course many more are -derived from them). These formulae are mostly taken from [Luke1969]_ and -[Prudnikov1990]_. They are all tested numerically. - -.. automodule:: sympy.simplify.hyperexpand_doc - -References -********** - -.. [Roach1996] Kelly B. Roach. Hypergeometric Function Representations. - In: Proceedings of the 1996 International Symposium on Symbolic and - Algebraic Computation, pages 301-308, New York, 1996. ACM. - -.. [Roach1997] Kelly B. Roach. Meijer G Function Representations. - In: Proceedings of the 1997 International Symposium on Symbolic and - Algebraic Computation, pages 205-211, New York, 1997. ACM. - -.. [Luke1969] Luke, Y. L. (1969), The Special Functions and Their - Approximations, Volume 1. - -.. [Prudnikov1990] A. P. Prudnikov, Yu. A. Brychkov and O. I. Marichev (1990). - Integrals and Series: More Special Functions, Vol. 3, - Gordon and Breach Science Publisher. diff --git a/dev-py3k/_sources/modules/simplify/simplify.txt b/dev-py3k/_sources/modules/simplify/simplify.txt deleted file mode 100644 index 8b038f659a7..00000000000 --- a/dev-py3k/_sources/modules/simplify/simplify.txt +++ /dev/null @@ -1,129 +0,0 @@ -Simplify -******** - -.. currentmodule:: sympy.simplify.simplify - -simplify --------- -.. autofunction:: simplify - -collect -------- -.. autofunction:: collect - -.. autofunction:: rcollect - -separate --------- -.. autofunction:: separate - -separatevars ------------- - -.. autofunction:: separatevars - -radsimp -------- -.. autofunction:: radsimp - -ratsimp -------- -.. autofunction:: ratsimp - -fraction --------- -.. autofunction:: fraction - -trigsimp --------- -.. autofunction:: trigsimp - -besselsimp ----------- -.. autofunction:: besselsimp - -powsimp -------- -.. autofunction:: powsimp - -combsimp --------- -.. autofunction:: combsimp - -hypersimp ---------- -.. autofunction:: hypersimp - -hypersimilar ------------- -.. autofunction:: hypersimilar - -nsimplify ---------- -.. autofunction:: nsimplify - -collect_sqrt ------------- -.. autofunction:: collect_sqrt - -collect_const -------------- -.. autofunction:: collect_const - -posify ------- -.. autofunction:: posify - -powdenest ---------- -.. autofunction:: powdenest - -logcombine ----------- -.. autofunction:: logcombine - - -Square Root Denest ------------------- -.. module:: sympy.simplify.sqrtdenest - -sqrtdenest -^^^^^^^^^^ -.. autofunction:: sqrtdenest - -Common Subexpresion Elimination -------------------------------- -.. module:: sympy.simplify.cse_main - -cse -^^^ -.. autofunction:: cse - -Hypergeometric Function Expansion ---------------------------------- -.. module:: sympy.simplify.hyperexpand - -hyperexpand -^^^^^^^^^^^ -.. autofunction:: hyperexpand - -Traversal Tools ---------------- -.. module:: sympy.simplify.traversaltools - -use -^^^ -.. autofunction:: use - -EPath Tools ------------ -.. module:: sympy.simplify.epathtools - -EPath class -^^^^^^^^^^^ -.. autoclass:: EPath - :members: - -epath -^^^^^ -.. autofunction:: epath diff --git a/dev-py3k/_sources/modules/solvers/ode.txt b/dev-py3k/_sources/modules/solvers/ode.txt deleted file mode 100644 index 44a5be8802e..00000000000 --- a/dev-py3k/_sources/modules/solvers/ode.txt +++ /dev/null @@ -1,107 +0,0 @@ -.. _ode-docs: - -ODE -=== - -.. module::sympy.solvers.ode - -User Functions --------------- -These are functions that are imported into the global namespace with ``from sympy import *``. They are intended for user use. - -:func:`dsolve` -^^^^^^^^^^^^^^ -.. autofunction:: sympy.solvers.ode.dsolve - -:func:`classify_ode` -^^^^^^^^^^^^^^^^^^^^ -.. autofunction:: sympy.solvers.ode.classify_ode - -:func:`ode_order` -^^^^^^^^^^^^^^^^^^^^ -.. autofunction:: sympy.solvers.ode.ode_order - -:func:`checkodesol` -^^^^^^^^^^^^^^^^^^^ -.. autofunction:: sympy.solvers.ode.checkodesol - -:func:`homogeneous_order` -^^^^^^^^^^^^^^^^^^^^^^^^^ -.. autofunction:: sympy.solvers.ode.homogeneous_order - -Hint Methods ------------- -These functions are intended for internal use by :func:`dsolve` and others. Nonetheless, they contain useful information in their docstrings on the various ODE solving methods. - -:obj:`preprocess` -^^^^^^^^^^^^^^^^^ -.. autofunction:: sympy.solvers.ode.preprocess - -:obj:`odesimp` -^^^^^^^^^^^^^^ -.. autofunction:: sympy.solvers.ode.odesimp - -:obj:`constant_renumber` -^^^^^^^^^^^^^^^^^^^^^^^^ -.. autofunction:: sympy.solvers.ode.constant_renumber - -:obj:`constantsimp` -^^^^^^^^^^^^^^^^^^^ -.. autofunction:: sympy.solvers.ode.constantsimp - -:obj:`sol_simplicity` -^^^^^^^^^^^^^^^^^^^^^^^^^ -.. autofunction:: sympy.solvers.ode.ode_sol_simplicity - -:obj:`1st_exact` -^^^^^^^^^^^^^^^^ -.. autofunction:: sympy.solvers.ode.ode_1st_exact - -:obj:`1st_homogeneous_coeff_best` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. autofunction:: sympy.solvers.ode.ode_1st_homogeneous_coeff_best - -:obj:`1st_homogeneous_coeff_subs_dep_div_indep` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. autofunction:: sympy.solvers.ode.ode_1st_homogeneous_coeff_subs_dep_div_indep - -:obj:`1st_homogeneous_coeff_subs_indep_div_dep` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. autofunction:: sympy.solvers.ode.ode_1st_homogeneous_coeff_subs_indep_div_dep - -:obj:`1st_linear` -^^^^^^^^^^^^^^^^^ -.. autofunction:: sympy.solvers.ode.ode_1st_linear - -:obj:`Bernoulli` -^^^^^^^^^^^^^^^^ -.. autofunction:: sympy.solvers.ode.ode_Bernoulli - -:obj:`Liouville` -^^^^^^^^^^^^^^^^ -.. autofunction:: sympy.solvers.ode.ode_Liouville - -:obj:`Riccati_special_minus2` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. autofunction:: sympy.solvers.ode.ode_Riccati_special_minus2 - -:obj:`nth_linear_constant_coeff_homogeneous` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. autofunction:: sympy.solvers.ode.ode_nth_linear_constant_coeff_homogeneous - -:obj:`nth_linear_constant_coeff_undetermined_coefficients` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. autofunction:: sympy.solvers.ode.ode_nth_linear_constant_coeff_undetermined_coefficients - -:obj:`nth_linear_constant_coeff_variation_of_parameters` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. autofunction:: sympy.solvers.ode.ode_nth_linear_constant_coeff_variation_of_parameters - -:obj:`separable` -^^^^^^^^^^^^^^^^ -.. autofunction:: sympy.solvers.ode.ode_separable - -Information on the ode module ------------------------------ - -.. automodule:: sympy.solvers.ode diff --git a/dev-py3k/_sources/modules/solvers/solvers.txt b/dev-py3k/_sources/modules/solvers/solvers.txt deleted file mode 100644 index b7f3682b4e5..00000000000 --- a/dev-py3k/_sources/modules/solvers/solvers.txt +++ /dev/null @@ -1,71 +0,0 @@ -Solvers -========== - -.. module:: sympy.solvers - -The *solvers* module in SymPy implements methods for solving equations. - -Algebraic equations --------------------- - -Use :func:`solve` to solve algebraic equations. We suppose all equations are equaled to 0, -so solving x**2 == 1 translates into the following code:: - - >>> from sympy.solvers import solve - >>> from sympy import Symbol - >>> x = Symbol('x') - >>> solve(x**2 - 1, x) - [-1, 1] - -The first argument for :func:`solve` is an equation (equaled to zero) and the second argument -is the symbol that we want to solve the equation for. - -.. autofunction:: sympy.solvers.solvers.solve - -.. autofunction:: sympy.solvers.solvers.solve_linear - -.. autofunction:: sympy.solvers.solvers.solve_linear_system - -.. autofunction:: sympy.solvers.solvers.solve_linear_system_LU - -.. autofunction:: sympy.solvers.solvers.solve_undetermined_coeffs - -.. autofunction:: sympy.solvers.solvers.tsolve - -.. autofunction:: sympy.solvers.solvers.nsolve - -.. autofunction:: sympy.solvers.solvers.check_assumptions - -.. autofunction:: sympy.solvers.solvers.checksol - -Ordinary Differential equations (ODEs) --------------------------------------- - -See :ref:`ode-docs`. - -Partial Differential Equations (PDEs) -------------------------------------- - -.. autofunction:: sympy.solvers.pde.pde_separate - -.. autofunction:: sympy.solvers.pde.pde_separate_add - -.. autofunction:: sympy.solvers.pde.pde_separate_mul - -Recurrence Equtions -------------------- - -.. autofunction:: sympy.solvers.recurr.rsolve - -.. autofunction:: sympy.solvers.recurr.rsolve_poly - -.. autofunction:: sympy.solvers.recurr.rsolve_ratio - -.. autofunction:: sympy.solvers.recurr.rsolve_hyper - -Systems of Polynomial Equations -------------------------------- - -.. autofunction:: sympy.solvers.polysys.solve_poly_system - -.. autofunction:: sympy.solvers.polysys.solve_triangulated diff --git a/dev-py3k/_sources/modules/statistics.txt b/dev-py3k/_sources/modules/statistics.txt deleted file mode 100644 index 43cfdf736e7..00000000000 --- a/dev-py3k/_sources/modules/statistics.txt +++ /dev/null @@ -1,136 +0,0 @@ -Statistics -========== - -.. module:: sympy.statistics - -Note: This module has been deprecated. See the stats module. - -The *statistics* module in SymPy implements standard probability distributions -and related tools. Its contents can be imported with the following statement:: - - >>> from sympy import * - >>> from sympy.statistics import * - >>> init_printing(use_unicode=False, wrap_line=False, no_global=True) - -Normal distributions --------------------- - -``Normal(mu, sigma)`` creates a normal distribution with mean value ``mu`` and -standard deviation ``sigma``. The ``Normal`` class defines several -useful methods and properties. Various properties can be accessed directly as -follows: - - >>> N = Normal(0, 1) - >>> N.mean - 0 - >>> N.median - 0 - >>> N.variance - 1 - >>> N.stddev - 1 - -You can generate random numbers from the desired distribution with the -``random`` method: - - >>> N = Normal(10, 5) - >>> N.random() #doctest: +SKIP - 4.914375200829805834246144514 - >>> N.random() #doctest: +SKIP - 11.84331557474637897087177407 - >>> N.random() #doctest: +SKIP - 17.22474580071733640806996846 - >>> N.random() #doctest: +SKIP - 9.864643097429464546621602494 - -The probability density function (pdf) and cumulative distribution function -(cdf) of a distribution can be computed, either in symbolic form or for -particular values: - - >>> N = Normal(1, 1) - >>> x = Symbol('x') - >>> N.pdf(1) - ___ - \/ 2 - -------- - ____ - 2*\/ pi - >>> N.pdf(3).evalf() - 0.0539909665131880 - >>> N.cdf(x) - / ___ \ - |\/ 2 *(x - 1)| - erf|-------------| - \ 2 / 1 - ------------------ + - - 2 2 - >>> N.cdf(-oo), N.cdf(1), N.cdf(oo) - (0, 1/2, 1) - >>> N.cdf(5).evalf() - 0.999968328758167 - -The method ``probability`` gives the total probability on a given interval (a -convenient alternative syntax for cdf(b)-cdf(a)): - - >>> N = Normal(0, 1) - >>> N.probability(-oo, 0) - 1/2 - >>> N.probability(-1, 1) - / ___\ - |\/ 2 | - erf|-----| - \ 2 / - >>> N.probability(-1, 1).evalf() - 0.682689492137086 - -You can also generate a symmetric confidence interval from a given desired -confidence level (given as a fraction 0-1). For the normal distribution, 68%, -95% and 99.7% confidence levels respectively correspond to approximately 1, 2 -and 3 standard deviations: - - >>> N = Normal(0, 1) - >>> N.confidence(0.68) - (-0.994457883209753, 0.994457883209753) - >>> N.confidence(0.95) - (-1.95996398454005, 1.95996398454005) - >>> N.confidence(0.997) - (-2.96773792534178, 2.96773792534178) - -Plug the interval back in to see that the value is correct: - - >>> N.probability(*N.confidence(0.95)).evalf() - 0.950000000000000 - -Other distributions -------------------- - -Besides the normal distribution, uniform continuous distributions are also -supported. ``Uniform(a, b)`` represents the distribution with uniform -probability on the interval [a, b] and zero probability everywhere else. The -``Uniform`` class supports the same methods as the ``Normal`` class. - -Additional distributions, including support for arbitrary user-defined distributions, are planned for the future. - -API Reference -------------- - -Sample -^^^^^^ - -.. autoclass:: sympy.statistics.distributions.Sample - :members: - -Continuous Probability Distributions -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. autoclass:: sympy.statistics.distributions.ContinuousProbability - :members: - -.. autoclass:: sympy.statistics.distributions.Normal - :members: - -.. autoclass:: sympy.statistics.distributions.Uniform - :members: - -.. autoclass:: sympy.statistics.distributions.PDF - :members: diff --git a/dev-py3k/_sources/modules/stats.txt b/dev-py3k/_sources/modules/stats.txt deleted file mode 100644 index 7ea6045312b..00000000000 --- a/dev-py3k/_sources/modules/stats.txt +++ /dev/null @@ -1,170 +0,0 @@ -Stats -=========== - -.. automodule:: sympy.stats - -Random Variable Types -^^^^^^^^^^^^^^^^^^^^^ - -Finite Types ---------------- -.. autofunction:: DiscreteUniform -.. autofunction:: Die -.. autofunction:: Bernoulli -.. autofunction:: Coin -.. autofunction:: Binomial -.. autofunction:: Hypergeometric -.. autofunction:: FiniteRV - -Continuous Types -------------------- - -.. autofunction:: Arcsin -.. autofunction:: Benini -.. autofunction:: Beta -.. autofunction:: BetaPrime -.. autofunction:: Cauchy -.. autofunction:: Chi -.. autofunction:: ChiNoncentral -.. autofunction:: ChiSquared -.. autofunction:: Dagum -.. autofunction:: Erlang -.. autofunction:: Exponential -.. autofunction:: FDistribution -.. autofunction:: FisherZ -.. autofunction:: Frechet -.. autofunction:: Gamma -.. autofunction:: GammaInverse -.. autofunction:: Kumaraswamy -.. autofunction:: Laplace -.. autofunction:: Logistic -.. autofunction:: LogNormal -.. autofunction:: Maxwell -.. autofunction:: Nakagami -.. autofunction:: Normal -.. autofunction:: Pareto -.. autofunction:: QuadraticU -.. autofunction:: RaisedCosine -.. autofunction:: Rayleigh -.. autofunction:: StudentT -.. autofunction:: Triangular -.. autofunction:: Uniform -.. autofunction:: UniformSum -.. autofunction:: VonMises -.. autofunction:: Weibull -.. autofunction:: WignerSemicircle -.. autofunction:: ContinuousRV - -Interface -^^^^^^^^^ - -.. autofunction:: P -.. autofunction:: E -.. autofunction:: density -.. autofunction:: given -.. autofunction:: where -.. autofunction:: variance -.. autofunction:: std -.. autofunction:: sample -.. autofunction:: sample_iter - -Mechanics -^^^^^^^^^ -.. module:: sympy.stats.rv - -SymPy Stats employs a relatively complex class hierarchy. - -RandomDomains are a mapping of variables to possible values. For example we -might say that the symbol Symbol('x') can take on the values {1,2,3,4,5,6}. - -.. class:: RandomDomain - -A PSpace, or Probability Space, combines a RandomDomain with a density to -provide probabilistic information. For example the above domain could be -enhanced by a finite density {1:1/6, 2:1/6, 3:1/6, 4:1/6, 5:1/6, 6:1/6} to -fully define the roll of a fair die named 'x'. - -.. class:: PSpace - -A RandomSymbol represents the PSpace's symbol 'x' inside of SymPy expressions. - -.. class:: RandomSymbol - -The RandomDomain and PSpace classes are almost never directly instantiated. -Instead they are subclassed for a variety of situations. - -RandomDomains and PSpaces must be sufficiently general to represent domains and -spaces of several variables with arbitrarily complex densities. This generality -is often unnecessary. Instead we often build SingleDomains and SinglePSpaces to -represent single, univariate events and processes such as a single die or a -single normal variable. - -.. class:: SinglePSpace -.. class:: SingleDomain - - -Another common case is to collect together a set of such univariate random -variables. A collection of independent SinglePSpaces or SingleDomains can be -brought together to form a ProductDomain or ProductPSpace. These objects would -be useful in representing three dice rolled together for example. - -.. class:: ProductDomain - -.. class:: ProductPSpace - -The Conditional adjective is added whenever we add a global condition to a -RandomDomain or PSpace. A common example would be three independent dice where -we know their sum to be greater than 12. - -.. class:: ConditionalDomain - -We specialize further into Finite and Continuous versions of these classes to -represent finite (such as dice) and continuous (such as normals) random -variables. - -.. module:: sympy.stats.frv -.. class:: FiniteDomain -.. class:: FinitePSpace - -.. module:: sympy.stats.crv -.. class:: ContinuousDomain -.. class:: ContinuousPSpace - -Additionally there are a few specialized classes that implement certain common -random variable types. There is for example a DiePSpace that implements -SingleFinitePSpace and a NormalPSpace that implements SingleContinuousPSpace. - -.. module:: sympy.stats.frv_types -.. class:: DiePSpace - -.. module:: sympy.stats.crv_types -.. class:: NormalPSpace - -RandomVariables can be extracted from these objects using the PSpace.values -method. - -As previously mentioned SymPy Stats employs a relatively complex class -structure. Inheritance is widely used in the implementation of end-level -classes. This tactic was chosen to balance between the need to allow SymPy to -represent arbitrarily defined random variables and optimizing for common cases. -This complicates the code but is structured to only be important to those -working on extending SymPy Stats to other random variable types. - -Users will not use this class structure. Instead these mechanics are exposed -through variable creation functions Die, Coin, FiniteRV, Normal, Exponential, -etc.... These build the appropriate SinglePSpaces and return the corresponding -RandomVariable. Conditional and Product spaces are formed in the natural -construction of SymPy expressions and the use of interface functions E, Given, -Density, etc.... - - -.. function:: sympy.stats.Die -.. function:: sympy.stats.Normal - -There are some additional functions that may be useful. They are largely used -internally. - - -.. autofunction:: sympy.stats.rv.random_symbols -.. autofunction:: sympy.stats.rv.pspace -.. autofunction:: sympy.stats.rv.rs_swap diff --git a/dev-py3k/_sources/modules/tensor/index.txt b/dev-py3k/_sources/modules/tensor/index.txt deleted file mode 100644 index 853f490aef7..00000000000 --- a/dev-py3k/_sources/modules/tensor/index.txt +++ /dev/null @@ -1,16 +0,0 @@ -.. _tensor_module: - -============= -Tensor Module -============= - -.. automodule:: sympy.tensor - -Contents -======== - -.. toctree:: - :maxdepth: 3 - - indexed.rst - index_methods.rst diff --git a/dev-py3k/_sources/modules/tensor/index_methods.txt b/dev-py3k/_sources/modules/tensor/index_methods.txt deleted file mode 100644 index 1fbff8cb511..00000000000 --- a/dev-py3k/_sources/modules/tensor/index_methods.txt +++ /dev/null @@ -1,6 +0,0 @@ -======= -Methods -======= - -.. automodule:: sympy.tensor.index_methods - :members: diff --git a/dev-py3k/_sources/modules/tensor/indexed.txt b/dev-py3k/_sources/modules/tensor/indexed.txt deleted file mode 100644 index d9ee71fff60..00000000000 --- a/dev-py3k/_sources/modules/tensor/indexed.txt +++ /dev/null @@ -1,6 +0,0 @@ -=============== -Indexed Objects -=============== - -.. automodule:: sympy.tensor.indexed - :members: diff --git a/dev-py3k/_sources/modules/utilities/autowrap.txt b/dev-py3k/_sources/modules/utilities/autowrap.txt deleted file mode 100644 index eb8a699f26a..00000000000 --- a/dev-py3k/_sources/modules/utilities/autowrap.txt +++ /dev/null @@ -1,53 +0,0 @@ -=============== -Autowrap Module -=============== - -The autowrap module works very well in tandem with the Indexed classes of the -:ref:`tensor_module`. Here is a simple example that shows how to setup a binary -routine that calculates a matrix-vector product. - ->>> from sympy.utilities.autowrap import autowrap ->>> from sympy import symbols, IndexedBase, Idx, Eq ->>> A, x, y = list(map(IndexedBase, ['A', 'x', 'y'])) ->>> m, n = symbols('m n', integer=True) ->>> i = Idx('i', m) ->>> j = Idx('j', n) ->>> instruction = Eq(y[i], A[i, j]*x[j]); instruction -y[i] == A[i, j]*x[j] - -Because the code printers treat Indexed objects with repeated indices as a -summation, the above equality instance will be translated to low-level code for -a matrix vector product. This is how you tell SymPy to generate the code, -compile it and wrap it as a python function: - ->>> matvec = autowrap(instruction) # doctest: +SKIP - -That's it. Now let's test it with some numpy arrays. The default wrapper -backend is f2py. The wrapper function it provides is set up to accept python -lists, which it will silently convert to numpy arrays. So we can test the -matrix vector product like this: - ->>> M = [[0, 1], -... [1, 0]] ->>> matvec(M, [2, 3]) # doctest: +SKIP -[ 3. 2.] - -Implementation details -====================== - -The autowrap module is implemented with a backend consisting of CodeWrapper -objects. The base class ``CodeWrapper`` takes care of details about module -name, filenames and options. It also contains the driver routine, which runs -through all steps in the correct order, and also takes care of setting up and -removing the temporary working directory. - -The actual compilation and wrapping is done by external resources, such as the -system installed f2py command. The Cython backend runs a distutils setup script -in a subprocess. Subclasses of CodeWrapper takes care of these -backend-dependent details. - -API Reference -============= - -.. automodule:: sympy.utilities.autowrap - :members: diff --git a/dev-py3k/_sources/modules/utilities/codegen.txt b/dev-py3k/_sources/modules/utilities/codegen.txt deleted file mode 100644 index 2897a9449b7..00000000000 --- a/dev-py3k/_sources/modules/utilities/codegen.txt +++ /dev/null @@ -1,50 +0,0 @@ -======= -Codegen -======= - -This module provides functionality to generate directly compilable code from -SymPy expressions. The ``codegen`` function is the user interface to the code -generation functionality in SymPy. Some details of the implementation is given -below for advanced users that may want to use the framework directly. - -.. note:: The ``codegen`` callable is not in the sympy namespace automatically, - to use it you must first execute - - >>> from sympy.utilities.codegen import codegen - -Impementation Details -===================== - -Here we present the most important pieces of the internal structure, as -advanced users may want to use it directly, for instance by subclassing a code -generator for a specialized application. **It is very likely that you would -prefer to use the codegen() function documented above.** - -Basic assumptions: - -* A generic Routine data structure describes the routine that must be translated - into C/Fortran/... code. This data structure covers all features present in - one or more of the supported languages. - -* Descendants from the CodeGen class transform multiple Routine instances into - compilable code. Each derived class translates into a specific language. - -* In many cases, one wants a simple workflow. The friendly functions in the last - part are a simple api on top of the Routine/CodeGen stuff. They are easier to - use, but are less powerful. - -Routine -======= - -The Routine class is a very important piece of the codegen module. Viewing the -codegen utility as a translator of mathematical expressions into a set of -statements in a programming language, the Routine instances are responsible for -extracting and storing information about how the math can be encapsulated in a -function call. Thus, it is the Routine constructor that decides what arguments -the routine will need and if there should be a return value. - -API Reference -============= - -.. automodule:: sympy.utilities.codegen - :members: diff --git a/dev-py3k/_sources/modules/utilities/cythonutils.txt b/dev-py3k/_sources/modules/utilities/cythonutils.txt deleted file mode 100644 index d733adbffe8..00000000000 --- a/dev-py3k/_sources/modules/utilities/cythonutils.txt +++ /dev/null @@ -1,6 +0,0 @@ -================ -Cython Utilities -================ - -.. automodule:: sympy.utilities.cythonutils - :members: diff --git a/dev-py3k/_sources/modules/utilities/decorator.txt b/dev-py3k/_sources/modules/utilities/decorator.txt deleted file mode 100644 index ee279faf4b6..00000000000 --- a/dev-py3k/_sources/modules/utilities/decorator.txt +++ /dev/null @@ -1,6 +0,0 @@ -========= -Decorator -========= - -.. automodule:: sympy.utilities.decorator - :members: diff --git a/dev-py3k/_sources/modules/utilities/index.txt b/dev-py3k/_sources/modules/utilities/index.txt deleted file mode 100644 index 0b507bfc7c2..00000000000 --- a/dev-py3k/_sources/modules/utilities/index.txt +++ /dev/null @@ -1,29 +0,0 @@ -.. _utilities-docs: - -========= -Utilities -========= - -.. TODO: add compilef.rst and benchmarking.rst when they're fixed - -.. automodule:: sympy.utilities - -Contents: - -.. toctree:: - :maxdepth: 2 - - autowrap.rst - codegen.rst - cythonutils.rst - decorator.rst - iterables.rst - lambdify.rst - memoization.rst - misc.rst - pkgdata.rst - pytest.rst - randtest.rst - runtests.rst - source.rst - timeutils.rst diff --git a/dev-py3k/_sources/modules/utilities/iterables.txt b/dev-py3k/_sources/modules/utilities/iterables.txt deleted file mode 100644 index f3376b148a5..00000000000 --- a/dev-py3k/_sources/modules/utilities/iterables.txt +++ /dev/null @@ -1,91 +0,0 @@ -========= -Iterables -========= - -cartes ------- - -Returns the cartesian product of sequences as a generator. - -Examples:: - >>> from sympy.utilities.iterables import cartes - >>> list(cartes([1,2,3], 'ab')) - [(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b'), (3, 'a'), (3, 'b')] - - - -variations ----------- - -variations(seq, n) Returns all the variations of the list of size n. - -Has an optional third argument. Must be a boolean value and makes the method -return the variations with repetition if set to True, or the variations -without repetition if set to False. - -Examples:: - >>> from sympy.utilities.iterables import variations - >>> list(variations([1,2,3], 2)) - [(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)] - >>> list(variations([1,2,3], 2, True)) - [(1, 1), (1, 2), (1, 3), (2, 1), (2, 2), (2, 3), (3, 1), (3, 2), (3, 3)] - - -partitions ----------- - -Although the combinatorics module contains Partition and IntegerPartition -classes for investigation and manipulation of partitions, there are a few -functions to generate partitions that can be used as low-level tools for -routines: ``partitions`` and ``multiset_partitions``. The former gives -integer partitions, and the latter gives enumerated partitions of elements. -There is also a routine ``kbins`` that will give a variety of permutations -of partions. - -partitions:: - - >>> from sympy.utilities.iterables import partitions - >>> [p.copy() for s, p in partitions(7, m=2, size=True) if s == 2] - [{1: 1, 6: 1}, {2: 1, 5: 1}, {3: 1, 4: 1}] - -multiset_partitions:: - - >>> from sympy.utilities.iterables import multiset_partitions - >>> [p for p in multiset_partitions(3, 2)] - [[[0, 1], [2]], [[0, 2], [1]], [[0], [1, 2]]] - >>> [p for p in multiset_partitions([1, 1, 1, 2], 2)] - [[[1, 1, 1], [2]], [[1, 1, 2], [1]], [[1, 1], [1, 2]]] - -kbins:: - - >>> from sympy.utilities.iterables import kbins - >>> def show(k): - ... rv = [] - ... for p in k: - ... rv.append(','.join([''.join(j) for j in p])) - ... return sorted(rv) - ... - >>> show(kbins("ABCD", 2)) - ['A,BCD', 'AB,CD', 'ABC,D'] - >>> show(kbins("ABC", 2)) - ['A,BC', 'AB,C'] - >>> show(kbins("ABC", 2, ordered=00)) # same as multiset_partitions - ['A,BC', 'AB,C', 'AC,B'] - >>> show(kbins("ABC", 2, ordered=0o1)) - ['A,BC', 'A,CB', - 'B,AC', 'B,CA', - 'C,AB', 'C,BA'] - >>> show(kbins("ABC", 2, ordered=10)) - ['A,BC', 'AB,C', 'AC,B', - 'B,AC', 'BC,A', - 'C,AB'] - >>> show(kbins("ABC", 2, ordered=11)) - ['A,BC', 'A,CB', 'AB,C', 'AC,B', - 'B,AC', 'B,CA', 'BA,C', 'BC,A', - 'C,AB', 'C,BA', 'CA,B', 'CB,A'] - -Docstring -========= - -.. automodule:: sympy.utilities.iterables - :members: diff --git a/dev-py3k/_sources/modules/utilities/lambdify.txt b/dev-py3k/_sources/modules/utilities/lambdify.txt deleted file mode 100644 index cf8466e0876..00000000000 --- a/dev-py3k/_sources/modules/utilities/lambdify.txt +++ /dev/null @@ -1,6 +0,0 @@ -======== -Lambdify -======== - -.. automodule:: sympy.utilities.lambdify - :members: diff --git a/dev-py3k/_sources/modules/utilities/memoization.txt b/dev-py3k/_sources/modules/utilities/memoization.txt deleted file mode 100644 index c8137af1113..00000000000 --- a/dev-py3k/_sources/modules/utilities/memoization.txt +++ /dev/null @@ -1,6 +0,0 @@ -=========== -Memoization -=========== - -.. automodule:: sympy.utilities.memoization - :members: diff --git a/dev-py3k/_sources/modules/utilities/misc.txt b/dev-py3k/_sources/modules/utilities/misc.txt deleted file mode 100644 index 8857c19ccdd..00000000000 --- a/dev-py3k/_sources/modules/utilities/misc.txt +++ /dev/null @@ -1,6 +0,0 @@ -============= -Miscellaneous -============= - -.. automodule:: sympy.utilities.misc - :members: diff --git a/dev-py3k/_sources/modules/utilities/pkgdata.txt b/dev-py3k/_sources/modules/utilities/pkgdata.txt deleted file mode 100644 index 50a4a02a428..00000000000 --- a/dev-py3k/_sources/modules/utilities/pkgdata.txt +++ /dev/null @@ -1,6 +0,0 @@ -======= -PKGDATA -======= - -.. automodule:: sympy.utilities.pkgdata - :members: diff --git a/dev-py3k/_sources/modules/utilities/pytest.txt b/dev-py3k/_sources/modules/utilities/pytest.txt deleted file mode 100644 index 2bf84507a7b..00000000000 --- a/dev-py3k/_sources/modules/utilities/pytest.txt +++ /dev/null @@ -1,6 +0,0 @@ -====== -pytest -====== - -.. automodule:: sympy.utilities.pytest - :members: diff --git a/dev-py3k/_sources/modules/utilities/randtest.txt b/dev-py3k/_sources/modules/utilities/randtest.txt deleted file mode 100644 index a5e5e8f96df..00000000000 --- a/dev-py3k/_sources/modules/utilities/randtest.txt +++ /dev/null @@ -1,6 +0,0 @@ -================== -Randomised Testing -================== - -.. automodule:: sympy.utilities.randtest - :members: diff --git a/dev-py3k/_sources/modules/utilities/runtests.txt b/dev-py3k/_sources/modules/utilities/runtests.txt deleted file mode 100644 index baa16deab3a..00000000000 --- a/dev-py3k/_sources/modules/utilities/runtests.txt +++ /dev/null @@ -1,6 +0,0 @@ -========= -Run Tests -========= - -.. automodule:: sympy.utilities.runtests - :members: diff --git a/dev-py3k/_sources/modules/utilities/source.txt b/dev-py3k/_sources/modules/utilities/source.txt deleted file mode 100644 index 12dee2171e9..00000000000 --- a/dev-py3k/_sources/modules/utilities/source.txt +++ /dev/null @@ -1,6 +0,0 @@ -====================== -Source Code Inspection -====================== - -.. automodule:: sympy.utilities.source - :members: diff --git a/dev-py3k/_sources/modules/utilities/timeutils.txt b/dev-py3k/_sources/modules/utilities/timeutils.txt deleted file mode 100644 index c6a5cdea0cf..00000000000 --- a/dev-py3k/_sources/modules/utilities/timeutils.txt +++ /dev/null @@ -1,6 +0,0 @@ -================ -Timing Utilities -================ - -.. automodule:: sympy.utilities.timeutils - :members: diff --git a/dev-py3k/_sources/outreach.txt b/dev-py3k/_sources/outreach.txt deleted file mode 100644 index 0d552892fbc..00000000000 --- a/dev-py3k/_sources/outreach.txt +++ /dev/null @@ -1,39 +0,0 @@ -SymPy Papers ------------- - -A list of papers which either use or mention SymPy, as well as presentations -given about SymPy at conferences can be seen at `SymPy Papers -`_. - -Planet SymPy ------------- - -We have a blog agregator at http://planet.sympy.org - -SymPy logos ------------ - -SymPy has a collection of official logos, which can be accessed here: - -https://github.com/sympy/sympy/tree/master/doc/logo - -The license of all the logos is the same as SymPy: BSD. See the LICENSE file in -the trunk for more information. - -Projects using SymPy --------------------- - -This is an (incomplete) list of projects that use SymPy. If you use SymPy in -your project, please let us know on our mailinglist_, so that we can add your -project here as well. - -* `SfePy `_ (simple finite elements in Python) -* `Quameon `_ (Quantum Monte Carlo in Python) - -.. _mailinglist: http://groups.google.com/group/sympy - -Blogs, News, Magazines ----------------------- - -You can see a list of blogs/news/magazines mentioning SymPy (that we know of) on -our `wiki page `_. diff --git a/dev-py3k/_sources/python-comparisons.txt b/dev-py3k/_sources/python-comparisons.txt deleted file mode 100644 index 4e82744b363..00000000000 --- a/dev-py3k/_sources/python-comparisons.txt +++ /dev/null @@ -1,388 +0,0 @@ -======================================= -Development Tips: Comparisons in Python -======================================= - -.. role:: input(strong) - -Introduction -============ - -When debugging comparisons and hashes in SymPy, it is necessary to understand -when exactly Python calls each method. -Unfortunately, the official Python documentation for this is -not very detailed (see the docs for `rich comparison -`_, -`__cmp__() `_ -and `__hash__() -`_ -methods). - -We wrote this guide to fill in the missing gaps. After reading it, you should -be able to understand which methods do (and do not) get called and the order in -which they are called. - -Hashing -======= - -Every Python class has a ``__hash__()`` method, the default -implementation of which is:: - - def __hash__(self): - return id(self) - -You can reimplement it to return a different integer that you compute on your own. -``hash(x)`` just calls ``x.__hash__()``. Python builtin classes usually redefine -the ``__hash__()`` method. For example, an ``int`` has something like this:: - - def __hash__(self): - return int(self) - -and a ``list`` does something like this:: - - def __hash__(self): - raise TypeError("list objects are unhashable") - -The general -idea about hashes is that if two objects have a different hash, they are not -equal, but if they have the same hash, they *might* be equal. (This is usually -called a "hash collision" and you need to use the methods described in the -next section to determine if the objects really are equal). - -The only requirement from the Python side is -that the hash value mustn't change after it is returned by the -``__hash__()`` method. - -Please be aware that hashing is *platform-dependent*. This means that you can -get different hashes for the same SymPy object on different platforms. This -affects for instance sorting of sympy expressions. You can also get SymPy -objects printed in different order. - -When developing, you have to be careful about this, especially when writing -tests. It is possible that your test runs on a 32-bit platform, but not on -64-bit. An example:: - - >> from sympy import * - >> x = Symbol('x') - >> r = rootfinding.roots_quartic(Poly(x**4 - 6*x**3 + 17*x**2 - 26*x + 20, x)) - >> [i.evalf(2) for i in r] - [1.0 + 1.7*I, 2.0 - 1.0*I, 2.0 + I, 1.0 - 1.7*I] - -If you get this order of solutions, you are probably running 32-bit system. -On a 64-bit system you would get the following:: - - >> [i.evalf(2) for i in r] - [1.0 - 1.7*I, 1.0 + 1.7*I, 2.0 + I, 2.0 - 1.0*I - -When you now write a test like this:: - - r = [i.evalf(2) for i in r] - assert r == [1.0 + 1.7*I, 2.0 - 1.0*I, 2.0 + I, 1.0 - 1.7*I] - -it will fail on a 64-bit platforms, even if it works for your 32-bit system. You can -avoid this by using the ``sorted()`` or ``set()`` Python built-in:: - - r = [i.evalf(2) for i in r] - assert set(r) == set([1.0 + 1.7*I, 2.0 - 1.0*I, 2.0 + I, 1.0 - 1.7*I]) - -This approach does not work for doctests since they always compare strings that would -be printed after a prompt. In that case you could make your test print results using -a combination of ``str()`` and ``sorted()``:: - - >> sorted([str(i.evalf(2)) for i in r]) - ['1.0 + 1.7*I', '1.0 - 1.7*I', '2.0 + I', '2.0 - 1.0*I'] - -or, if you don't want to show the values as strings, then sympify the results or the -sorted list:: - - >> [S(s) for s in sorted([str(i.evalf(2)) for i in r])] - [1.0 + 1.7*I, 1.0 - 1.7*I, 2.0 + I, 2.0 - I] - -The printing of SymPy expressions might be also affected, so be careful -with doctests. If you get the following on a 32-bit system:: - - >> print dsolve(f(x).diff(x, 2) + 2*f(x).diff(x) - f(x), f(x)) - f(x) == C1*exp(-x + x*sqrt(2)) + C2*exp(-x - x*sqrt(2)) - -you might get the following on a 64-bit platform:: - - >> print dsolve(f(x).diff(x, 2) + 2*f(x).diff(x) - f(x), f(x)) - f(x) == C1*exp(-x - x*sqrt(2)) + C2*exp(-x + x*sqrt(2)) - -Method Resolution -================= - -Let ``a``, ``b`` and ``c`` be instances of any one of the Python classes. -As can be easily checked by the `python script`_ at the end of this guide, -if you write:: - - a == b - -Python calls the following -- in this order:: - - a.__eq__(b) - b.__eq__(a) - a.__cmp__(b) - b.__cmp__(a) - id(a) == id(b) - -If a particular method is not implemented (or a method -returns ``NotImplemented`` [1]_) Python skips it -and tries the next one until it succeeds (i.e. until the method returns something -meaningful). The last line is a catch-all method that always succeeds. - -If you write:: - - a != b - -Python tries to call:: - - a.__ne__(b) - b.__ne__(a) - a.__cmp__(b) - b.__cmp__(a) - id(a) == id(b) - -If you write:: - - a < b - -Python tries to call:: - - a.__lt__(b) - b.__gt__(a) - a.__cmp__(b) - b.__cmp__(a) - id(a) < id(b) - -If you write:: - - a <= b - -Python tries to call:: - - a.__le__(b) - b.__ge__(a) - a.__cmp__(b) - b.__cmp__(a) - id(a) <= id(b) - -And similarly for ``a > b`` and ``a >= b``. - -If you write:: - - sorted([a, b, c]) - -Python calls the same chain of methods as for the ``b < a`` and ``c < b`` -comparisons. - -If you write any of the following:: - - a in {d: 5} - a in set([d, d, d]) - set([a, b]) == set([a, b]) - -Python first compares hashes, e.g.:: - - a.__hash__() - d.__hash__() - -If ``hash(a) != hash(d)`` then the result of the statement ``a in {d: 5}`` is -immediately ``False`` (remember how hashes work in general). If -``hash(a) == hash(d)``) Python goes through the method resolution of the -``==`` operator as shown above. - -General Notes and Caveats -========================= - -In the method resolution for ``<``, ``<=``, ``==``, ``!=``, ``>=``, ``>`` and -``sorted([a, b, c])`` operators the ``__hash__()`` method is *not* called, so -in these cases it doesn't matter what it returns. The ``__hash__()`` method is -only called for sets and dictionaries. - -In the official Python documentation you can read about `hashable and -non-hashable `_ objects. -In reality, you don't have to think about it, you just follow the method -resolution described here. E.g. if you try to use lists as dictionary keys, the -list's ``__hash__()`` method will be called and it returns an exception. - -In SymPy, every instance of any subclass of ``Basic`` is -immutable. Technically this means, that it's behavior through all the methods -above mustn't change once the instance is created. Especially, the hash value -mustn't change (as already stated above) or else objects will get mixed up in -dictionaries and wrong values will be returned for a given key, etc.... - -.. _python script: - -Script To Verify This Guide -============================ - -The above method resolution can be verified using the following program:: - - class A(object): - - def __init__(self, a, hash): - self.a = a - self._hash = hash - - def __lt__(self, o): - print "%s.__lt__(%s)" % (self.a, o.a) - return NotImplemented - - def __le__(self, o): - print "%s.__le__(%s)" % (self.a, o.a) - return NotImplemented - - def __gt__(self, o): - print "%s.__gt__(%s)" % (self.a, o.a) - return NotImplemented - - def __ge__(self, o): - print "%s.__ge__(%s)" % (self.a, o.a) - return NotImplemented - - def __cmp__(self, o): - print "%s.__cmp__(%s)" % (self.a, o.a) - #return cmp(self._hash, o._hash) - return NotImplemented - - def __eq__(self, o): - print "%s.__eq__(%s)" % (self.a, o.a) - return NotImplemented - - def __ne__(self, o): - print "%s.__ne__(%s)" % (self.a, o.a) - return NotImplemented - - def __hash__(self): - print "%s.__hash__()" % (self.a) - return self._hash - - def show(s): - print "--- %s " % s + "-"*40 - eval(s) - - a = A("a", 1) - b = A("b", 2) - c = A("c", 3) - d = A("d", 1) - - show("a == b") - show("a != b") - show("a < b") - show("a <= b") - show("a > b") - show("a >= b") - show("sorted([a, b, c])") - show("{d: 5}") - show("a in {d: 5}") - show("set([d, d, d])") - show("a in set([d, d, d])") - show("set([a, b])") - - print "--- x = set([a, b]); y = set([a, b]); ---" - x = set([a, b]) - y = set([a, b]) - print " x == y :" - x == y - - print "--- x = set([a, b]); y = set([b, d]); ---" - x = set([a, b]) - y = set([b, d]) - print " x == y :" - x == y - - -and its output:: - - --- a == b ---------------------------------------- - a.__eq__(b) - b.__eq__(a) - a.__cmp__(b) - b.__cmp__(a) - --- a != b ---------------------------------------- - a.__ne__(b) - b.__ne__(a) - a.__cmp__(b) - b.__cmp__(a) - --- a < b ---------------------------------------- - a.__lt__(b) - b.__gt__(a) - a.__cmp__(b) - b.__cmp__(a) - --- a <= b ---------------------------------------- - a.__le__(b) - b.__ge__(a) - a.__cmp__(b) - b.__cmp__(a) - --- a > b ---------------------------------------- - a.__gt__(b) - b.__lt__(a) - a.__cmp__(b) - b.__cmp__(a) - --- a >= b ---------------------------------------- - a.__ge__(b) - b.__le__(a) - a.__cmp__(b) - b.__cmp__(a) - --- sorted([a, b, c]) ---------------------------------------- - b.__lt__(a) - a.__gt__(b) - b.__cmp__(a) - a.__cmp__(b) - c.__lt__(b) - b.__gt__(c) - c.__cmp__(b) - b.__cmp__(c) - --- {d: 5} ---------------------------------------- - d.__hash__() - --- a in {d: 5} ---------------------------------------- - d.__hash__() - a.__hash__() - d.__eq__(a) - a.__eq__(d) - d.__cmp__(a) - a.__cmp__(d) - --- set([d, d, d]) ---------------------------------------- - d.__hash__() - d.__hash__() - d.__hash__() - --- a in set([d, d, d]) ---------------------------------------- - d.__hash__() - d.__hash__() - d.__hash__() - a.__hash__() - d.__eq__(a) - a.__eq__(d) - d.__cmp__(a) - a.__cmp__(d) - --- set([a, b]) ---------------------------------------- - a.__hash__() - b.__hash__() - --- x = set([a, b]); y = set([a, b]); --- - a.__hash__() - b.__hash__() - a.__hash__() - b.__hash__() - x == y : - --- x = set([a, b]); y = set([b, d]); --- - a.__hash__() - b.__hash__() - b.__hash__() - d.__hash__() - x == y : - d.__eq__(a) - a.__eq__(d) - d.__cmp__(a) - a.__cmp__(d) - ----------- - -.. [1] There is also the similar ``NotImplementedError`` exception, which one may - be tempted to raise to obtain the same effect as returning - ``NotImplemented``. - - But these are **not** the same, and Python will completely ignore - ``NotImplementedError`` with respect to choosing appropriate comparison - method, and will just propagate this exception upwards, to the caller. - - So ``return NotImplemented`` is not the same as ``raise NotImplementedError``. diff --git a/dev-py3k/_sources/tutorial/tutorial.en.txt b/dev-py3k/_sources/tutorial/tutorial.en.txt deleted file mode 100644 index e4ed9e50e77..00000000000 --- a/dev-py3k/_sources/tutorial/tutorial.en.txt +++ /dev/null @@ -1,1063 +0,0 @@ -.. _tutorial: - -======== -Tutorial -======== - -.. role:: input(strong) - -Introduction -============ - -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. - -This tutorial gives an overview and introduction to SymPy. -Read this to have an idea what SymPy can do for you (and how) and if you want -to know more, read the :ref:`SymPy User's Guide `, the -:ref:`SymPy Modules Reference `, or the -`sources `_ directly. - -First Steps with SymPy -====================== - -The easiest way to download it is to go to -http://code.google.com/p/sympy/ and -download the latest tarball from the Featured Downloads: - -.. image:: ../figures/featured-downloads.png - -Unpack it: - -.. parsed-literal:: - - $ :input:`tar xzf sympy-0.5.12.tar.gz` - -and try it from a Python interpreter: - -.. parsed-literal:: - - $ :input:`cd sympy-0.5.12` - $ :input:`python` - Python 2.4.4 (#2, Jan 3 2008, 13:36:28) - [GCC 4.2.3 20071123 (prerelease) (Debian 4.2.2-4)] on linux2 - Type "help", "copyright", "credits" or "license" for more information. - >>> from sympy import Symbol, cos - >>> x = Symbol("x") - >>> (1/cos(x)).series(x, 0, 10) - 1 + x**2/2 + 5*x**4/24 + 61*x**6/720 + 277*x**8/8064 + O(x**10) - -You can use SymPy as shown above and this is indeed the recommended way if you -use it in your program. You can also install it using ``./setup.py install`` as -any other Python module, or just install a package in your favourite Linux -distribution, e.g.: - -.. topic:: Installing SymPy in Debian - - .. parsed-literal:: - - $ :input:`sudo apt-get install python-sympy` - Reading package lists... Done - Building dependency tree - Reading state information... Done - The following NEW packages will be installed: - python-sympy - 0 upgraded, 1 newly installed, 0 to remove and 18 not upgraded. - Need to get 991kB of archives. - After this operation, 5976kB of additional disk space will be used. - Get:1 http://ftp.cz.debian.org unstable/main python-sympy 0.5.12-1 [991kB] - Fetched 991kB in 2s (361kB/s) - Selecting previously deselected package python-sympy. - (Reading database ... 232619 files and directories currently installed.) - Unpacking python-sympy (from .../python-sympy_0.5.12-1_all.deb) ... - Setting up python-sympy (0.5.12-1) ... - - -For other means how to install SymPy, consult the wiki page -`Download and Installation `_. - - -isympy Console --------------- - -For experimenting with new features, or when figuring out how to do things, you -can use our special wrapper around IPython called ``isympy`` (located in -``bin/isympy`` if you are running from the source directory) which is just a -standard Python shell that has already imported the relevant SymPy modules and -defined the symbols x, y, z and some other things: - -.. parsed-literal:: - - $ :input:`cd sympy` - $ :input:`./bin/isympy` - IPython console for SymPy 0.7.2-git (Python 2.7.1) (ground types: gmpy) - - These commands were executed: - >>> - >>> from sympy import * - >>> x, y, z, t = symbols('x y z t') - >>> k, m, n = symbols('k m n', integer=True) - >>> f, g, h = symbols('f g h', cls=Function) - - Documentation can be found at http://www.sympy.org - - In [1]: :input:`(1/cos(x)).series(x, 0, 10)` - Out[1]: - 2 4 6 8 - x 5*x 61*x 277*x / 10\\ - 1 + -- + ---- + ----- + ------ + O\\x / - 2 24 720 8064 - -.. note:: - - Commands entered by you are bold. Thus what we did in 3 lines in a regular - Python interpreter can be done in 1 line in isympy. - - -Using SymPy as a calculator ---------------------------- - -SymPy has three built-in numeric types: Float, Rational and Integer. - -The Rational class represents a rational number as a pair of two Integers: -the numerator and the denominator. So Rational(1, 2) represents 1/2, -Rational(5, 2) represents 5/2, and so on. - -:: - - >>> from sympy import Rational - >>> a = Rational(1, 2) - - >>> a - 1/2 - - >>> a*2 - 1 - - >>> Rational(2)**50/Rational(10)**50 - 1/88817841970012523233890533447265625 - - -Proceed with caution while working with Python int's and floating -point numbers, especially in division, since you may create a -Python number, not a SymPy number. A ratio of two Python ints may -create a float -- the "true division" standard of Python 3 -and the default behavior of ``isympy`` which imports division -from __future__: - -:: - - >>> 1/2 #doctest: +SKIP - 0.5 - -But in earlier Python versions where division has not been imported, a -truncated int will result: - -:: - - >>> 1/2 #doctest: +SKIP - 0 - -In both cases, however, you are not dealing with a SymPy Number because -Python created its own number. Most of the time you will probably be -working with Rational numbers, so make sure to use Rational to get -the SymPy result. One might find it convenient to equate ``R`` and -Rational: - -:: - - >>> R = Rational - >>> R(1, 2) - 1/2 - >>> R(1)/2 # R(1) is a SymPy Integer and Integer/int gives a Rational - 1/2 - -We also have some special constants, like e and pi, that are treated as symbols -(1 + pi won't evaluate to something numeric, rather it will remain as 1 + pi), and -have arbitrary precision: - -:: - - >>> from sympy import pi, E - >>> pi**2 - pi**2 - - >>> pi.evalf() - 3.14159265358979 - - >>> (pi + E).evalf() - 5.85987448204884 - -as you see, evalf evaluates the expression to a floating-point number - -The symbol ``oo`` is used for a class defining mathematical infinity: - -:: - - >>> from sympy import oo - >>> oo > 99999 - True - >>> oo + 1 - oo - -Symbols -------- - -In contrast to other Computer Algebra Systems, in SymPy you have to declare -symbolic variables explicitly: - -:: - - >>> from sympy import Symbol - >>> x = Symbol('x') - >>> y = Symbol('y') - -On the left is the normal Python variable which has been assigned to the -SymPy Symbol class. Predefined symbols (including those for symbols with -Greek names) are available for import from abc: - - >>> from sympy.abc import x, theta - -Symbols can also be created with the ``symbols`` or ``var`` functions, the -latter automatically adding the created symbols to the namespace, and both -accepting a range notation: - - >>> from sympy import symbols, var - >>> a, b, c = symbols('a,b,c') - >>> d, e, f = symbols('d:f') - >>> var('g:h') - (g, h) - >>> var('g:2') - (g0, g1) - -Instances of the Symbol class "play well together" and are the building blocks -of expresions: - -:: - - >>> x + y + x - y - 2*x - - >>> (x + y)**2 - (x + y)**2 - - >>> ((x + y)**2).expand() - x**2 + 2*x*y + y**2 - -They can be substituted with other numbers, symbols or expressions using ``subs(old, new)``: - -:: - - >>> ((x + y)**2).subs(x, 1) - (y + 1)**2 - - >>> ((x + y)**2).subs(x, y) - 4*y**2 - - >>> ((x + y)**2).subs(x, 1 - y) - 1 - -For the remainder of the tutorial, we assume that we have run: - -:: - - >>> from sympy import init_printing - >>> init_printing(use_unicode=False, wrap_line=False, no_global=True) - -This will make things look better when printed. See the :ref:`printing-tutorial` -section below. If you have a unicode font installed, you can pass -use_unicode=True for a slightly nicer output. - -Algebra -======= - -For partial fraction decomposition, use ``apart(expr, x)``: - -:: - - >>> from sympy import apart - >>> from sympy.abc import x, y, z - - >>> 1/( (x + 2)*(x + 1) ) - 1 - --------------- - (x + 1)*(x + 2) - - >>> apart(1/( (x + 2)*(x + 1) ), x) - 1 1 - - ----- + ----- - x + 2 x + 1 - - >>> (x + 1)/(x - 1) - x + 1 - ----- - x - 1 - - >>> apart((x + 1)/(x - 1), x) - 2 - 1 + ----- - x - 1 - -To combine things back together, use ``together(expr, x)``: - -:: - - >>> from sympy import together - >>> together(1/x + 1/y + 1/z) - x*y + x*z + y*z - --------------- - x*y*z - - >>> together(apart((x + 1)/(x - 1), x), x) - x + 1 - ----- - x - 1 - - >>> together(apart(1/( (x + 2)*(x + 1) ), x), x) - 1 - --------------- - (x + 1)*(x + 2) - - -.. index:: calculus - -Calculus -======== - -.. index:: limits - -Limits ------- - -Limits are easy to use in SymPy, they follow the syntax ``limit(function, -variable, point)``, so to compute the limit of f(x) as x -> 0, you would issue -``limit(f, x, 0)``: - -:: - - >>> from sympy import limit, Symbol, sin, oo - >>> x = Symbol("x") - >>> limit(sin(x)/x, x, 0) - 1 - -you can also calculate the limit at infinity: - -:: - - >>> limit(x, x, oo) - oo - - >>> limit(1/x, x, oo) - 0 - - >>> limit(x**x, x, 0) - 1 - -for some non-trivial examples on limits, you can read the test file -`test_demidovich.py -`_ - -.. index:: differentiation, diff - -Differentiation ---------------- - -You can differentiate any SymPy expression using ``diff(func, var)``. Examples: - -:: - - >>> from sympy import diff, Symbol, sin, tan - >>> x = Symbol('x') - >>> diff(sin(x), x) - cos(x) - >>> diff(sin(2*x), x) - 2*cos(2*x) - - >>> diff(tan(x), x) - 2 - tan (x) + 1 - -You can check, that it is correct by: - -:: - - >>> from sympy import limit - >>> from sympy.abc import delta - >>> limit((tan(x + delta) - tan(x))/delta, delta, 0) - 2 - tan (x) + 1 - -Higher derivatives can be calculated using the ``diff(func, var, n)`` method: - -:: - - >>> diff(sin(2*x), x, 1) - 2*cos(2*x) - - >>> diff(sin(2*x), x, 2) - -4*sin(2*x) - - >>> diff(sin(2*x), x, 3) - -8*cos(2*x) - - -.. index:: - single: series expansion - single: expansion; series - -Series expansion ----------------- - -Use ``.series(var, point, order)``: - -:: - - >>> from sympy import Symbol, cos - >>> x = Symbol('x') - >>> cos(x).series(x, 0, 10) - 2 4 6 8 - x x x x / 10\ - 1 - -- + -- - --- + ----- + O\x / - 2 24 720 40320 - >>> (1/cos(x)).series(x, 0, 10) - 2 4 6 8 - x 5*x 61*x 277*x / 10\ - 1 + -- + ---- + ----- + ------ + O\x / - 2 24 720 8064 - -Another simple example: - -:: - - >>> from sympy import Integral, pprint - - >>> y = Symbol("y") - >>> e = 1/(x + y) - >>> s = e.series(x, 0, 5) - - >>> print(s) - 1/y - x/y**2 + x**2/y**3 - x**3/y**4 + x**4/y**5 + O(x**5) - >>> pprint(s) - 2 3 4 - 1 x x x x / 5\ - - - -- + -- - -- + -- + O\x / - y 2 3 4 5 - y y y y - - - - - -.. index:: summation - -Summation ---------- - -Compute the summation of f with respect to the given summation variable over the given limits. - -summation(f, (i, a, b)) computes the sum of f with respect to i from a to b, -i.e., - -:: - - b - ____ - \ ` - summation(f, (i, a, b)) = ) f - /___, - i = a - - -If it cannot compute the sum, it prints the corresponding summation formula. -Repeated sums can be computed by introducing additional limits: - -:: - - >>> from sympy import summation, oo, symbols, log - >>> i, n, m = symbols('i n m', integer=True) - - >>> summation(2*i - 1, (i, 1, n)) - 2 - n - >>> summation(1/2**i, (i, 0, oo)) - 2 - >>> summation(1/log(n)**n, (n, 2, oo)) - oo - ___ - \ ` - \ -n - / log (n) - /__, - n = 2 - >>> summation(i, (i, 0, n), (n, 0, m)) - 3 2 - m m m - -- + -- + - - 6 2 3 - >>> from sympy.abc import x - >>> from sympy import factorial - >>> summation(x**n/factorial(n), (n, 0, oo)) - x - e - - -.. index:: integration - -Integration ------------ - -SymPy has support for indefinite and definite integration of transcendental -elementary and special functions via ``integrate()`` facility, which uses -powerful extended Risch-Norman algorithm and some heuristics and pattern -matching: - -:: - - >>> from sympy import integrate, erf, exp, sin, log, oo, pi, sinh, symbols - >>> x, y = symbols('x,y') - -You can integrate elementary functions: - -:: - - >>> integrate(6*x**5, x) - 6 - x - >>> integrate(sin(x), x) - -cos(x) - >>> integrate(log(x), x) - x*log(x) - x - >>> integrate(2*x + sinh(x), x) - 2 - x + cosh(x) - -Also special functions are handled easily: - -:: - - >>> integrate(exp(-x**2)*erf(x), x) - ____ 2 - \/ pi *erf (x) - -------------- - 4 - -It is possible to compute definite integrals: - -:: - - >>> integrate(x**3, (x, -1, 1)) - 0 - >>> integrate(sin(x), (x, 0, pi/2)) - 1 - >>> integrate(cos(x), (x, -pi/2, pi/2)) - 2 - -Also, improper integrals are supported as well: - -:: - - >>> integrate(exp(-x), (x, 0, oo)) - 1 - >>> integrate(log(x), (x, 0, 1)) - -1 - -.. index:: - single: complex numbers - single: expansion; complex - -Complex numbers ---------------- - -Besides the imaginary unit, I, which is imaginary, symbols can be created with -attributes (e.g. real, positive, complex, etc...) and this will affect how -they behave: - -:: - - >>> from sympy import Symbol, exp, I - >>> x = Symbol("x") # a plain x with no attributes - >>> exp(I*x).expand() - I*x - e - >>> exp(I*x).expand(complex=True) - -im(x) -im(x) - I*e *sin(re(x)) + e *cos(re(x)) - >>> x = Symbol("x", real=True) - >>> exp(I*x).expand(complex=True) - I*sin(x) + cos(x) - -Functions ---------- - -**trigonometric** - -:: - - >>> from sympy import asin, asinh, cos, sin, sinh, symbols, I - >>> x, y = symbols('x,y') - - >>> sin(x + y).expand(trig=True) - sin(x)*cos(y) + sin(y)*cos(x) - - >>> cos(x + y).expand(trig=True) - -sin(x)*sin(y) + cos(x)*cos(y) - - >>> sin(I*x) - I*sinh(x) - - >>> sinh(I*x) - I*sin(x) - - >>> asinh(I) - I*pi - ---- - 2 - - >>> asinh(I*x) - I*asin(x) - - >>> sin(x).series(x, 0, 10) - 3 5 7 9 - x x x x / 10\ - x - -- + --- - ---- + ------ + O\x / - 6 120 5040 362880 - - >>> sinh(x).series(x, 0, 10) - 3 5 7 9 - x x x x / 10\ - x + -- + --- + ---- + ------ + O\x / - 6 120 5040 362880 - - >>> asin(x).series(x, 0, 10) - 3 5 7 9 - x 3*x 5*x 35*x / 10\ - x + -- + ---- + ---- + ----- + O\x / - 6 40 112 1152 - - >>> asinh(x).series(x, 0, 10) - 3 5 7 9 - x 3*x 5*x 35*x / 10\ - x - -- + ---- - ---- + ----- + O\x / - 6 40 112 1152 - -**spherical harmonics** - -:: - - >>> from sympy import Ylm - >>> from sympy.abc import theta, phi - - >>> Ylm(1, 0, theta, phi) - ___ - \/ 3 *cos(theta) - ---------------- - ____ - 2*\/ pi - - >>> Ylm(1, 1, theta, phi) - ___ I*phi - -\/ 6 *e *sin(theta) - ------------------------ - ____ - 4*\/ pi - - >>> Ylm(2, 1, theta, phi) - ____ I*phi - -\/ 30 *e *sin(theta)*cos(theta) - ------------------------------------ - ____ - 4*\/ pi - -**factorials and gamma function** - -:: - - >>> from sympy import factorial, gamma, Symbol - >>> x = Symbol("x") - >>> n = Symbol("n", integer=True) - - >>> factorial(x) - x! - - >>> factorial(n) - n! - - >>> gamma(x + 1).series(x, 0, 3) # i.e. factorial(x) - / 2 2\ - 2 |EulerGamma pi | / 3\ - 1 - EulerGamma*x + x *|----------- + ---| + O\x / - \ 2 12/ - -**zeta function** - -:: - - >>> from sympy import zeta - >>> zeta(4, x) - zeta(4, x) - - >>> zeta(4, 1) - 4 - pi - --- - 90 - - >>> zeta(4, 2) - 4 - pi - -1 + --- - 90 - - >>> zeta(4, 3) - 4 - 17 pi - - -- + --- - 16 90 - - -**polynomials** - -:: - - >>> from sympy import assoc_legendre, chebyshevt, legendre, hermite - >>> chebyshevt(2, x) - 2 - 2*x - 1 - - >>> chebyshevt(4, x) - 4 2 - 8*x - 8*x + 1 - - >>> legendre(2, x) - 2 - 3*x 1 - ---- - - - 2 2 - - >>> legendre(8, x) - 8 6 4 2 - 6435*x 3003*x 3465*x 315*x 35 - ------- - ------- + ------- - ------ + --- - 128 32 64 32 128 - - >>> assoc_legendre(2, 1, x) - __________ - / 2 - -3*x*\/ - x + 1 - - >>> assoc_legendre(2, 2, x) - 2 - - 3*x + 3 - - >>> hermite(3, x) - 3 - 8*x - 12*x - -.. index:: equations; differential, diff, dsolve - -Differential Equations ----------------------- - -In ``isympy``: - -:: - - >>> from sympy import Function, Symbol, dsolve - >>> f = Function('f') - >>> x = Symbol('x') - >>> f(x).diff(x, x) + f(x) - 2 - d - f(x) + ---(f(x)) - 2 - dx - - >>> dsolve(f(x).diff(x, x) + f(x), f(x)) - f(x) = C1*sin(x) + C2*cos(x) - -.. index:: equations; algebraic, solve - -Algebraic equations -------------------- - -In ``isympy``: - -:: - - >>> from sympy import solve, symbols - >>> x, y = symbols('x,y') - >>> solve(x**4 - 1, x) - [-1, 1, -I, I] - - >>> solve([x + 5*y - 2, -3*x + 6*y - 15], [x, y]) - {x: -3, y: 1} - -.. index:: linear algebra - -Linear Algebra -============== - -.. index:: Matrix - -Matrices --------- - -Matrices are created as instances from the Matrix class: - -:: - - >>> from sympy import Matrix, Symbol - >>> Matrix([[1, 0], [0, 1]]) - [1 0] - [ ] - [0 1] - -They can also contain symbols: - -:: - - >>> x = Symbol('x') - >>> y = Symbol('y') - >>> A = Matrix([[1, x], [y, 1]]) - >>> A - [1 x] - [ ] - [y 1] - - >>> A**2 - [x*y + 1 2*x ] - [ ] - [ 2*y x*y + 1] - -For more about Matrices, see the Linear Algebra tutorial. - -.. index:: pattern matching, match, Wild, WildFunction - -Pattern matching -================ - -Use the ``.match()`` method, along with the ``Wild`` class, to perform pattern -matching on expressions. The method will return a dictionary with the required -substitutions, as follows: - -:: - - >>> from sympy import Symbol, Wild - >>> x = Symbol('x') - >>> p = Wild('p') - >>> (5*x**2).match(p*x**2) - {p: 5} - - >>> q = Wild('q') - >>> (x**2).match(p*x**q) - {p: 1, q: 2} - -If the match is unsuccessful, it returns ``None``: - -:: - - >>> print((x + 1).match(p**x)) - None - -One can also use the exclude parameter of the ``Wild`` class to ensure that -certain things do not show up in the result: - -:: - - >>> p = Wild('p', exclude=[1, x]) - >>> print((x + 1).match(x + p)) # 1 is excluded - None - >>> print((x + 1).match(p + 1)) # x is excluded - None - >>> print((x + 1).match(x + 2 + p)) # -1 is not excluded - {p_: -1} - -.. _printing-tutorial: - -Printing -======== - -There are many ways to print expressions. - -**Standard** - -This is what ``str(expression)`` returns and it looks like this: - - >>> from sympy import Integral - >>> from sympy.abc import x - >>> print(x**2) - x**2 - >>> print(1/x) - 1/x - >>> print(Integral(x**2, x)) - Integral(x**2, x) - -**Pretty printing** - -Nice ascii-art printing is produced by the ``pprint`` function: - - >>> from sympy import Integral, pprint - >>> from sympy.abc import x - >>> pprint(x**2) - 2 - x - >>> pprint(1/x) - 1 - - - x - >>> pprint(Integral(x**2, x)) - / - | - | 2 - | x dx - | - / - -If you have a unicode font installed, the ``pprint`` function will use it by -default. You can override this using the ``use_unicode`` option.: - - >>> pprint(Integral(x**2, x), use_unicode=True) - ⌠ - ⎮ 2 - ⎮ x dx - ⌡ - -See also the wiki `Pretty Printing -`_ for more examples of a nice -unicode printing. - -Tip: To make pretty printing the default in the Python interpreter, use: - -:: - - $ python - Python 2.5.2 (r252:60911, Jun 25 2008, 17:58:32) - [GCC 4.3.1] on linux2 - Type "help", "copyright", "credits" or "license" for more information. - >>> from sympy import init_printing, var, Integral - >>> init_printing(use_unicode=False, wrap_line=False, no_global=True) - >>> var("x") - x - >>> x**3/3 - 3 - x - -- - 3 - >>> Integral(x**2, x) #doctest: +NORMALIZE_WHITESPACE - / - | - | 2 - | x dx - | - / - -**Python printing** - -:: - - >>> from sympy.printing.python import python - >>> from sympy import Integral - >>> from sympy.abc import x - >>> print(python(x**2)) - x = Symbol('x') - e = x**2 - >>> print(python(1/x)) - x = Symbol('x') - e = 1/x - >>> print(python(Integral(x**2, x))) - x = Symbol('x') - e = Integral(x**2, x) - - -**LaTeX printing** - -:: - - >>> from sympy import Integral, latex - >>> from sympy.abc import x - >>> latex(x**2) - x^{2} - >>> latex(x**2, mode='inline') - $x^{2}$ - >>> latex(x**2, mode='equation') - \begin{equation}x^{2}\end{equation} - >>> latex(x**2, mode='equation*') - \begin{equation*}x^{2}\end{equation*} - >>> latex(1/x) - \frac{1}{x} - >>> latex(Integral(x**2, x)) - \int x^{2}\, dx - -**MathML** - -:: - - >>> from sympy.printing.mathml import mathml - >>> from sympy import Integral, latex - >>> from sympy.abc import x - >>> print(mathml(x**2)) - x2 - >>> print(mathml(1/x)) - x-1 - -**Pyglet** - -:: - - >>> from sympy import Integral, preview - >>> from sympy.abc import x - >>> preview(Integral(x**2, x)) #doctest:+SKIP - -If pyglet is installed, a pyglet window will open containing the LaTeX -rendered expression: - -.. image:: ../pics/pngview1.png - -Notes ------ - -``isympy`` calls ``pprint`` automatically, so that's why you see pretty -printing by default. - -Note that there is also a printing module available, ``sympy.printing``. Other -printing methods available through this module are: - -* ``pretty(expr)``, ``pretty_print(expr)``, ``pprint(expr)``: Return or print, respectively, a pretty representation of ``expr``. This is the same as the second level of representation described above. - -* ``latex(expr)``, ``print_latex(expr)``: Return or print, respectively, a `LaTeX `_ representation of ``expr`` - -* ``mathml(expr)``, ``print_mathml(expr)``: Return or print, respectively, a `MathML `_ representation of ``expr``. - -* ``print_gtk(expr)``: Print ``expr`` to `Gtkmathview `_, a GTK widget that displays MathML code. The `Gtkmathview `_ program is required. - -Further documentation -===================== - -Now it's time to learn more about SymPy. Go through the -:ref:`SymPy User's Guide ` and -:ref:`SymPy Modules Reference `. - -Be sure to also browse our public `wiki.sympy.org `_, -that contains a lot of useful examples, tutorials, cookbooks that we and our -users contributed, and feel free to edit it. - -.. only:: html or gettext - - Translations - ------------ - - This tutorial is also available in other languages: - -.. only:: html - - - `Български `_ - - `Česky `_ - - `Deutsch `_ - - `English `_ - - `Français `_ - - `Polski `_ - - `Русский `_ - - `Српски `_ - - `中文 `_ diff --git a/dev-py3k/_sources/wiki.txt b/dev-py3k/_sources/wiki.txt deleted file mode 100644 index d7c09ca61c7..00000000000 --- a/dev-py3k/_sources/wiki.txt +++ /dev/null @@ -1,12 +0,0 @@ -Wiki -==== - -We have a public wiki: http://wiki.sympy.org - -Anyone please feel free to contribute to it anything you find -interesting/useful to other users. - -FAQ ---- - -`FAQ `_ is one of the very useful wiki pages. diff --git a/dev-py3k/_static/SymPy-Favicon.ico b/dev-py3k/_static/SymPy-Favicon.ico deleted file mode 100644 index b6d1f0aba337d3a06dafeea22546374b7c82209a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6230 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 AZ2$lO diff --git a/dev-py3k/_static/ajax-loader.gif b/dev-py3k/_static/ajax-loader.gif deleted file mode 100644 index 61faf8cab23993bd3e1560bff0668bd628642330..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 673 zcmZ?wbhEHb6krfw_{6~Q|Nno%(3)e{?)x>&1u}A`t?OF7Z|1gRivOgXi&7IyQd1Pl zGfOfQ60;I3a`F>X^fL3(@);C=vM_KlFfb_o=k{|A33hf2a5d61U}gjg=>Rd%XaNQW zW@Cw{|b%Y*pl8F?4B9 zlo4Fz*0kZGJabY|>}Okf0}CCg{u4`zEPY^pV?j2@h+|igy0+Kz6p;@SpM4s6)XEMg z#3Y4GX>Hjlml5ftdH$4x0JGdn8~MX(U~_^d!Hi)=HU{V%g+mi8#UGbE-*ao8f#h+S z2a0-5+vc7MU$e-NhmBjLIC1v|)9+Im8x1yacJ7{^tLX(ZhYi^rpmXm0`@ku9b53aN zEXH@Y3JaztblgpxbJt{AtE1ad1Ca>{v$rwwvK(>{m~Gf_=-Ro7Fk{#;i~+{{>QtvI yb2P8Zac~?~=sRA>$6{!(^3;ZP0TPFR(G_-UDU(8Jl0?(IXu$~#4A!880|o%~Al1tN diff --git a/dev-py3k/_static/basic.css b/dev-py3k/_static/basic.css deleted file mode 100644 index 43e8bafaf35..00000000000 --- a/dev-py3k/_static/basic.css +++ /dev/null @@ -1,540 +0,0 @@ -/* - * basic.css - * ~~~~~~~~~ - * - * Sphinx stylesheet -- basic theme. - * - * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -/* -- main layout ----------------------------------------------------------- */ - -div.clearer { - clear: both; -} - -/* -- relbar ---------------------------------------------------------------- */ - -div.related { - width: 100%; - font-size: 90%; -} - -div.related h3 { - display: none; -} - -div.related ul { - margin: 0; - padding: 0 0 0 10px; - list-style: none; -} - -div.related li { - display: inline; -} - -div.related li.right { - float: right; - margin-right: 5px; -} - -/* -- sidebar --------------------------------------------------------------- */ - -div.sphinxsidebarwrapper { - padding: 10px 5px 0 10px; -} - -div.sphinxsidebar { - float: left; - width: 230px; - margin-left: -100%; - font-size: 90%; -} - -div.sphinxsidebar ul { - list-style: none; -} - -div.sphinxsidebar ul ul, -div.sphinxsidebar ul.want-points { - margin-left: 20px; - list-style: square; -} - -div.sphinxsidebar ul ul { - margin-top: 0; - margin-bottom: 0; -} - -div.sphinxsidebar form { - margin-top: 10px; -} - -div.sphinxsidebar input { - border: 1px solid #98dbcc; - font-family: sans-serif; - font-size: 1em; -} - -div.sphinxsidebar #searchbox input[type="text"] { - width: 170px; -} - -div.sphinxsidebar #searchbox input[type="submit"] { - width: 30px; -} - -img { - border: 0; -} - -/* -- search page ----------------------------------------------------------- */ - -ul.search { - margin: 10px 0 0 20px; - padding: 0; -} - -ul.search li { - padding: 5px 0 5px 20px; - background-image: url(file.png); - background-repeat: no-repeat; - background-position: 0 7px; -} - -ul.search li a { - font-weight: bold; -} - -ul.search li div.context { - color: #888; - margin: 2px 0 0 30px; - text-align: left; -} - -ul.keywordmatches li.goodmatch a { - font-weight: bold; -} - -/* -- index page ------------------------------------------------------------ */ - -table.contentstable { - width: 90%; -} - -table.contentstable p.biglink { - line-height: 150%; -} - -a.biglink { - font-size: 1.3em; -} - -span.linkdescr { - font-style: italic; - padding-top: 5px; - font-size: 90%; -} - -/* -- general index --------------------------------------------------------- */ - -table.indextable { - width: 100%; -} - -table.indextable td { - text-align: left; - vertical-align: top; -} - -table.indextable dl, table.indextable dd { - margin-top: 0; - margin-bottom: 0; -} - -table.indextable tr.pcap { - height: 10px; -} - -table.indextable tr.cap { - margin-top: 10px; - background-color: #f2f2f2; -} - -img.toggler { - margin-right: 3px; - margin-top: 3px; - cursor: pointer; -} - -div.modindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -div.genindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -/* -- general body styles --------------------------------------------------- */ - -a.headerlink { - visibility: hidden; -} - -h1:hover > a.headerlink, -h2:hover > a.headerlink, -h3:hover > a.headerlink, -h4:hover > a.headerlink, -h5:hover > a.headerlink, -h6:hover > a.headerlink, -dt:hover > a.headerlink { - visibility: visible; -} - -div.body p.caption { - text-align: inherit; -} - -div.body td { - text-align: left; -} - -.field-list ul { - padding-left: 1em; -} - -.first { - margin-top: 0 !important; -} - -p.rubric { - margin-top: 30px; - font-weight: bold; -} - -img.align-left, .figure.align-left, object.align-left { - clear: left; - float: left; - margin-right: 1em; -} - -img.align-right, .figure.align-right, object.align-right { - clear: right; - float: right; - margin-left: 1em; -} - -img.align-center, .figure.align-center, object.align-center { - display: block; - margin-left: auto; - margin-right: auto; -} - -.align-left { - text-align: left; -} - -.align-center { - text-align: center; -} - -.align-right { - text-align: right; -} - -/* -- sidebars -------------------------------------------------------------- */ - -div.sidebar { - margin: 0 0 0.5em 1em; - border: 1px solid #ddb; - padding: 7px 7px 0 7px; - background-color: #ffe; - width: 40%; - float: right; -} - -p.sidebar-title { - font-weight: bold; -} - -/* -- topics ---------------------------------------------------------------- */ - -div.topic { - border: 1px solid #ccc; - padding: 7px 7px 0 7px; - margin: 10px 0 10px 0; -} - -p.topic-title { - font-size: 1.1em; - font-weight: bold; - margin-top: 10px; -} - -/* -- admonitions ----------------------------------------------------------- */ - -div.admonition { - margin-top: 10px; - margin-bottom: 10px; - padding: 7px; -} - -div.admonition dt { - font-weight: bold; -} - -div.admonition dl { - margin-bottom: 0; -} - -p.admonition-title { - margin: 0px 10px 5px 0px; - font-weight: bold; -} - -div.body p.centered { - text-align: center; - margin-top: 25px; -} - -/* -- tables ---------------------------------------------------------------- */ - -table.docutils { - border: 0; - border-collapse: collapse; -} - -table.docutils td, table.docutils th { - padding: 1px 8px 1px 5px; - border-top: 0; - border-left: 0; - border-right: 0; - border-bottom: 1px solid #aaa; -} - -table.field-list td, table.field-list th { - border: 0 !important; -} - -table.footnote td, table.footnote th { - border: 0 !important; -} - -th { - text-align: left; - padding-right: 5px; -} - -table.citation { - border-left: solid 1px gray; - margin-left: 1px; -} - -table.citation td { - border-bottom: none; -} - -/* -- other body styles ----------------------------------------------------- */ - -ol.arabic { - list-style: decimal; -} - -ol.loweralpha { - list-style: lower-alpha; -} - -ol.upperalpha { - list-style: upper-alpha; -} - -ol.lowerroman { - list-style: lower-roman; -} - -ol.upperroman { - list-style: upper-roman; -} - -dl { - margin-bottom: 15px; -} - -dd p { - margin-top: 0px; -} - -dd ul, dd table { - margin-bottom: 10px; -} - -dd { - margin-top: 3px; - margin-bottom: 10px; - margin-left: 30px; -} - -dt:target, .highlighted { - background-color: #fbe54e; -} - -dl.glossary dt { - font-weight: bold; - font-size: 1.1em; -} - -.field-list ul { - margin: 0; - padding-left: 1em; -} - -.field-list p { - margin: 0; -} - -.refcount { - color: #060; -} - -.optional { - font-size: 1.3em; -} - -.versionmodified { - font-style: italic; -} - -.system-message { - background-color: #fda; - padding: 5px; - border: 3px solid red; -} - -.footnote:target { - background-color: #ffa; -} - -.line-block { - display: block; - margin-top: 1em; - margin-bottom: 1em; -} - -.line-block .line-block { - margin-top: 0; - margin-bottom: 0; - margin-left: 1.5em; -} - -.guilabel, .menuselection { - font-family: sans-serif; -} - -.accelerator { - text-decoration: underline; -} - -.classifier { - font-style: oblique; -} - -abbr, acronym { - border-bottom: dotted 1px; - cursor: help; -} - -/* -- code displays --------------------------------------------------------- */ - -pre { - overflow: auto; - overflow-y: hidden; /* fixes display issues on Chrome browsers */ -} - -td.linenos pre { - padding: 5px 0px; - border: 0; - background-color: transparent; - color: #aaa; -} - -table.highlighttable { - margin-left: 0.5em; -} - -table.highlighttable td { - padding: 0 0.5em 0 0.5em; -} - -tt.descname { - background-color: transparent; - font-weight: bold; - font-size: 1.2em; -} - -tt.descclassname { - background-color: transparent; -} - -tt.xref, a tt { - background-color: transparent; - font-weight: bold; -} - -h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { - background-color: transparent; -} - -.viewcode-link { - float: right; -} - -.viewcode-back { - float: right; - font-family: sans-serif; -} - -div.viewcode-block:target { - margin: -1px -10px; - padding: 0 10px; -} - -/* -- math display ---------------------------------------------------------- */ - -img.math { - vertical-align: middle; -} - -div.body div.math p { - text-align: center; -} - -span.eqno { - float: right; -} - -/* -- printout stylesheet --------------------------------------------------- */ - -@media print { - div.document, - div.documentwrapper, - div.bodywrapper { - margin: 0 !important; - width: 100%; - } - - div.sphinxsidebar, - div.related, - div.footer, - #top-link { - display: none; - } -} \ No newline at end of file diff --git a/dev-py3k/_static/comment-bright.png b/dev-py3k/_static/comment-bright.png deleted file mode 100644 index 551517b8c83b76f734ff791f847829a760ad1903..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3500 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 A2LJ#7 diff --git a/dev-py3k/_static/comment.png b/dev-py3k/_static/comment.png deleted file mode 100644 index 92feb52b8824c6b0f59b658b1196c61de9162a95..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3445 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 Xn!ZpXD%P(H00000NkvXXu0mjf= 0 && !jQuery(node.parentNode).hasClass(className)) { - var span = document.createElement("span"); - span.className = className; - span.appendChild(document.createTextNode(val.substr(pos, text.length))); - node.parentNode.insertBefore(span, node.parentNode.insertBefore( - document.createTextNode(val.substr(pos + text.length)), - node.nextSibling)); - node.nodeValue = val.substr(0, pos); - } - } - else if (!jQuery(node).is("button, select, textarea")) { - jQuery.each(node.childNodes, function() { - highlight(this); - }); - } - } - return this.each(function() { - highlight(this); - }); -}; - -/** - * Small JavaScript module for the documentation. - */ -var Documentation = { - - init : function() { - this.fixFirefoxAnchorBug(); - this.highlightSearchWords(); - this.initIndexTable(); - }, - - /** - * i18n support - */ - TRANSLATIONS : {}, - PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; }, - LOCALE : 'unknown', - - // gettext and ngettext don't access this so that the functions - // can safely bound to a different name (_ = Documentation.gettext) - gettext : function(string) { - var translated = Documentation.TRANSLATIONS[string]; - if (typeof translated == 'undefined') - return string; - return (typeof translated == 'string') ? translated : translated[0]; - }, - - ngettext : function(singular, plural, n) { - var translated = Documentation.TRANSLATIONS[singular]; - if (typeof translated == 'undefined') - return (n == 1) ? singular : plural; - return translated[Documentation.PLURALEXPR(n)]; - }, - - addTranslations : function(catalog) { - for (var key in catalog.messages) - this.TRANSLATIONS[key] = catalog.messages[key]; - this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); - this.LOCALE = catalog.locale; - }, - - /** - * add context elements like header anchor links - */ - addContextElements : function() { - $('div[id] > :header:first').each(function() { - $('\u00B6'). - attr('href', '#' + this.id). - attr('title', _('Permalink to this headline')). - appendTo(this); - }); - $('dt[id]').each(function() { - $('\u00B6'). - attr('href', '#' + this.id). - attr('title', _('Permalink to this definition')). - appendTo(this); - }); - }, - - /** - * workaround a firefox stupidity - */ - fixFirefoxAnchorBug : function() { - if (document.location.hash && $.browser.mozilla) - window.setTimeout(function() { - document.location.href += ''; - }, 10); - }, - - /** - * highlight the search words provided in the url in the text - */ - highlightSearchWords : function() { - var params = $.getQueryParameters(); - var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; - if (terms.length) { - var body = $('div.body'); - window.setTimeout(function() { - $.each(terms, function() { - body.highlightText(this.toLowerCase(), 'highlighted'); - }); - }, 10); - $('') - .appendTo($('#searchbox')); - } - }, - - /** - * init the domain index toggle buttons - */ - initIndexTable : function() { - var togglers = $('img.toggler').click(function() { - var src = $(this).attr('src'); - var idnum = $(this).attr('id').substr(7); - $('tr.cg-' + idnum).toggle(); - if (src.substr(-9) == 'minus.png') - $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); - else - $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); - }).css('display', ''); - if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { - togglers.click(); - } - }, - - /** - * helper function to hide the search marks again - */ - hideSearchWords : function() { - $('#searchbox .highlight-link').fadeOut(300); - $('span.highlighted').removeClass('highlighted'); - }, - - /** - * make the url absolute - */ - makeURL : function(relativeURL) { - return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; - }, - - /** - * get the current relative url - */ - getCurrentURL : function() { - var path = document.location.pathname; - var parts = path.split(/\//); - $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { - if (this == '..') - parts.pop(); - }); - var url = parts.join('/'); - return path.substring(url.lastIndexOf('/') + 1, path.length - 1); - } -}; - -// quick alias for translations -_ = Documentation.gettext; - -$(document).ready(function() { - Documentation.init(); -}); diff --git a/dev-py3k/_static/down-pressed.png b/dev-py3k/_static/down-pressed.png deleted file mode 100644 index 6f7ad782782e4f8e39b0c6e15c7344700cdd2527..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 368 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4S$Y z{B+)352QE?JR*yM+OLB!qm#z$3ZNi+iKnkC`z>}Z23@f-Ava~9&<9T!#}JFtXD=!G zGdl{fK6ro2OGiOl+hKvH6i=D3%%Y^j`yIkRn!8O>@bG)IQR0{Kf+mxNd=_WScA8u_ z3;8(7x2){m9`nt+U(Nab&1G)!{`SPVpDX$w8McLTzAJ39wprG3p4XLq$06M`%}2Yk zRPPsbES*dnYm1wkGL;iioAUB*Or2kz6(-M_r_#Me-`{mj$Z%( diff --git a/dev-py3k/_static/down.png b/dev-py3k/_static/down.png deleted file mode 100644 index 3003a88770de3977d47a2ba69893436a2860f9e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 363 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4S$Y z{B+)352QE?JR*yM+OLB!qm#z$3ZNi+iKnkC`z>}xaV3tUZ$qnrLa#kt978NlpS`ru z&)HFc^}^>{UOEce+71h5nn>6&w6A!ieNbu1wh)UGh{8~et^#oZ1# z>T7oM=FZ~xXWnTo{qnXm$ZLOlqGswI_m2{XwVK)IJmBjW{J3-B3x@C=M{ShWt#fYS9M?R;8K$~YwlIqwf>VA7q=YKcwf2DS4Zj5inDKXXB1zl=(YO3ST6~rDq)&z z*o>z)=hxrfG-cDBW0G$!?6{M<$@{_4{m1o%Ub!naEtn|@^frU1tDnm{r-UW|!^@B8 diff --git a/dev-py3k/_static/file.png b/dev-py3k/_static/file.png deleted file mode 100644 index d18082e397e7e54f20721af768c4c2983258f1b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 392 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmP$HyOL$D9)yc9|lc|nKf<9@eUiWd>3GuTC!a5vdfWYEazjncPj5ZQX%+1 zt8B*4=d)!cdDz4wr^#OMYfqGz$1LDFF>|#>*O?AGil(WEs?wLLy{Gj2J_@opDm%`dlax3yA*@*N$G&*ukFv>P8+2CBWO(qz zD0k1@kN>hhb1_6`&wrCswzINE(evt-5C1B^STi2@PmdKI;Vst0PQB6!2kdN diff --git a/dev-py3k/_static/jquery.js b/dev-py3k/_static/jquery.js deleted file mode 100644 index 7c243080233..00000000000 --- a/dev-py3k/_static/jquery.js +++ /dev/null @@ -1,154 +0,0 @@ -/*! - * jQuery JavaScript Library v1.4.2 - * http://jquery.com/ - * - * Copyright 2010, John Resig - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * Includes Sizzle.js - * http://sizzlejs.com/ - * Copyright 2010, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * - * Date: Sat Feb 13 22:33:48 2010 -0500 - */ -(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/, -Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&& -(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this, -a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b=== -"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this, -function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b
    a"; -var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected, -parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent= -false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n= -s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true, -applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando]; -else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this, -a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b=== -w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i, -cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected= -c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed"); -a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g, -function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split("."); -k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a), -C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B=0){a.type= -e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&& -f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive; -if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data", -e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a, -"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a, -d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, -e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift(); -t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D|| -g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()}, -CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m, -g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)}, -text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}}, -setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return hl[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h= -h[3];l=0;for(m=h.length;l=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m=== -"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g, -h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&& -q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML=""; -if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="

    ";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}(); -(function(){var g=s.createElement("div");g.innerHTML="
    ";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}: -function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f0)for(var j=d;j0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j= -{},i;if(f&&a.length){e=0;for(var o=a.length;e-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a=== -"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode", -d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")? -a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType=== -1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/"},F={option:[1,""],legend:[1,"
    ","
    "],thead:[1,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],col:[2,"","
    "],area:[1,"",""],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div
    ","
    "];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= -c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, -wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, -prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, -this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); -return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja, -""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]); -return this}else{e=0;for(var j=d.length;e0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["", -""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]===""&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e= -c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]? -c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja= -function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter= -Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a, -"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f= -a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b= -a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=//gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!== -"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("
    ").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this}, -serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), -function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href, -global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&& -e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)? -"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache=== -false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B= -false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since", -c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E|| -d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x); -g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status=== -1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b=== -"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional; -if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration=== -"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]|| -c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start; -this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now= -this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem, -e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b
    "; -a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b); -c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a, -d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top- -f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset": -"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in -e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window); diff --git a/dev-py3k/_static/minus.png b/dev-py3k/_static/minus.png deleted file mode 100644 index da1c5620d10c047525a467a425abe9ff5269cfc2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 199 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s1SHkYJtzcHoCO|{#XvD(5N2eUHAey{$X?>< z>&kweokM_|(Po{+Q=kw>iEBiObAE1aYF-J$w=>iB1I2R$WLpMkF=>bh=@O1TaS?83{1OVknK< z>&kweokM`jkU7Va11Q8%;u=xnoS&PUnpeW`?aZ|OK(QcC7sn8Z%gHvy&v=;Q4jejg zV8NnAO`-4Z@2~&zopr02WF_WB>pF diff --git a/dev-py3k/_static/pygments.css b/dev-py3k/_static/pygments.css deleted file mode 100644 index 1a14f2ae1ab..00000000000 --- a/dev-py3k/_static/pygments.css +++ /dev/null @@ -1,62 +0,0 @@ -.highlight .hll { background-color: #ffffcc } -.highlight { background: #eeffcc; } -.highlight .c { color: #408090; font-style: italic } /* Comment */ -.highlight .err { border: 1px solid #FF0000 } /* Error */ -.highlight .k { color: #007020; font-weight: bold } /* Keyword */ -.highlight .o { color: #666666 } /* Operator */ -.highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ -.highlight .cp { color: #007020 } /* Comment.Preproc */ -.highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ -.highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ -.highlight .gd { color: #A00000 } /* Generic.Deleted */ -.highlight .ge { font-style: italic } /* Generic.Emph */ -.highlight .gr { color: #FF0000 } /* Generic.Error */ -.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ -.highlight .gi { color: #00A000 } /* Generic.Inserted */ -.highlight .go { color: #303030 } /* Generic.Output */ -.highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ -.highlight .gs { font-weight: bold } /* Generic.Strong */ -.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.highlight .gt { color: #0040D0 } /* Generic.Traceback */ -.highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ -.highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ -.highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ -.highlight .kp { color: #007020 } /* Keyword.Pseudo */ -.highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ -.highlight .kt { color: #902000 } /* Keyword.Type */ -.highlight .m { color: #208050 } /* Literal.Number */ -.highlight .s { color: #4070a0 } /* Literal.String */ -.highlight .na { color: #4070a0 } /* Name.Attribute */ -.highlight .nb { color: #007020 } /* Name.Builtin */ -.highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ -.highlight .no { color: #60add5 } /* Name.Constant */ -.highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ -.highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ -.highlight .ne { color: #007020 } /* Name.Exception */ -.highlight .nf { color: #06287e } /* Name.Function */ -.highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ -.highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ -.highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ -.highlight .nv { color: #bb60d5 } /* Name.Variable */ -.highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ -.highlight .w { color: #bbbbbb } /* Text.Whitespace */ -.highlight .mf { color: #208050 } /* Literal.Number.Float */ -.highlight .mh { color: #208050 } /* Literal.Number.Hex */ -.highlight .mi { color: #208050 } /* Literal.Number.Integer */ -.highlight .mo { color: #208050 } /* Literal.Number.Oct */ -.highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ -.highlight .sc { color: #4070a0 } /* Literal.String.Char */ -.highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ -.highlight .s2 { color: #4070a0 } /* Literal.String.Double */ -.highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ -.highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ -.highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ -.highlight .sx { color: #c65d09 } /* Literal.String.Other */ -.highlight .sr { color: #235388 } /* Literal.String.Regex */ -.highlight .s1 { color: #4070a0 } /* Literal.String.Single */ -.highlight .ss { color: #517918 } /* Literal.String.Symbol */ -.highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ -.highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ -.highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ -.highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ -.highlight .il { color: #208050 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/dev-py3k/_static/searchtools.js b/dev-py3k/_static/searchtools.js deleted file mode 100644 index cf88109558a..00000000000 --- a/dev-py3k/_static/searchtools.js +++ /dev/null @@ -1,560 +0,0 @@ -/* - * searchtools.js_t - * ~~~~~~~~~~~~~~~~ - * - * Sphinx JavaScript utilties for the full-text search. - * - * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -/** - * helper function to return a node containing the - * search summary for a given text. keywords is a list - * of stemmed words, hlwords is the list of normal, unstemmed - * words. the first one is used to find the occurance, the - * latter for highlighting it. - */ - -jQuery.makeSearchSummary = function(text, keywords, hlwords) { - var textLower = text.toLowerCase(); - var start = 0; - $.each(keywords, function() { - var i = textLower.indexOf(this.toLowerCase()); - if (i > -1) - start = i; - }); - start = Math.max(start - 120, 0); - var excerpt = ((start > 0) ? '...' : '') + - $.trim(text.substr(start, 240)) + - ((start + 240 - text.length) ? '...' : ''); - var rv = $('
    ').text(excerpt); - $.each(hlwords, function() { - rv = rv.highlightText(this, 'highlighted'); - }); - return rv; -} - - -/** - * Porter Stemmer - */ -var Stemmer = function() { - - var step2list = { - ational: 'ate', - tional: 'tion', - enci: 'ence', - anci: 'ance', - izer: 'ize', - bli: 'ble', - alli: 'al', - entli: 'ent', - eli: 'e', - ousli: 'ous', - ization: 'ize', - ation: 'ate', - ator: 'ate', - alism: 'al', - iveness: 'ive', - fulness: 'ful', - ousness: 'ous', - aliti: 'al', - iviti: 'ive', - biliti: 'ble', - logi: 'log' - }; - - var step3list = { - icate: 'ic', - ative: '', - alize: 'al', - iciti: 'ic', - ical: 'ic', - ful: '', - ness: '' - }; - - var c = "[^aeiou]"; // consonant - var v = "[aeiouy]"; // vowel - var C = c + "[^aeiouy]*"; // consonant sequence - var V = v + "[aeiou]*"; // vowel sequence - - var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 - var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 - var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 - var s_v = "^(" + C + ")?" + v; // vowel in stem - - this.stemWord = function (w) { - var stem; - var suffix; - var firstch; - var origword = w; - - if (w.length < 3) - return w; - - var re; - var re2; - var re3; - var re4; - - firstch = w.substr(0,1); - if (firstch == "y") - w = firstch.toUpperCase() + w.substr(1); - - // Step 1a - re = /^(.+?)(ss|i)es$/; - re2 = /^(.+?)([^s])s$/; - - if (re.test(w)) - w = w.replace(re,"$1$2"); - else if (re2.test(w)) - w = w.replace(re2,"$1$2"); - - // Step 1b - re = /^(.+?)eed$/; - re2 = /^(.+?)(ed|ing)$/; - if (re.test(w)) { - var fp = re.exec(w); - re = new RegExp(mgr0); - if (re.test(fp[1])) { - re = /.$/; - w = w.replace(re,""); - } - } - else if (re2.test(w)) { - var fp = re2.exec(w); - stem = fp[1]; - re2 = new RegExp(s_v); - if (re2.test(stem)) { - w = stem; - re2 = /(at|bl|iz)$/; - re3 = new RegExp("([^aeiouylsz])\\1$"); - re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); - if (re2.test(w)) - w = w + "e"; - else if (re3.test(w)) { - re = /.$/; - w = w.replace(re,""); - } - else if (re4.test(w)) - w = w + "e"; - } - } - - // Step 1c - re = /^(.+?)y$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(s_v); - if (re.test(stem)) - w = stem + "i"; - } - - // Step 2 - re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - suffix = fp[2]; - re = new RegExp(mgr0); - if (re.test(stem)) - w = stem + step2list[suffix]; - } - - // Step 3 - re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - suffix = fp[2]; - re = new RegExp(mgr0); - if (re.test(stem)) - w = stem + step3list[suffix]; - } - - // Step 4 - re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; - re2 = /^(.+?)(s|t)(ion)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(mgr1); - if (re.test(stem)) - w = stem; - } - else if (re2.test(w)) { - var fp = re2.exec(w); - stem = fp[1] + fp[2]; - re2 = new RegExp(mgr1); - if (re2.test(stem)) - w = stem; - } - - // Step 5 - re = /^(.+?)e$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(mgr1); - re2 = new RegExp(meq1); - re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); - if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) - w = stem; - } - re = /ll$/; - re2 = new RegExp(mgr1); - if (re.test(w) && re2.test(w)) { - re = /.$/; - w = w.replace(re,""); - } - - // and turn initial Y back to y - if (firstch == "y") - w = firstch.toLowerCase() + w.substr(1); - return w; - } -} - - -/** - * Search Module - */ -var Search = { - - _index : null, - _queued_query : null, - _pulse_status : -1, - - init : function() { - var params = $.getQueryParameters(); - if (params.q) { - var query = params.q[0]; - $('input[name="q"]')[0].value = query; - this.performSearch(query); - } - }, - - loadIndex : function(url) { - $.ajax({type: "GET", url: url, data: null, success: null, - dataType: "script", cache: true}); - }, - - setIndex : function(index) { - var q; - this._index = index; - if ((q = this._queued_query) !== null) { - this._queued_query = null; - Search.query(q); - } - }, - - hasIndex : function() { - return this._index !== null; - }, - - deferQuery : function(query) { - this._queued_query = query; - }, - - stopPulse : function() { - this._pulse_status = 0; - }, - - startPulse : function() { - if (this._pulse_status >= 0) - return; - function pulse() { - Search._pulse_status = (Search._pulse_status + 1) % 4; - var dotString = ''; - for (var i = 0; i < Search._pulse_status; i++) - dotString += '.'; - Search.dots.text(dotString); - if (Search._pulse_status > -1) - window.setTimeout(pulse, 500); - }; - pulse(); - }, - - /** - * perform a search for something - */ - performSearch : function(query) { - // create the required interface elements - this.out = $('#search-results'); - this.title = $('

    ' + _('Searching') + '

    ').appendTo(this.out); - this.dots = $('').appendTo(this.title); - this.status = $('

    ').appendTo(this.out); - this.output = $('